Compare commits

...

232 Commits

Author SHA1 Message Date
dsehnal
9bec644997 3.2.0 2022-02-17 19:24:55 +01:00
dsehnal
650d38dff8 Store IndexPairBonds as a dynamic property 2022-02-17 19:21:49 +01:00
David Sehnal
097277e397 Merge pull request #373 from JonStargaryen/master
Add TraceOnly option for Structure Superposition
2022-02-14 19:53:51 +01:00
Sebastian Bittrich
82a4d5eedf cleanup 2022-02-14 10:46:03 -08:00
Sebastian Bittrich
2a00248812 determine trace during buildIndex 2022-02-14 10:41:10 -08:00
Sebastian Bittrich
9841c773cb reset camera after all alignments 2022-02-10 13:55:09 -08:00
Sebastian Bittrich
b21a78ad14 support shuffled atoms when aligning SIFTS trace 2022-02-10 13:49:37 -08:00
Sebastian Bittrich
7329fa597d options obj for alignAndSuperposeWithSIFTSMapping 2022-02-10 11:36:50 -08:00
Sebastian Bittrich
f1d8f0ecb4 auth 2022-02-10 11:32:21 -08:00
Sebastian Bittrich
1d127f2364 changelog 2022-02-10 11:30:55 -08:00
Sebastian Bittrich
b244405cc3 polymer-based query 2022-02-10 10:39:22 -08:00
Sebastian Bittrich
38adfe0ca6 cleanup 2022-02-10 10:29:34 -08:00
Sebastian Bittrich
6f0d798847 optional filtering for trace during alignment 2022-02-10 10:23:31 -08:00
Sebastian Bittrich
6e58bfd2b0 traceOnly param for superpos 2022-02-08 16:03:05 -08:00
David Sehnal
bc2e8d8ac4 Merge pull request #369 from molstar/pdbx_sifts-export
Better support for atom_site.pdbx_sifts_xref
2022-02-08 10:19:02 +01:00
dsehnal
7be654d47f tweak labels 2022-02-08 10:18:01 +01:00
dsehnal
bfe46e3604 pdbx_sifts_xref PR feedback 2022-02-08 10:12:21 +01:00
dsehnal
008b597fc5 changelog 2022-02-07 17:51:13 +01:00
dsehnal
c4b4f2e3b1 rename BestDatabaseSequenceMapping -> SIFTSMapping 2022-02-07 17:49:10 +01:00
dsehnal
76ee97301b atom_site.pdbx_label_index support 2022-02-07 17:44:03 +01:00
dsehnal
289dc09eae add support for atom_site.pdbx_sifts_xref export 2022-02-07 17:33:11 +01:00
Alexander Rose
f23f84f0f3 3.1.0 2022-02-06 15:56:41 -08:00
Alexander Rose
62259f3295 changelog 2022-02-06 15:51:40 -08:00
Alexander Rose
854a430a12 tweak quick-styles order 2022-02-06 15:46:40 -08:00
Alexander Rose
d27cdb5637 schema updates 2022-02-06 15:41:09 -08:00
Alexander Rose
7d12d9ee90 package udpdates 2022-02-06 15:38:09 -08:00
Alexander Rose
d70a4ff347 Merge pull request #366 from molstar/repr-defaults-tweaks
cleaner default representation style
2022-02-06 15:24:27 -08:00
Alexander Rose
49541558d1 Merge branch 'master' into repr-defaults-tweaks 2022-02-06 15:24:16 -08:00
Alexander Rose
7b00a1227c changelog 2022-02-06 15:22:50 -08:00
Alexander Rose
7800603c81 Merge pull request #367 from molstar/quick-styles
Quick styles
2022-02-06 15:16:43 -08:00
Alexander Rose
fca00c8116 variable naming 2022-02-06 15:15:55 -08:00
Alexander Rose
00fa549e44 Merge pull request #368 from JonStargaryen/master
iterate over `structure.unitSymmetryGroups`, fixes #364
2022-02-06 15:13:57 -08:00
Alexander Rose
36181b6b87 tweak quick style names 2022-02-06 15:13:15 -08:00
JonStargaryen
88a95162e9 iterate over structure.unitSymmetryGroups, fixes #364 2022-02-06 10:47:40 -08:00
Alexander Rose
9c1d59a2c8 add Quick Styles panel 2022-02-05 12:58:32 -08:00
Alexander Rose
fdd894956a fix representation preset side effects 2022-02-05 12:56:55 -08:00
Alexander Rose
eb6dc0859d cleaner default representation style 2022-02-05 12:23:28 -08:00
Alexander Rose
b99026bba2 add ignoreLight to component params 2022-02-05 12:10:56 -08:00
Alexander Rose
6fa50eb8d5 fix xrayShader & ignoreLight not working together 2022-02-05 11:57:39 -08:00
David Sehnal
e5046f15a9 Merge pull request #365 from JonStargaryen/master
Volume Streaming Error Message
2022-02-04 17:11:31 +01:00
JonStargaryen
09f1c066a0 logic 2022-02-03 17:11:50 -08:00
Sebastian Bittrich
ed2f0b34c9 volume streaming err msg 2022-02-03 16:11:10 -08:00
Alexander Rose
c7f75861de 3.0.2 2022-01-30 12:24:48 -08:00
Alexander Rose
ccd04dbc9d changelog 2022-01-30 12:19:49 -08:00
Alexander Rose
e71f8d2c10 Merge pull request #360 from molstar/fix/visual-visibility
Fix visual visibility edge case
2022-01-30 12:17:12 -08:00
dsehnal
d9b4c60239 Fix visual visibility edge case 2022-01-30 15:02:46 +01:00
Alexander Rose
103c1fca21 measurement options tweaks
- allow larger text size
- make customText essential
2022-01-29 19:30:03 -08:00
Alexander Rose
49559bf5fb citation tweak 2022-01-29 16:05:17 -08:00
Alexander Rose
26dceabf83 add citation file 2022-01-29 15:56:39 -08:00
Alexander Rose
abe506182e fix multi-instance entity label display
- fix empty elements created in extendToAllInstances
2022-01-29 11:21:48 -08:00
Alexander Rose
582a0e2a38 fix Sphere.expand for highly directional extrema 2022-01-29 11:13:19 -08:00
dsehnal
2784ccf379 3.0.1 2022-01-27 12:15:53 +01:00
dsehnal
0ad1d578fe changelog 2022-01-27 12:13:22 +01:00
David Sehnal
31fd1c9c68 Merge pull request #353 from molstar/drag-tweak
emit drag event whenever started within viewport
2022-01-27 12:12:07 +01:00
David Sehnal
9c961297a2 Merge branch 'master' into drag-tweak 2022-01-27 12:11:59 +01:00
David Sehnal
b920053349 Merge pull request #352 from molstar/var-fixes
Various fixes
2022-01-27 12:09:39 +01:00
David Sehnal
0a5c764e4a Merge pull request #351 from molstar/volume-server-data-fix
Volume server data fix
2022-01-27 11:37:31 +01:00
Alexander Rose
b9a71c83ff emit drag event whenever started within viewport 2022-01-26 21:32:14 -08:00
Alexander Rose
3255f207d0 Merge branch 'master' of https://github.com/molstar/molstar into var-fixes 2022-01-26 20:46:11 -08:00
Alexander Rose
e3b4ca8862 add entity-id and entity-source as carbonColor 2022-01-26 20:42:38 -08:00
Alexander Rose
6810793015 fix marking of InteractionsInterUnitVisual 2022-01-26 20:40:12 -08:00
Alexander Rose
1feb3c2095 fix entity-id coloring broken for non-ihm models 2022-01-26 20:38:46 -08:00
Alexander Rose
f2da6033d0 fix pdbe xray maps url 2022-01-26 20:36:52 -08:00
Alexander Rose
28bc212132 fix marking pass for transparentBackground 2022-01-26 20:35:38 -08:00
dsehnal
1a7c62eec6 remove gl from dependencies & install it on demand instead
- installing gl on M1 Mac was taking several minutes to compile on each update
2022-01-26 23:12:12 +01:00
dsehnal
de67dbacba iso-value adjustment for VolumeServer data in default Viewer 2022-01-26 17:47:23 +01:00
dsehnal
57223a0f9a Fix VolumeServer/query CLI 2022-01-26 16:49:18 +01:00
Alexander Rose
2ad0754b90 3.0.0 2022-01-23 18:12:48 -08:00
Alexander Rose
3ecb3af57b changelog 2022-01-23 18:08:01 -08:00
Alexander Rose
ec4f15f549 improve/fix InteractionsIntraUnitVisual marking 2022-01-23 17:55:51 -08:00
Alexander Rose
2458ea7b92 Merge pull request #349 from molstar/custom-theme-colors
add custom theme colors
2022-01-23 15:13:24 -08:00
Alexander Rose
c5e6bedf11 Merge branch 'master' into custom-theme-colors 2022-01-23 13:54:57 -08:00
dsehnal
8528e5a666 Support/bugfixes for atom_site.pdbx_sifts_xref categories 2022-01-23 20:46:49 +01:00
dsehnal
6ed232b3d9 fix using default values for webgl1/wboit features in the default viewer app 2022-01-23 19:25:09 +01:00
dsehnal
f8aae8cbd1 skip Coarse models in export extension 2022-01-23 16:13:02 +01:00
Alexander Rose
00c2517045 package updates 2022-01-22 14:06:26 -08:00
Alexander Rose
99b043a929 avoid circular dependency 2022-01-22 11:10:41 -08:00
Alexander Rose
5900e27e39 add custom theme colors
- element-symbol
- molecule-type
- residue-name
- secondary-structure
2022-01-22 10:51:41 -08:00
Alexander Rose
1b79d34907 fix marking of carbohydrate visuals 2022-01-22 08:53:47 -08:00
Alexander Rose
fc52e29c92 re-add using _struct_asym cat in getStructAsymMap
- needed for ihm model support
2022-01-22 08:50:29 -08:00
Alexander Rose
df23b3c0fe fix coarse model support in entity-id color theme
- add StructureProperties.coarse.entity_id
2022-01-22 08:48:07 -08:00
David Sehnal
5e25716c98 Merge pull request #334 from molstar/export-extension
Model export extension & related improvements
2022-01-18 13:23:11 +01:00
dsehnal
f70a10bc56 add info about CIF export failure 2022-01-18 13:20:53 +01:00
dsehnal
0ccb045f4e Merge branch 'master' of https://github.com/molstar/molstar into export-extension 2022-01-18 12:30:45 +01:00
Alexander Rose
fa18d0d852 3.0.0-dev.10 2022-01-17 14:08:28 -08:00
Alexander Rose
687c4342fb changelog 2022-01-17 14:03:55 -08:00
Alexander Rose
9459af46b8 Merge pull request #337 from molstar/anim-rock
Add rock animation
2022-01-17 13:38:08 -08:00
Alexander Rose
fc5832747a Merge pull request #345 from molstar/bump-immutable
bump immutable to 4.0
2022-01-17 13:37:19 -08:00
Alexander Rose
01205d244b tweak trackball speed calc 2022-01-17 13:16:19 -08:00
dsehnal
31a555255a bump immutable to 4.0 2022-01-17 17:56:39 +01:00
dsehnal
fbb60c9493 Treat empty string as non-present value in BinaryCIF 2022-01-17 16:21:14 +01:00
dsehnal
9f953ef51c add Model Export overlay 2022-01-17 15:47:56 +01:00
dsehnal
4871f1547c Generate structAsymMap from normalized atomic hierarchy
- Fixes issue with remapped chains
- Requires to parse PRD separately
2022-01-17 14:52:53 +01:00
dsehnal
d6413529f4 PR feedback 2022-01-17 13:58:20 +01:00
dsehnal
724cf5a0da Add integrations section to readme 2022-01-17 12:58:35 +01:00
dsehnal
b6847907ca Add ModelExport to viewer/app.ts 2022-01-17 11:37:49 +01:00
dsehnal
fb54a1aed7 Merge branch 'master' of https://github.com/molstar/molstar into export-extension 2022-01-17 11:37:16 +01:00
Alexander Rose
9815318daf add entity-source option to illustrative coloring 2022-01-16 23:08:43 -08:00
Alexander Rose
bc13b98111 Merge branch 'master' of https://github.com/molstar/molstar into anim-rock 2022-01-16 15:48:33 -08:00
Alexander Rose
238b70c121 simplify rock state/trackball animation
- use sin() instead of smoothstep()
2022-01-16 15:47:25 -08:00
Alexander Rose
dcd23bc0cb improve illustrative style support
- add illustrative representation preset
- add style option to illustrative color theme
2022-01-16 13:13:00 -08:00
Alexander Rose
4694ea85fa support custom colors in molecule-type theme 2022-01-16 13:05:43 -08:00
Alexander Rose
bdb17743d7 update schemas 2022-01-15 19:54:22 -08:00
Alexander Rose
8b76ff2461 update packages 2022-01-15 19:53:04 -08:00
Alexander Rose
ea5421002b add camera rock state animation 2022-01-15 18:59:12 -08:00
Alexander Rose
76ac55917d type, tweaks 2022-01-15 18:45:56 -08:00
Alexander Rose
5cfb2376c4 Merge branch 'master' of https://github.com/molstar/molstar into anim-rock 2022-01-15 15:56:55 -08:00
Alexander Rose
6b9d3fd80e cleaner MembraneOrientationVisuals defaults 2022-01-15 13:52:14 -08:00
dsehnal
a09752b62e changelog and contibutors in package.json 2022-01-14 12:53:51 +01:00
David Sehnal
3134e1d9f9 Merge pull request #341 from molstar/fix-camera-spin-stutter
Pass animation info to state animations
2022-01-14 12:51:51 +01:00
David Sehnal
e94ecf2a0b Merge pull request #314 from ptourlas/feature/formal-charge-labels
Feature/formal charge labels
2022-01-14 12:51:09 +01:00
ptourlas
1bd4d841a1 ADD: Test multiple charge lines in molfiles 2022-01-12 17:01:36 +02:00
ptourlas
8e349f47a5 (author tags) 2022-01-12 16:50:06 +02:00
ptourlas
119c0a4231 ADD: Test formal charge parsing in sdf reader 2022-01-12 16:25:23 +02:00
ptourlas
f009f533e0 TWEAK: Default charges in V3000 sdf files 2022-01-12 15:05:07 +02:00
ptourlas
3ab0c1e509 ADD: Test formal charge parsing in mol reader 2022-01-12 12:42:29 +02:00
dsehnal
e3d264e239 Pass animation info to state animations
+ Fix camera stutter for "camera spin"
2022-01-11 19:38:32 +01:00
dsehnal
9bd60f8e8e Fix getOperatorsForIndex 2022-01-11 17:34:43 +01:00
ptourlas
bcec1d9637 (forgot to cleanup) 2022-01-11 16:41:23 +02:00
ptourlas
1b431b1d20 RFR: Formal charge assignment at model creation 2022-01-11 16:04:46 +02:00
ptourlas
23c2dcdfd4 FIX: Use tokenizer to ensure the loop terminates 2022-01-11 15:01:15 +02:00
ptourlas
dd415bf802 FTR: Support multiple formal charge lines
* Start with two empty arrays for indexes and charges.
* Each `M CHG` line is passed to `handleFormalCharges()` and the
parsed elements are pushed to the arrays.
* The `Column`s are made at the end of this process.
2022-01-11 14:40:57 +02:00
ptourlas
7bc0e9db7c RFR: formalChargeMapper tweaks
* The key is of type number, no need to stringify it.
* Simplified `switch()`.
2022-01-11 00:18:25 +02:00
ptourlas
ca10bb01db Merge branch 'molstar:master' into feature/formal-charge-labels 2022-01-10 23:46:37 +02:00
Alexander Rose
c0f14b7c33 3.0.0-dev.9 2022-01-09 18:24:30 -08:00
Alexander Rose
b096f328fc changelog 2022-01-09 18:19:52 -08:00
Alexander Rose
88dbd43884 re-allow interaction during trackball animation
- was disallowed as a stop-gap measure
- ok after improving temporal multi sampling
2022-01-09 14:39:09 -08:00
Alexander Rose
ade5e4d4b8 re-allow interaction during trackball animation
- was disallowed as a stop-gap measure
- ok after improving temporal multi sampling
2022-01-09 14:30:43 -08:00
ptourlas
cb76b53a1b FTR: Add formal charges during model creation
This implementation takes into account both the property and atom block
cases and makes sure the latter is ignored if the first one is present.
2022-01-10 00:30:25 +02:00
Alexander Rose
b9423f70d4 Merge branch 'master' of https://github.com/molstar/molstar into anim-rock 2022-01-09 14:25:36 -08:00
Alexander Rose
d61e18e6f3 fix mol2 element symbol assignment 2022-01-09 14:04:31 -08:00
Alexander Rose
ca4a725a79 Merge pull request #336 from molstar/bond-dist-id
IndexPairBonds improvements
2022-01-09 13:37:38 -08:00
Alexander Rose
17a18d5fea tweak bond assignment from IndexPairBonds
- fix & clarify logic
2022-01-09 13:25:14 -08:00
Alexander Rose
73be238ac4 rename IndexPairBonds mapping field from id to key 2022-01-09 12:51:26 -08:00
Alexander Rose
796a034fec add rock animation to trackball controls 2022-01-08 17:02:33 -08:00
Alexander Rose
952b320975 Merge branch 'master' of https://github.com/molstar/molstar into bond-dist-id 2022-01-08 13:23:15 -08:00
Alexander Rose
be0f06ff0f fix mol2 crysin support 2022-01-08 13:22:26 -08:00
Alexander Rose
6294ef2db2 fix stats for single element in multi-chain unit
- observe in, e.g., label for water molecule in 3pqr
2022-01-08 12:42:37 -08:00
Alexander Rose
78b5d505bd improve IndexPairBonds
- add id field
- better distance-based assignment
2022-01-08 12:35:40 -08:00
Alexander Rose
22afdffa15 add mol2 symmetry support
- only for spacegroup setting 1
2022-01-08 12:14:14 -08:00
Alexander Rose
d1056eddeb Merge pull request #335 from molstar/standalone-viewer
move Viewer class to separate file
2022-01-08 11:55:53 -08:00
dsehnal
8655f4d85a move viewer app to separate file 2022-01-08 10:31:53 +01:00
ptourlas
f9deb54352 FIX: Found the right tokenizer operations
(at last)
2022-01-08 01:16:59 +02:00
ptourlas
eae3c1b33a FIX: Formal charges are all on the same line
Therefore we have to get the count of charges and iterate based on that.
A handle properties function is added so that new handler can be used based
the property type. Note that this function only returns the charges at
the moment for simplicity. A more general version should return multiple
properties.
2022-01-07 16:14:47 +02:00
ptourlas
5c5f8aa741 FTR:(WIP) Add formal charges during model creation 2022-01-06 16:12:44 +02:00
ptourlas
ba68ac2e32 FTR: Parse formal charges in the atom block 2022-01-06 15:58:49 +02:00
ptourlas
239fef281e Merge branch 'master' into feature/formal-charge-labels 2022-01-06 13:32:05 +02:00
dsehnal
c0880b647f changelog 2022-01-03 13:51:09 +01:00
dsehnal
039dc6a76b changelog 2022-01-03 13:49:59 +01:00
dsehnal
042a7625ad isWithoutOperator tweak 2022-01-03 13:46:13 +01:00
dsehnal
41827c478d open zip files with multiple entries 2022-01-03 13:42:37 +01:00
dsehnal
9a73180c3c support transformed export & structAsymMap parsing fix 2022-01-03 13:15:51 +01:00
dsehnal
333ee85fdb omit suffix for identity assembly operators 2022-01-03 12:52:05 +01:00
dsehnal
fa8ca45b6a do not include assembly categories in export if an operator has been applied 2022-01-03 12:34:37 +01:00
dsehnal
c2bae1aeb7 model export extension 2022-01-03 12:26:36 +01:00
Alexander Rose
ada7a45fe6 add PDBj pdb-provider option 2022-01-01 16:56:23 -08:00
Alexander Rose
2d09df55a9 3.0.0-dev.8 2021-12-31 11:42:09 -08:00
Alexander Rose
ec2554537e changelog 2021-12-31 11:37:40 -08:00
Alexander Rose
f266dfadc6 emulate round function for webgl1 compatibility 2021-12-31 11:37:02 -08:00
Alexander Rose
99048eed61 Merge pull request #323 from molstar/dv-refactor
Direct-volume refactor
2021-12-31 10:43:26 -08:00
Alexander Rose
fe63718b0c Merge branch 'master' into dv-refactor 2021-12-31 10:34:50 -08:00
Alexander Rose
2c1200433c Merge pull request #333 from molstar/surface-size-theme
Use size theme in molecular/gaussian surface & label representations
2021-12-31 10:33:46 -08:00
Alexander Rose
4a3252c929 use size theme in label repr 2021-12-30 18:17:40 -08:00
Alexander Rose
5e052174ee use size theme in molecular/gaussian surface repr 2021-12-30 18:12:53 -08:00
Alexander Rose
cda0966105 filter repr size themes to applicable types 2021-12-30 18:01:02 -08:00
Alexander Rose
c0a9716846 Merge branch 'master' of https://github.com/molstar/molstar into dv-refactor 2021-12-30 17:30:44 -08:00
Alexander Rose
18c7395f9d Merge pull request #326 from molstar/marker-fixes
Marker improvements
2021-12-30 17:29:07 -08:00
Alexander Rose
d3da79f3dd better use of StateSelection API 2021-12-30 17:18:21 -08:00
Alexander Rose
5a215daca4 remove superfluous arg from canvas3d.requestDraw 2021-12-30 17:14:07 -08:00
Alexander Rose
8527a3b3ef change canvas3d draw to use options argument 2021-12-30 17:08:28 -08:00
Alexander Rose
492dc1ba32 Merge branch 'master' of https://github.com/molstar/molstar into marker-fixes 2021-12-30 17:04:17 -08:00
Alexander Rose
305a8ca802 Merge pull request #332 from molstar/fix-331
fix gpu isosurface group id
2021-12-30 17:02:54 -08:00
Alexander Rose
6d2a35494f rename float-packing to number-packing
- more telling name
2021-12-30 17:01:23 -08:00
Alexander Rose
e76a08c73a remove unused en/decodeFloatLog 2021-12-30 16:59:39 -08:00
Alexander Rose
a0fef0c20f rename en/decodeFloatRGB
- since they only work for positive integers
- encodeFloatRGB -> packIntToRGB
- decodeFloatRGB -> unpackRGBToInt
2021-12-30 16:57:50 -08:00
Alexander Rose
605432ddd1 fix glsl decodeFloatRGB 2021-12-30 16:39:06 -08:00
Alexander Rose
0c895071d8 support axis order in direct-volume shader 2021-12-30 15:36:12 -08:00
Alexander Rose
79cd833ae6 cleanup 2021-12-30 14:37:34 -08:00
Alexander Rose
0fee928e37 fix gpu isosurface group id
- was wrong for axis order other than 012
- fixes #331
2021-12-30 13:49:08 -08:00
Alexander Rose
7f698336d7 fix webgl error in volume-streaming behavior
- don't use gpu mc for small volumes
- return empty texture-mesh for volumes of size 1 or 0
2021-12-29 18:51:55 -08:00
Alexander Rose
cef04f192a no picking during camera spinning 2021-12-29 16:54:26 -08:00
Alexander Rose
4087c4c226 changelog 2021-12-29 16:27:43 -08:00
Alexander Rose
87bdcd2372 add prefer-webgl1 & disable-wboit Viewer GET params 2021-12-29 16:03:45 -08:00
Alexander Rose
1dbcc0d7c8 Merge branch 'master' of https://github.com/molstar/molstar into dv-refactor 2021-12-29 15:42:41 -08:00
Alexander Rose
4c93f01c64 improve temporal multi-smaple handling
- add forceOn arg
- instead of letting temporal converge do full sampling
- saves one geometry pass
2021-12-28 20:39:03 -08:00
Alexander Rose
0a18412da0 refactor draw/multiSample .render args 2021-12-28 20:24:16 -08:00
Alexander Rose
b67d16bdc4 enable marking pass by default 2021-12-28 19:47:54 -08:00
Alexander Rose
976542d355 ensure mark changes are rendered (even w/ noDraw)
- schedule using forceNextRender to avoid rendering twice
2021-12-28 19:30:45 -08:00
Alexander Rose
a2e5fda646 improve temporal multi-sample rendering
- don't render all when spinning
- only render all when explicitely requested (new allowMulti arg)
2021-12-28 19:28:36 -08:00
Alexander Rose
41b1b65d5f improve SelectLoci behavior
- only handle direct children of updated objects (no update of same repr multiple times)
2021-12-28 19:25:40 -08:00
Alexander Rose
2ec2d1997f improve StructureSelectionManager.onUpdate
- only process for root structure (processing childs is superfluous)
2021-12-28 19:20:54 -08:00
Alexander Rose
0bc65f3b72 improve visual marking performance
- treat structure-loci as every-loci
2021-12-28 19:17:46 -08:00
Alexander Rose
9ed96b3599 fix getMarkersAverage edge case with small arrays 2021-12-28 19:16:08 -08:00
David Sehnal
b1cf9566f6 Merge pull request #324 from molstar/safari15-wboit
PluginFeatureDetection and disable WBOIT in Safari
2021-12-28 20:46:46 +01:00
ptourlas
13ea97bd98 Merge branch 'master' into feature/formal-charge-labels 2021-12-27 18:18:27 +02:00
ptourlas
009a17a9ca (forgot the author tag) 2021-12-27 17:56:25 +02:00
ptourlas
ca38d9adb1 (WIP) Formal charges handler implementation 2021-12-27 17:44:59 +02:00
ptourlas
4e5a86e3db (WIP) Add the formal charges handler to sdf parser 2021-12-27 17:37:52 +02:00
dsehnal
983ae4f8c2 PluginFeatureDetection and disable WBOIT in Safari 2021-12-24 11:21:15 +01:00
Alexander Rose
7ce3531cc7 direct-volume define cleanup 2021-12-23 15:35:14 -08:00
Alexander Rose
11f1a7fd1c add general dGeometryType
- remove dRenderMode & dGeoTexture
2021-12-23 15:16:14 -08:00
Alexander Rose
47d7dd4d22 move direct-volume coloring into theme
- add 'direct' color type
- remove color from transfer-function (now only alpha)
- add direct-volume color theme support
- add volume-value color theme
2021-12-23 14:47:17 -08:00
Alexander Rose
0d4f6bb5d9 3.0.0-dev.7 2021-12-20 22:42:23 -08:00
Alexander Rose
b3a4e1976d changelog 2021-12-20 22:36:56 -08:00
Alexander Rose
8b3c0fd94e Merge branch 'master' of https://github.com/molstar/molstar into dv-refactor 2021-12-20 17:51:38 -08:00
Alexander Rose
ab4a24d8ab Merge pull request #310 from molstar/clipping
Per Object Clip Objects
2021-12-20 17:38:41 -08:00
Alexander Rose
7c93e9f834 changelog & fix clipping exclude group filtering 2021-12-20 17:22:15 -08:00
Alexander Rose
90ea8cfebd Merge branch 'master' of https://github.com/molstar/molstar into clipping 2021-12-20 16:49:58 -08:00
Alexander Rose
7fc1866dac Merge pull request #320 from molstar/shader-compilation
Shader compilation
2021-12-20 16:46:56 -08:00
Alexander Rose
bb520ff424 fix spec 2021-12-20 16:38:24 -08:00
Alexander Rose
f9d2e20cb9 webgl1 compat 2021-12-20 16:16:22 -08:00
Alexander Rose
6f13b67bf1 combine markingDepth/markingMask shader variants 2021-12-20 16:14:37 -08:00
Alexander Rose
f37026a980 Merge branch 'master' of https://github.com/molstar/molstar into shader-compilation 2021-12-20 14:15:06 -08:00
Alexander Rose
74f123265b correctly set shader define flags
- overpaint, transparency, substance, clipping
2021-12-18 19:53:35 -08:00
Alexander Rose
ccfae65b01 fix tests 2021-12-18 19:29:02 -08:00
Alexander Rose
bcfaef77c9 combined pick shader variant 2021-12-18 18:11:31 -08:00
Alexander Rose
0b6243c0d1 remove log statement 2021-12-18 16:53:48 -08:00
Alexander Rose
472866d8ec support ignoring defines for shader variants 2021-12-18 16:48:56 -08:00
Alexander Rose
471163f3d8 better defaults for postprocessing pass 2021-12-18 16:44:08 -08:00
Alexander Rose
ab6106896d only include vPaletteV for color shaders 2021-12-18 16:43:48 -08:00
Alexander Rose
7ca624d04b remove dMarkerType shader define 2021-12-18 16:42:43 -08:00
Alexander Rose
bd3d18f43f use uniform for double-sided shader param 2021-12-18 16:39:15 -08:00
Alexander Rose
21eb21b6dd support variants for graphics render-itms 2021-12-18 16:32:20 -08:00
ptourlas
a736fe7989 Add formal charge option to atom site 2021-12-15 00:35:34 +02:00
ptourlas
1970b7f249 Spot the formal charge prefix 2021-12-15 00:34:06 +02:00
Alexander Rose
24ad38e260 add clip support to components & focus repr 2021-12-12 18:09:26 -08:00
Alexander Rose
53bac83dff remove renderable.noClip 2021-12-12 17:59:09 -08:00
Alexander Rose
ab1578f667 move clip props to base geometry 2021-12-12 17:56:14 -08:00
Alexander Rose
4e70301b62 cleanup after merge 2021-12-12 17:27:47 -08:00
Alexander Rose
76ed2e9e11 Merge branch 'master' of https://github.com/molstar/molstar into clipping 2021-12-12 17:11:59 -08:00
Alexander Rose
aa1f081664 remove direct-volume isosurface render-mode 2021-12-05 14:24:18 -08:00
Alexander Rose
504406eb22 move clip variant and objects to repr state 2021-08-01 21:16:44 -07:00
213 changed files with 7036 additions and 7585 deletions

View File

@@ -6,6 +6,115 @@ Note that since we don't clearly distinguish between a public and private interf
## [Unreleased]
## [v3.2.0] - 2022-02-17
- Rename "best database mapping" to "SIFTS Mapping"
- Add schema and export support for ``atom_site.pdbx_sifts_xref_*`` fields
- Add schema export support for ``atom_site.pdbx_label_index`` field
- Add `traceOnly` parameter to chain/UniProt-based structure alignment
- Store ``IndexPairBonds`` as a dynamic property.
## [v3.1.0] - 2022-02-06
- Fix ``xrayShaded`` & ``ignoreLight`` params not working at the same time
- Add ``ignoreLight`` to component params
- Tweaks for cleaner default representation style
- Cartoon: use ``nucleotide-ring`` instead of ``nucleotide-block``
- Focus: use ``xrayShaded`` instead of opacity; adjust target size; don't show non-covalent interactions twice
- Fix representation preset side effects (changing post-processing parameters, see #363)
- Add Quick Styles panel (default, illustrative, stylized)
- Fix exported structure missing secondary-structure categories (#364)
- Fix volume streaming error message: distinguish between missing data and server error (#364)
## [v3.0.2] - 2022-01-30
- Fix color smoothing of elongated structures (by fixing ``Sphere.expand`` for spheres with highly directional extrema)
- Fix entity label not displayed when multiple instances of the same entity are highlighted
- Fix empty elements created in ``StructureElement.Loci.extendToAllInstances``
- Measurement options tweaks (allow larger ``textSize``; make ``customText`` essential)
- Fix visual visibility sync edge case when changing state snapshots
## [v3.0.1] - 2022-01-27
- Fix marking pass not working with ``transparentBackground``
- Fix pdbe xray maps url not https
- Fix entity-id color theme broken for non-IHM models
- Improve/fix marking of ``InteractionsInterUnitVisual`` (mark when all contact-feature members are given)
- Add missing "entity-id" and "enity-source" options for carbon coloring to "element-symbol" color theme
- Fix VolumeServer/query CLI
- Support automatic iso-value adjustment for VolumeServer data in ``Viewer.loadVolumeFromUrl``
- Emit drag event whenever started within viewport (not only for non-empty loci)
## [v3.0.0] - 2022-01-23
- Assembly handling tweaks:
- Do not include suffix for "identity assembly operators"
- Do not include assembly-related categories to export if the structure was composed from an assembly
- Special case for ``structAsymMap`` if Mol* asym id operator mapping is present
- Support for opening ZIP files with multiple entries
- Add Model Export extension
- Bugfix: Automatically treat empty string as "non-present" value in BinaryCIF writer.
- Fix coarse model support in entity-id color theme
- Fix marking of carbohydrate visuals (whole chain could get marked instead of single residue)
- Add custom colors to "element-symbol", "molecule-type", "residue-name", and "secondary-structure" themes
- Support/bugfixes for ``atom_site.pdbx_sifts_xref`` categories
- Improve/fix marking of ``InteractionsIntraUnitVisual`` (mark when all contact-feature members are given)
## [v3.0.0-dev.10] - 2022-01-17
- Fix ``getOperatorsForIndex``
- Pass animation info (current frame & count) to state animations
- Fix camera stutter for "camera spin" animation
- Add formal charge parsing support for MOL/SDF files (thanks @ptourlas)
- [Breaking] Cleaner looking ``MembraneOrientationVisuals`` defaults
- [Breaking] Add rock animation to trackball controls
- Add ``animate`` to ``TrackballControlsParams``, remove ``spin`` and ``spinSpeed``
- Add ``animate`` to ``SimpleSettingsParams``, remove ``spin``
- Add "camera rock" state animation
- Add support for custom colors to "molecule-type" theme
- [Breaking] Add style parameter to "illustrative" color theme
- Defaults to "entity-id" style instead of "chain-id"
- Add "illustrative" representation preset
## [v3.0.0-dev.9] - 2022-01-09
- Add PDBj as a ``pdb-provider`` option
- Move Viewer APP to a separate file to allow use without importing light theme & index.html
- Add symmetry support for mol2 files (only spacegroup setting 1)
- Fix mol2 files element symbol assignment
- Improve bond assignment from ``IndexPairBonds``
- Add ``key`` field for mapping to source data
- Fix assignment of bonds with unphysical length
- Fix label/stats of single atom selection in multi-chain units
## [v3.0.0-dev.8] - 2021-12-31
- Add ``PluginFeatureDetection`` and disable WBOIT in Safari 15.
- Add ``disable-wboit`` Viewer GET param
- Add ``prefer-webgl1`` Viewer GET param
- [Breaking] Refactor direct-volume rendering
- Remove isosurface render-mode (use GPU MC instead)
- Move coloring into theme (like for other geometries/renderables)
- Add ``direct`` color type
- Remove color from transfer-function (now only alpha)
- Add direct-volume color theme support
- Add volume-value color theme
- [Breaking] Use size theme in molecular/gaussian surface & label representations
- This is breaking because it was hardcoded to ``physical`` internally but the repr size theme default was ``uniform`` (now ``physical``)
## [v3.0.0-dev.7] - 2021-12-20
- Reduce number of created programs/shaders
- Support specifying variants when creating graphics render-items
- Change double-side shader param from define to uniform
- Remove dMarkerType shader define (use uMarker as needed)
- Support to ignore defines depending on the shader variant
- Combine pickObject/pickInstance/pickGroup shader variants into one
- Combine markingDepth/markingMask shader variants into one
- Correctly set shader define flags for overpaint, transparency, substance, clipping
- [Breaking] Add per-object clip rendering properties (variant/objects)
- ``SimpleSettingsParams.clipping.variant/objects`` and ``RendererParams.clip`` were removed
## [v3.0.0-dev.6] - 2021-12-19
- Enable temporal multi-sampling by default

72
CITATION.cff Normal file
View File

@@ -0,0 +1,72 @@
cff-version: 1.2.0
title: >-
Mol* library
message: >-
Please cite this software using the metadata from
'preferred-citation'.
authors:
- given-names: Alexander S
family-names: Rose
orcid: 'https://orcid.org/0000-0002-0893-5551'
- given-names: David
family-names: Sehnal
orcid: 'https://orcid.org/0000-0002-0682-3089'
- given-names: Sebastian
family-names: Bittrich
orcid: 'https://orcid.org/0000-0003-3576-0387'
- given-names: Áron Samuel
family-names: Kovács
- given-names: Ludovic
family-names: Autin
orcid: 'https://orcid.org/0000-0002-2197-191X'
- given-names: Michal
family-names: Malý
- given-names: Jiří
family-names: Černý
- given-names: Panagiotis
family-names: Tourlas
type: software
doi: 10.5281/zenodo.3947306
preferred-citation:
authors:
- given-names: David
family-names: Sehnal
orcid: 'https://orcid.org/0000-0002-0682-3089'
- given-names: Sebastian
family-names: Bittrich
orcid: 'https://orcid.org/0000-0003-3576-0387'
- given-names: Mandar
family-names: Deshpande
orcid: 'https://orcid.org/0000-0002-9043-7665'
- given-names: Radka
family-names: Svobodová
orcid: 'https://orcid.org/0000-0002-3840-8760'
- given-names: Karel
family-names: Berka
orcid: 'https://orcid.org/0000-0001-9472-2589'
- given-names: Václav
family-names: Bazgier
orcid: 'https://orcid.org/0000-0003-3393-3010'
- given-names: Sameer
family-names: Velankar
orcid: 'https://orcid.org/0000-0002-8439-5964'
- given-names: Stephen K
family-names: Burley
orcid: 'https://orcid.org/0000-0002-2487-9713'
- given-names: Jaroslav
family-names: Koča
orcid: 'https://orcid.org/0000-0002-2780-4901'
- given-names: Alexander S
family-names: Rose
orcid: 'https://orcid.org/0000-0002-0893-5551'
title: >-
Mol* Viewer: modern web app for 3D visualization
and analysis of large biomolecular structures
type: article
doi: 10.1093/nar/gkab314
journal: "Nucleic Acids Research"
issue: W1
volume: 49
year: 2021
month: 7
pages: "W431W437"

View File

@@ -11,6 +11,13 @@ When using Mol*, please cite:
David Sehnal, Sebastian Bittrich, Mandar Deshpande, Radka Svobodová, Karel Berka, Václav Bazgier, Sameer Velankar, Stephen K Burley, Jaroslav Koča, Alexander S Rose: [Mol* Viewer: modern web app for 3D visualization and analysis of large biomolecular structures](https://doi.org/10.1093/nar/gkab314), *Nucleic Acids Research*, 2021; https://doi.org/10.1093/nar/gkab314.
### Protein Data Bank Integrations
- The [pdbe-molstar](https://github.com/molstar/pdbe-molstar) library is the Mol* implementation used by EMBL-EBI data resources such as [PDBe](https://pdbe.org/), [PDBe-KB](https://pdbe-kb.org/) and [AlphaFold DB](https://alphafold.ebi.ac.uk/). This implementation can be used as a JS plugin and a Web component and supports property/attribute-based easy customisation. It provides helper methods to facilitate programmatic interactions between the web application and the 3D viewer. It also provides a superposition view for overlaying all the observed ligand molecules on representative protein conformations.
- [rcsb-molstar](https://github.com/molstar/rcsb-molstar) is the Mol* plugin used by [RCSB PDB](https://www.rcsb.org). The project provides additional presets for the visualization of structure alignments and structure motifs such as ligand binding sites. Furthermore, [rcsb-molstar](https://github.com/molstar/rcsb-molstar) allows to interactively add or hide of (parts of) chains, as seen in the [3D Protein Feature View](https://www.rcsb.org/3d-sequence/4hhb).
## Project Structure Overview
The core of Mol* consists of these modules (see under `src/`):

View File

@@ -24,6 +24,11 @@ atom_site.auth_asym_id
atom_site.auth_seq_id
atom_site.pdbx_PDB_model_num
atom_site.ihm_model_id
atom_site.pdbx_label_index
atom_site.pdbx_sifts_xref_db_name
atom_site.pdbx_sifts_xref_db_acc
atom_site.pdbx_sifts_xref_db_num
atom_site.pdbx_sifts_xref_db_res
atom_site_anisotrop.id
atom_site_anisotrop.U
1 atom_sites.entry_id
24 atom_site.pdbx_PDB_model_num
25 atom_site.ihm_model_id
26 atom_site_anisotrop.id atom_site.pdbx_label_index
27 atom_site.pdbx_sifts_xref_db_name
28 atom_site.pdbx_sifts_xref_db_acc
29 atom_site.pdbx_sifts_xref_db_num
30 atom_site.pdbx_sifts_xref_db_res
31 atom_site_anisotrop.id
32 atom_site_anisotrop.U
33 atom_site_anisotrop.U_esd
34 atom_site_anisotrop.pdbx_PDB_ins_code

8634
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "3.0.0-dev.6",
"version": "3.2.0",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -13,7 +13,7 @@
"scripts": {
"lint": "eslint .",
"lint-fix": "eslint . --fix",
"test": "npm run lint && jest",
"test": "npm install --no-save \"gl@^5.0.0\" && npm run lint && jest",
"jest": "jest",
"build": "npm run build-tsc && npm run build-extra && npm run build-webpack",
"clean": "node ./scripts/clean.js",
@@ -86,68 +86,69 @@
"Áron Samuel Kovács <aron.kovacs@mail.muni.cz>",
"Ludovic Autin <autin@scripps.edu>",
"Michal Malý <michal.maly@ibt.cas.cz>",
"Jiří Černý <jiri.cerny@ibt.cas.cz>"
"Jiří Černý <jiri.cerny@ibt.cas.cz>",
"Panagiotis Tourlas <panagiot_tourlov@hotmail.com>"
],
"license": "MIT",
"devDependencies": {
"@graphql-codegen/add": "^3.1.0",
"@graphql-codegen/cli": "^2.3.0",
"@graphql-codegen/time": "^3.1.0",
"@graphql-codegen/typescript": "^2.4.1",
"@graphql-codegen/typescript-graphql-files-modules": "^2.1.0",
"@graphql-codegen/typescript-graphql-request": "^4.3.1",
"@graphql-codegen/typescript-operations": "^2.2.1",
"@graphql-codegen/add": "^3.1.1",
"@graphql-codegen/cli": "^2.5.0",
"@graphql-codegen/time": "^3.1.1",
"@graphql-codegen/typescript": "^2.4.3",
"@graphql-codegen/typescript-graphql-files-modules": "^2.1.1",
"@graphql-codegen/typescript-graphql-request": "^4.3.4",
"@graphql-codegen/typescript-operations": "^2.2.4",
"@types/cors": "^2.8.12",
"@types/gl": "^4.1.0",
"@types/jest": "^27.0.3",
"@typescript-eslint/eslint-plugin": "^5.5.0",
"@typescript-eslint/parser": "^5.5.0",
"@types/jest": "^27.4.0",
"@typescript-eslint/eslint-plugin": "^5.10.2",
"@typescript-eslint/parser": "^5.10.2",
"benchmark": "^2.1.4",
"concurrently": "^6.4.0",
"cpx2": "^4.0.0",
"concurrently": "^7.0.0",
"cpx2": "^4.1.2",
"crypto-browserify": "^3.12.0",
"css-loader": "^6.5.1",
"eslint": "^8.3.0",
"css-loader": "^6.6.0",
"eslint": "^8.8.0",
"extra-watch-webpack-plugin": "^1.0.3",
"file-loader": "^6.2.0",
"fs-extra": "^10.0.0",
"graphql": "^15.7.2",
"http-server": "^14.0.0",
"jest": "^27.3.1",
"mini-css-extract-plugin": "^2.4.5",
"graphql": "^16.3.0",
"http-server": "^14.1.0",
"jest": "^27.5.0",
"mini-css-extract-plugin": "^2.5.3",
"path-browserify": "^1.0.1",
"raw-loader": "^4.0.2",
"sass": "^1.43.5",
"sass-loader": "^12.3.0",
"simple-git": "^2.47.0",
"sass": "^1.49.7",
"sass-loader": "^12.4.0",
"simple-git": "^3.1.1",
"stream-browserify": "^3.0.0",
"style-loader": "^3.3.1",
"ts-jest": "^27.0.7",
"typescript": "^4.5.2",
"webpack": "^5.64.4",
"webpack-cli": "^4.9.1"
"ts-jest": "^27.1.3",
"typescript": "^4.5.5",
"webpack": "^5.68.0",
"webpack-cli": "^4.9.2"
},
"dependencies": {
"@types/argparse": "^2.0.10",
"@types/benchmark": "^2.1.1",
"@types/compression": "1.7.2",
"@types/express": "^4.17.13",
"@types/node": "^16.11.10",
"@types/node": "^16.11.22",
"@types/node-fetch": "^2.5.12",
"@types/react": "^17.0.37",
"@types/react": "^17.0.39",
"@types/react-dom": "^17.0.11",
"@types/swagger-ui-dist": "3.30.1",
"argparse": "^2.0.1",
"body-parser": "^1.19.0",
"body-parser": "^1.19.1",
"compression": "^1.7.4",
"cors": "^2.8.5",
"express": "^4.17.1",
"express": "^4.17.2",
"h264-mp4-encoder": "^1.0.12",
"immer": "^9.0.7",
"immutable": "^3.8.2",
"node-fetch": "^2.6.2",
"rxjs": "^7.4.0",
"swagger-ui-dist": "^4.1.1",
"immer": "^9.0.12",
"immutable": "^4.0.0",
"node-fetch": "^2.6.7",
"rxjs": "^7.5.2",
"swagger-ui-dist": "^4.5.0",
"tslib": "^2.3.1",
"util.promisify": "^1.1.1",
"xhr2": "^0.2.1"
@@ -155,8 +156,5 @@
"peerDependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"optionalDependencies": {
"gl": "^4.9.2"
}
}

491
src/apps/viewer/app.ts Normal file
View File

@@ -0,0 +1,491 @@
/**
* 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 { ANVILMembraneOrientation } from '../../extensions/anvil/behavior';
import { CellPack } from '../../extensions/cellpack';
import { DnatcoConfalPyramids } from '../../extensions/dnatco';
import { G3DFormat, G3dProvider } from '../../extensions/g3d/format';
import { GeometryExport } from '../../extensions/geo-export';
import { MAQualityAssessment } from '../../extensions/model-archive/quality-assessment/behavior';
import { QualityAssessmentPLDDTPreset, QualityAssessmentQmeanPreset } from '../../extensions/model-archive/quality-assessment/behavior';
import { QualityAssessment } from '../../extensions/model-archive/quality-assessment/prop';
import { ModelExport } from '../../extensions/model-export';
import { Mp4Export } from '../../extensions/mp4-export';
import { PDBeStructureQualityReport } from '../../extensions/pdbe';
import { RCSBAssemblySymmetry, RCSBValidationReport } from '../../extensions/rcsb';
import { Volume } from '../../mol-model/volume';
import { DownloadStructure, PdbDownloadProvider } from '../../mol-plugin-state/actions/structure';
import { DownloadDensity } from '../../mol-plugin-state/actions/volume';
import { PresetTrajectoryHierarchy } from '../../mol-plugin-state/builder/structure/hierarchy-preset';
import { PresetStructureRepresentations, StructureRepresentationPresetProvider } from '../../mol-plugin-state/builder/structure/representation-preset';
import { DataFormatProvider } from '../../mol-plugin-state/formats/provider';
import { BuildInStructureFormat } from '../../mol-plugin-state/formats/structure';
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
import { BuildInVolumeFormat } from '../../mol-plugin-state/formats/volume';
import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers/volume-representation-params';
import { PluginStateObject } from '../../mol-plugin-state/objects';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { TrajectoryFromModelAndCoordinates } from '../../mol-plugin-state/transforms/model';
import { createPluginUI } from '../../mol-plugin-ui';
import { PluginUIContext } from '../../mol-plugin-ui/context';
import { DefaultPluginUISpec, PluginUISpec } from '../../mol-plugin-ui/spec';
import { PluginCommands } from '../../mol-plugin/commands';
import { PluginConfig } from '../../mol-plugin/config';
import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout';
import { PluginSpec } from '../../mol-plugin/spec';
import { PluginState } from '../../mol-plugin/state';
import { StateObjectRef, StateObjectSelector } from '../../mol-state';
import { Asset } from '../../mol-util/assets';
import { Color } from '../../mol-util/color';
import '../../mol-util/polyfill';
import { ObjectKeys } from '../../mol-util/type-helpers';
export { PLUGIN_VERSION as version } from '../../mol-plugin/version';
export { setDebugMode, setProductionMode } from '../../mol-util/debug';
const CustomFormats = [
['g3d', G3dProvider] as const
];
const Extensions = {
'cellpack': PluginSpec.Behavior(CellPack),
'dnatco-confal-pyramids': PluginSpec.Behavior(DnatcoConfalPyramids),
'pdbe-structure-quality-report': PluginSpec.Behavior(PDBeStructureQualityReport),
'rcsb-assembly-symmetry': PluginSpec.Behavior(RCSBAssemblySymmetry),
'rcsb-validation-report': PluginSpec.Behavior(RCSBValidationReport),
'anvil-membrane-orientation': PluginSpec.Behavior(ANVILMembraneOrientation),
'g3d': PluginSpec.Behavior(G3DFormat),
'model-export': PluginSpec.Behavior(ModelExport),
'mp4-export': PluginSpec.Behavior(Mp4Export),
'geo-export': PluginSpec.Behavior(GeometryExport),
'ma-quality-assessment': PluginSpec.Behavior(MAQualityAssessment),
};
const DefaultViewerOptions = {
customFormats: CustomFormats as [string, DataFormatProvider][],
extensions: ObjectKeys(Extensions),
layoutIsExpanded: true,
layoutShowControls: true,
layoutShowRemoteState: true,
layoutControlsDisplay: 'reactive' as PluginLayoutControlsDisplay,
layoutShowSequence: true,
layoutShowLog: true,
layoutShowLeftPanel: true,
collapseLeftPanel: false,
collapseRightPanel: false,
disableAntialiasing: PluginConfig.General.DisableAntialiasing.defaultValue,
pixelScale: PluginConfig.General.PixelScale.defaultValue,
pickScale: PluginConfig.General.PickScale.defaultValue,
pickPadding: PluginConfig.General.PickPadding.defaultValue,
enableWboit: PluginConfig.General.EnableWboit.defaultValue,
preferWebgl1: PluginConfig.General.PreferWebGl1.defaultValue,
viewportShowExpand: PluginConfig.Viewport.ShowExpand.defaultValue,
viewportShowControls: PluginConfig.Viewport.ShowControls.defaultValue,
viewportShowSettings: PluginConfig.Viewport.ShowSettings.defaultValue,
viewportShowSelectionMode: PluginConfig.Viewport.ShowSelectionMode.defaultValue,
viewportShowAnimation: PluginConfig.Viewport.ShowAnimation.defaultValue,
pluginStateServer: PluginConfig.State.DefaultServer.defaultValue,
volumeStreamingServer: PluginConfig.VolumeStreaming.DefaultServer.defaultValue,
volumeStreamingDisabled: !PluginConfig.VolumeStreaming.Enabled.defaultValue,
pdbProvider: PluginConfig.Download.DefaultPdbProvider.defaultValue,
emdbProvider: PluginConfig.Download.DefaultEmdbProvider.defaultValue,
};
type ViewerOptions = typeof DefaultViewerOptions;
export class Viewer {
constructor(public plugin: PluginUIContext) {
}
static async create(elementOrId: string | HTMLElement, options: Partial<ViewerOptions> = {}) {
const definedOptions = {} as any;
// filter for defined properies only so the default values
// are property applied
for (const p of Object.keys(options) as (keyof ViewerOptions)[]) {
if (options[p] !== void 0) definedOptions[p] = options[p];
}
const o: ViewerOptions = { ...DefaultViewerOptions, ...definedOptions };
const defaultSpec = DefaultPluginUISpec();
const spec: PluginUISpec = {
actions: defaultSpec.actions,
behaviors: [
...defaultSpec.behaviors,
...o.extensions.map(e => Extensions[e]),
],
animations: [...defaultSpec.animations || []],
customParamEditors: defaultSpec.customParamEditors,
customFormats: o?.customFormats,
layout: {
initial: {
isExpanded: o.layoutIsExpanded,
showControls: o.layoutShowControls,
controlsDisplay: o.layoutControlsDisplay,
regionState: {
bottom: 'full',
left: o.collapseLeftPanel ? 'collapsed' : 'full',
right: o.collapseRightPanel ? 'hidden' : 'full',
top: 'full',
}
},
},
components: {
...defaultSpec.components,
controls: {
...defaultSpec.components?.controls,
top: o.layoutShowSequence ? undefined : 'none',
bottom: o.layoutShowLog ? undefined : 'none',
left: o.layoutShowLeftPanel ? undefined : 'none',
},
remoteState: o.layoutShowRemoteState ? 'default' : 'none',
},
config: [
[PluginConfig.General.DisableAntialiasing, o.disableAntialiasing],
[PluginConfig.General.PixelScale, o.pixelScale],
[PluginConfig.General.PickScale, o.pickScale],
[PluginConfig.General.PickPadding, o.pickPadding],
[PluginConfig.General.EnableWboit, o.enableWboit],
[PluginConfig.General.PreferWebGl1, o.preferWebgl1],
[PluginConfig.Viewport.ShowExpand, o.viewportShowExpand],
[PluginConfig.Viewport.ShowControls, o.viewportShowControls],
[PluginConfig.Viewport.ShowSettings, o.viewportShowSettings],
[PluginConfig.Viewport.ShowSelectionMode, o.viewportShowSelectionMode],
[PluginConfig.Viewport.ShowAnimation, o.viewportShowAnimation],
[PluginConfig.State.DefaultServer, o.pluginStateServer],
[PluginConfig.State.CurrentServer, o.pluginStateServer],
[PluginConfig.VolumeStreaming.DefaultServer, o.volumeStreamingServer],
[PluginConfig.VolumeStreaming.Enabled, !o.volumeStreamingDisabled],
[PluginConfig.Download.DefaultPdbProvider, o.pdbProvider],
[PluginConfig.Download.DefaultEmdbProvider, o.emdbProvider],
[PluginConfig.Structure.DefaultRepresentationPreset, ViewerAutoPreset.id],
]
};
const element = typeof elementOrId === 'string'
? document.getElementById(elementOrId)
: elementOrId;
if (!element) throw new Error(`Could not get element with id '${elementOrId}'`);
const plugin = await createPluginUI(element, spec, {
onBeforeUIRender: plugin => {
// the preset needs to be added before the UI renders otherwise
// "Download Structure" wont be able to pick it up
plugin.builders.structure.representation.registerPreset(ViewerAutoPreset);
}
});
return new Viewer(plugin);
}
setRemoteSnapshot(id: string) {
const url = `${this.plugin.config.get(PluginConfig.State.CurrentServer)}/get/${id}`;
return PluginCommands.State.Snapshots.Fetch(this.plugin, { url });
}
loadSnapshotFromUrl(url: string, type: PluginState.SnapshotType) {
return PluginCommands.State.Snapshots.OpenUrl(this.plugin, { url, type });
}
loadStructureFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false, options?: LoadStructureOptions) {
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
source: {
name: 'url',
params: {
url: Asset.Url(url),
format: format as any,
isBinary,
options: { ...params.source.params.options, representationParams: options?.representationParams as any },
}
}
}));
}
async loadAllModelsOrAssemblyFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false, options?: LoadStructureOptions) {
const plugin = this.plugin;
const data = await plugin.builders.data.download({ url, isBinary }, { state: { isGhost: true } });
const trajectory = await plugin.builders.structure.parseTrajectory(data, format);
await this.plugin.builders.structure.hierarchy.applyPreset(trajectory, 'all-models', { useDefaultIfSingleModel: true, representationPresetParams: options?.representationParams });
}
async loadStructureFromData(data: string | number[], format: BuiltInTrajectoryFormat, options?: { dataLabel?: string }) {
const _data = await this.plugin.builders.data.rawData({ data, label: options?.dataLabel });
const trajectory = await this.plugin.builders.structure.parseTrajectory(_data, format);
await this.plugin.builders.structure.hierarchy.applyPreset(trajectory, 'default');
}
loadPdb(pdb: string, options?: LoadStructureOptions) {
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
const provider = this.plugin.config.get(PluginConfig.Download.DefaultPdbProvider)!;
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
source: {
name: 'pdb' as const,
params: {
provider: {
id: pdb,
server: {
name: provider,
params: PdbDownloadProvider[provider].defaultValue as any
}
},
options: { ...params.source.params.options, representationParams: options?.representationParams as any },
}
}
}));
}
loadPdbDev(pdbDev: string) {
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
source: {
name: 'pdb-dev' as const,
params: {
provider: {
id: pdbDev,
encoding: 'bcif',
},
options: params.source.params.options,
}
}
}));
}
loadEmdb(emdb: string, options?: { detail?: number }) {
const provider = this.plugin.config.get(PluginConfig.Download.DefaultEmdbProvider)!;
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadDensity, {
source: {
name: 'pdb-emd-ds' as const,
params: {
provider: {
id: emdb,
server: provider,
},
detail: options?.detail ?? 3,
}
}
}));
}
loadAlphaFoldDb(afdb: string) {
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
source: {
name: 'alphafolddb' as const,
params: {
id: afdb,
options: {
...params.source.params.options,
representation: 'preset-structure-representation-ma-quality-assessment-plddt'
},
}
}
}));
}
loadModelArchive(id: string) {
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
source: {
name: 'modelarchive' as const,
params: {
id,
options: params.source.params.options,
}
}
}));
}
/**
* @example Load X-ray density from volume server
viewer.loadVolumeFromUrl({
url: 'https://www.ebi.ac.uk/pdbe/densities/x-ray/1tqn/cell?detail=3',
format: 'dscif',
isBinary: true
}, [{
type: 'relative',
value: 1.5,
color: 0x3362B2
}, {
type: 'relative',
value: 3,
color: 0x33BB33,
volumeIndex: 1
}, {
type: 'relative',
value: -3,
color: 0xBB3333,
volumeIndex: 1
}], {
entryId: ['2FO-FC', 'FO-FC'],
isLazy: true
});
* *********************
* @example Load EM density from volume server
viewer.loadVolumeFromUrl({
url: 'https://maps.rcsb.org/em/emd-30210/cell?detail=6',
format: 'dscif',
isBinary: true
}, [{
type: 'relative',
value: 1,
color: 0x3377aa
}], {
entryId: 'EMD-30210',
isLazy: true
});
*/
async loadVolumeFromUrl({ url, format, isBinary }: { url: string, format: BuildInVolumeFormat, isBinary: boolean }, isovalues: VolumeIsovalueInfo[], options?: { entryId?: string | string[], isLazy?: boolean }) {
const plugin = this.plugin;
if (!plugin.dataFormats.get(format)) {
throw new Error(`Unknown density format: ${format}`);
}
if (options?.isLazy) {
const update = this.plugin.build();
update.toRoot().apply(StateTransforms.Data.LazyVolume, {
url,
format,
entryId: options?.entryId,
isBinary,
isovalues: isovalues.map(v => ({ alpha: 1, volumeIndex: 0, ...v }))
});
return update.commit();
}
return plugin.dataTransaction(async () => {
const data = await plugin.builders.data.download({ url, isBinary }, { state: { isGhost: true } });
const parsed = await plugin.dataFormats.get(format)!.parse(plugin, data, { entryId: options?.entryId });
const firstVolume = (parsed.volume || parsed.volumes[0]) as StateObjectSelector<PluginStateObject.Volume.Data>;
if (!firstVolume?.isOk) throw new Error('Failed to parse any volume.');
const repr = plugin.build();
for (const iso of isovalues) {
const volume: StateObjectSelector<PluginStateObject.Volume.Data> = parsed.volumes?.[iso.volumeIndex ?? 0] ?? parsed.volume;
const volumeData = volume.cell!.obj!.data;
repr
.to(volume)
.apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(this.plugin, firstVolume.data!, {
type: 'isosurface',
typeParams: { alpha: iso.alpha ?? 1, isoValue: Volume.adjustedIsoValue(volumeData, iso.value, iso.type) },
color: 'uniform',
colorParams: { value: iso.color }
}));
}
await repr.commit();
});
}
/**
* @example
* viewer.loadTrajectory({
* model: { kind: 'model-url', url: 'villin.gro', format: 'gro' },
* coordinates: { kind: 'coordinates-url', url: 'villin.xtc', format: 'xtc', isBinary: true },
* preset: 'all-models' // or 'default'
* });
*/
async loadTrajectory(params: LoadTrajectoryParams) {
const plugin = this.plugin;
let model: StateObjectSelector, coords: StateObjectSelector;
if (params.model.kind === 'model-data' || params.model.kind === 'model-url') {
const data = params.model.kind === 'model-data'
? await plugin.builders.data.rawData({ data: params.model.data, label: params.modelLabel })
: await plugin.builders.data.download({ url: params.model.url, isBinary: params.model.isBinary, label: params.modelLabel });
const trajectory = await plugin.builders.structure.parseTrajectory(data, params.model.format ?? 'mmcif');
model = await plugin.builders.structure.createModel(trajectory);
} else {
const data = params.model.kind === 'topology-data'
? await plugin.builders.data.rawData({ data: params.model.data, label: params.modelLabel })
: await plugin.builders.data.download({ url: params.model.url, isBinary: params.model.isBinary, label: params.modelLabel });
const provider = plugin.dataFormats.get(params.model.format);
model = await provider!.parse(plugin, data);
}
{
const data = params.coordinates.kind === 'coordinates-data'
? await plugin.builders.data.rawData({ data: params.coordinates.data, label: params.coordinatesLabel })
: await plugin.builders.data.download({ url: params.coordinates.url, isBinary: params.coordinates.isBinary, label: params.coordinatesLabel });
const provider = plugin.dataFormats.get(params.coordinates.format);
coords = await provider!.parse(plugin, data);
}
const trajectory = await plugin.build().toRoot()
.apply(TrajectoryFromModelAndCoordinates, {
modelRef: model.ref,
coordinatesRef: coords.ref
}, { dependsOn: [model.ref, coords.ref] })
.commit();
const preset = await plugin.builders.structure.hierarchy.applyPreset(trajectory, params.preset ?? 'default');
return { model, coords, preset };
}
handleResize() {
this.plugin.layout.events.updated.next(void 0);
}
}
export interface LoadStructureOptions {
representationParams?: StructureRepresentationPresetProvider.CommonParams
}
export interface VolumeIsovalueInfo {
type: 'absolute' | 'relative',
value: number,
color: Color,
alpha?: number,
volumeIndex?: number
}
export interface LoadTrajectoryParams {
model: { kind: 'model-url', url: string, format?: BuiltInTrajectoryFormat /* mmcif */, isBinary?: boolean }
| { kind: 'model-data', data: string | number[] | ArrayBuffer | Uint8Array, format?: BuiltInTrajectoryFormat /* mmcif */ }
| { kind: 'topology-url', url: string, format: BuildInStructureFormat, isBinary?: boolean }
| { kind: 'topology-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuildInStructureFormat },
modelLabel?: string,
coordinates: { kind: 'coordinates-url', url: string, format: BuildInStructureFormat, isBinary?: boolean }
| { kind: 'coordinates-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuildInStructureFormat },
coordinatesLabel?: string,
preset?: keyof PresetTrajectoryHierarchy
}
export const ViewerAutoPreset = StructureRepresentationPresetProvider({
id: 'preset-structure-representation-viewer-auto',
display: {
name: 'Automatic (w/ Annotation)', group: 'Annotation',
description: 'Show standard automatic representation but colored by quality assessment (if available in the model).'
},
isApplicable(a) {
return (
!!a.data.models.some(m => QualityAssessment.isApplicable(m, 'pLDDT')) ||
!!a.data.models.some(m => QualityAssessment.isApplicable(m, 'qmean'))
);
},
params: () => StructureRepresentationPresetProvider.CommonParams,
async apply(ref, params, plugin) {
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
const structure = structureCell?.obj?.data;
if (!structureCell || !structure) return {};
if (!!structure.models.some(m => QualityAssessment.isApplicable(m, 'pLDDT'))) {
return await QualityAssessmentPLDDTPreset.apply(ref, params, plugin);
} else if (!!structure.models.some(m => QualityAssessment.isApplicable(m, 'qmean'))) {
return await QualityAssessmentQmeanPreset.apply(ref, params, plugin);
} else {
return await PresetStructureRepresentations.auto.apply(ref, params, plugin);
}
}
});

View File

@@ -56,6 +56,8 @@
var pixelScale = getParam('pixel-scale', '[^&]+').trim();
var pickScale = getParam('pick-scale', '[^&]+').trim();
var pickPadding = getParam('pick-padding', '[^&]+').trim();
var disableWboit = getParam('disable-wboit', '[^&]+').trim() === '1';
var preferWebgl1 = getParam('prefer-webgl1', '[^&]+').trim() === '1' || void 0;
molstar.Viewer.create('app', {
layoutShowControls: !hideControls,
@@ -69,6 +71,8 @@
pixelScale: parseFloat(pixelScale) || 1,
pickScale: parseFloat(pickScale) || 0.25,
pickPadding: isNaN(parseFloat(pickPadding)) ? 1 : parseFloat(pickPadding),
enableWboit: disableWboit ? true : void 0, // use default value if disable-wboit is not set
preferWebgl1: preferWebgl1,
}).then(viewer => {
var snapshotId = getParam('snapshot-id', '[^&]+').trim();
if (snapshotId) viewer.setRemoteSnapshot(snapshotId);

View File

@@ -1,482 +1,12 @@
/**
* 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>
*/
import { ANVILMembraneOrientation } from '../../extensions/anvil/behavior';
import { CellPack } from '../../extensions/cellpack';
import { DnatcoConfalPyramids } from '../../extensions/dnatco';
import { G3DFormat, G3dProvider } from '../../extensions/g3d/format';
import { GeometryExport } from '../../extensions/geo-export';
import { MAQualityAssessment } from '../../extensions/model-archive/quality-assessment/behavior';
import { QualityAssessmentPLDDTPreset, QualityAssessmentQmeanPreset } from '../../extensions/model-archive/quality-assessment/behavior';
import { QualityAssessment } from '../../extensions/model-archive/quality-assessment/prop';
import { Mp4Export } from '../../extensions/mp4-export';
import { PDBeStructureQualityReport } from '../../extensions/pdbe';
import { RCSBAssemblySymmetry, RCSBValidationReport } from '../../extensions/rcsb';
import { DownloadStructure, PdbDownloadProvider } from '../../mol-plugin-state/actions/structure';
import { DownloadDensity } from '../../mol-plugin-state/actions/volume';
import { PresetTrajectoryHierarchy } from '../../mol-plugin-state/builder/structure/hierarchy-preset';
import { PresetStructureRepresentations, StructureRepresentationPresetProvider } from '../../mol-plugin-state/builder/structure/representation-preset';
import { DataFormatProvider } from '../../mol-plugin-state/formats/provider';
import { BuildInStructureFormat } from '../../mol-plugin-state/formats/structure';
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
import { BuildInVolumeFormat } from '../../mol-plugin-state/formats/volume';
import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers/volume-representation-params';
import { PluginStateObject } from '../../mol-plugin-state/objects';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { TrajectoryFromModelAndCoordinates } from '../../mol-plugin-state/transforms/model';
import { createPluginUI } from '../../mol-plugin-ui';
import { PluginUIContext } from '../../mol-plugin-ui/context';
import { DefaultPluginUISpec, PluginUISpec } from '../../mol-plugin-ui/spec';
import { PluginCommands } from '../../mol-plugin/commands';
import { PluginConfig } from '../../mol-plugin/config';
import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout';
import { PluginSpec } from '../../mol-plugin/spec';
import { PluginState } from '../../mol-plugin/state';
import { StateObjectRef, StateObjectSelector } from '../../mol-state';
import { Asset } from '../../mol-util/assets';
import { Color } from '../../mol-util/color';
import '../../mol-util/polyfill';
import { ObjectKeys } from '../../mol-util/type-helpers';
import './embedded.html';
import './favicon.ico';
import './index.html';
require('mol-plugin-ui/skin/light.scss');
export { PLUGIN_VERSION as version } from '../../mol-plugin/version';
export { setDebugMode, setProductionMode } from '../../mol-util/debug';
const CustomFormats = [
['g3d', G3dProvider] as const
];
const Extensions = {
'cellpack': PluginSpec.Behavior(CellPack),
'dnatco-confal-pyramids': PluginSpec.Behavior(DnatcoConfalPyramids),
'pdbe-structure-quality-report': PluginSpec.Behavior(PDBeStructureQualityReport),
'rcsb-assembly-symmetry': PluginSpec.Behavior(RCSBAssemblySymmetry),
'rcsb-validation-report': PluginSpec.Behavior(RCSBValidationReport),
'anvil-membrane-orientation': PluginSpec.Behavior(ANVILMembraneOrientation),
'g3d': PluginSpec.Behavior(G3DFormat),
'mp4-export': PluginSpec.Behavior(Mp4Export),
'geo-export': PluginSpec.Behavior(GeometryExport),
'ma-quality-assessment': PluginSpec.Behavior(MAQualityAssessment),
};
const DefaultViewerOptions = {
customFormats: CustomFormats as [string, DataFormatProvider][],
extensions: ObjectKeys(Extensions),
layoutIsExpanded: true,
layoutShowControls: true,
layoutShowRemoteState: true,
layoutControlsDisplay: 'reactive' as PluginLayoutControlsDisplay,
layoutShowSequence: true,
layoutShowLog: true,
layoutShowLeftPanel: true,
collapseLeftPanel: false,
collapseRightPanel: false,
disableAntialiasing: PluginConfig.General.DisableAntialiasing.defaultValue,
pixelScale: PluginConfig.General.PixelScale.defaultValue,
pickScale: PluginConfig.General.PickScale.defaultValue,
pickPadding: PluginConfig.General.PickPadding.defaultValue,
enableWboit: PluginConfig.General.EnableWboit.defaultValue,
viewportShowExpand: PluginConfig.Viewport.ShowExpand.defaultValue,
viewportShowControls: PluginConfig.Viewport.ShowControls.defaultValue,
viewportShowSettings: PluginConfig.Viewport.ShowSettings.defaultValue,
viewportShowSelectionMode: PluginConfig.Viewport.ShowSelectionMode.defaultValue,
viewportShowAnimation: PluginConfig.Viewport.ShowAnimation.defaultValue,
pluginStateServer: PluginConfig.State.DefaultServer.defaultValue,
volumeStreamingServer: PluginConfig.VolumeStreaming.DefaultServer.defaultValue,
volumeStreamingDisabled: !PluginConfig.VolumeStreaming.Enabled.defaultValue,
pdbProvider: PluginConfig.Download.DefaultPdbProvider.defaultValue,
emdbProvider: PluginConfig.Download.DefaultEmdbProvider.defaultValue,
};
type ViewerOptions = typeof DefaultViewerOptions;
export class Viewer {
constructor(public plugin: PluginUIContext) {
}
static async create(elementOrId: string | HTMLElement, options: Partial<ViewerOptions> = {}) {
const o = { ...DefaultViewerOptions, ...options };
const defaultSpec = DefaultPluginUISpec();
const spec: PluginUISpec = {
actions: defaultSpec.actions,
behaviors: [
...defaultSpec.behaviors,
...o.extensions.map(e => Extensions[e]),
],
animations: [...defaultSpec.animations || []],
customParamEditors: defaultSpec.customParamEditors,
customFormats: o?.customFormats,
layout: {
initial: {
isExpanded: o.layoutIsExpanded,
showControls: o.layoutShowControls,
controlsDisplay: o.layoutControlsDisplay,
regionState: {
bottom: 'full',
left: o.collapseLeftPanel ? 'collapsed' : 'full',
right: o.collapseRightPanel ? 'hidden' : 'full',
top: 'full',
}
},
},
components: {
...defaultSpec.components,
controls: {
...defaultSpec.components?.controls,
top: o.layoutShowSequence ? undefined : 'none',
bottom: o.layoutShowLog ? undefined : 'none',
left: o.layoutShowLeftPanel ? undefined : 'none',
},
remoteState: o.layoutShowRemoteState ? 'default' : 'none',
},
config: [
[PluginConfig.General.DisableAntialiasing, o.disableAntialiasing],
[PluginConfig.General.PixelScale, o.pixelScale],
[PluginConfig.General.PickScale, o.pickScale],
[PluginConfig.General.PickPadding, o.pickPadding],
[PluginConfig.General.EnableWboit, o.enableWboit],
[PluginConfig.Viewport.ShowExpand, o.viewportShowExpand],
[PluginConfig.Viewport.ShowControls, o.viewportShowControls],
[PluginConfig.Viewport.ShowSettings, o.viewportShowSettings],
[PluginConfig.Viewport.ShowSelectionMode, o.viewportShowSelectionMode],
[PluginConfig.Viewport.ShowAnimation, o.viewportShowAnimation],
[PluginConfig.State.DefaultServer, o.pluginStateServer],
[PluginConfig.State.CurrentServer, o.pluginStateServer],
[PluginConfig.VolumeStreaming.DefaultServer, o.volumeStreamingServer],
[PluginConfig.VolumeStreaming.Enabled, !o.volumeStreamingDisabled],
[PluginConfig.Download.DefaultPdbProvider, o.pdbProvider],
[PluginConfig.Download.DefaultEmdbProvider, o.emdbProvider],
[PluginConfig.Structure.DefaultRepresentationPreset, ViewerAutoPreset.id],
]
};
const element = typeof elementOrId === 'string'
? document.getElementById(elementOrId)
: elementOrId;
if (!element) throw new Error(`Could not get element with id '${elementOrId}'`);
const plugin = await createPluginUI(element, spec, {
onBeforeUIRender: plugin => {
// the preset needs to be added before the UI renders otherwise
// "Download Structure" wont be able to pick it up
plugin.builders.structure.representation.registerPreset(ViewerAutoPreset);
}
});
return new Viewer(plugin);
}
setRemoteSnapshot(id: string) {
const url = `${this.plugin.config.get(PluginConfig.State.CurrentServer)}/get/${id}`;
return PluginCommands.State.Snapshots.Fetch(this.plugin, { url });
}
loadSnapshotFromUrl(url: string, type: PluginState.SnapshotType) {
return PluginCommands.State.Snapshots.OpenUrl(this.plugin, { url, type });
}
loadStructureFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false, options?: LoadStructureOptions) {
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
source: {
name: 'url',
params: {
url: Asset.Url(url),
format: format as any,
isBinary,
options: { ...params.source.params.options, representationParams: options?.representationParams as any },
}
}
}));
}
async loadAllModelsOrAssemblyFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false, options?: LoadStructureOptions) {
const plugin = this.plugin;
const data = await plugin.builders.data.download({ url, isBinary }, { state: { isGhost: true } });
const trajectory = await plugin.builders.structure.parseTrajectory(data, format);
await this.plugin.builders.structure.hierarchy.applyPreset(trajectory, 'all-models', { useDefaultIfSingleModel: true, representationPresetParams: options?.representationParams });
}
async loadStructureFromData(data: string | number[], format: BuiltInTrajectoryFormat, options?: { dataLabel?: string }) {
const _data = await this.plugin.builders.data.rawData({ data, label: options?.dataLabel });
const trajectory = await this.plugin.builders.structure.parseTrajectory(_data, format);
await this.plugin.builders.structure.hierarchy.applyPreset(trajectory, 'default');
}
loadPdb(pdb: string, options?: LoadStructureOptions) {
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
const provider = this.plugin.config.get(PluginConfig.Download.DefaultPdbProvider)!;
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
source: {
name: 'pdb' as const,
params: {
provider: {
id: pdb,
server: {
name: provider,
params: PdbDownloadProvider[provider].defaultValue as any
}
},
options: { ...params.source.params.options, representationParams: options?.representationParams as any },
}
}
}));
}
loadPdbDev(pdbDev: string) {
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
source: {
name: 'pdb-dev' as const,
params: {
provider: {
id: pdbDev,
encoding: 'bcif',
},
options: params.source.params.options,
}
}
}));
}
loadEmdb(emdb: string, options?: { detail?: number }) {
const provider = this.plugin.config.get(PluginConfig.Download.DefaultEmdbProvider)!;
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadDensity, {
source: {
name: 'pdb-emd-ds' as const,
params: {
provider: {
id: emdb,
server: provider,
},
detail: options?.detail ?? 3,
}
}
}));
}
loadAlphaFoldDb(afdb: string) {
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
source: {
name: 'alphafolddb' as const,
params: {
id: afdb,
options: {
...params.source.params.options,
representation: 'preset-structure-representation-ma-quality-assessment-plddt'
},
}
}
}));
}
loadModelArchive(id: string) {
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
source: {
name: 'modelarchive' as const,
params: {
id,
options: params.source.params.options,
}
}
}));
}
/**
* @example Load X-ray density from volume server
viewer.loadVolumeFromUrl({
url: 'https://www.ebi.ac.uk/pdbe/densities/x-ray/1tqn/cell?detail=3',
format: 'dscif',
isBinary: true
}, [{
type: 'relative',
value: 1.5,
color: 0x3362B2
}, {
type: 'relative',
value: 3,
color: 0x33BB33,
volumeIndex: 1
}, {
type: 'relative',
value: -3,
color: 0xBB3333,
volumeIndex: 1
}], {
entryId: ['2FO-FC', 'FO-FC'],
isLazy: true
});
* *********************
* @example Load EM density from volume server
viewer.loadVolumeFromUrl({
url: 'https://maps.rcsb.org/em/emd-30210/cell?detail=6',
format: 'dscif',
isBinary: true
}, [{
type: 'relative',
value: 1,
color: 0x3377aa
}], {
entryId: 'EMD-30210',
isLazy: true
});
*/
async loadVolumeFromUrl({ url, format, isBinary }: { url: string, format: BuildInVolumeFormat, isBinary: boolean }, isovalues: VolumeIsovalueInfo[], options?: { entryId?: string | string[], isLazy?: boolean }) {
const plugin = this.plugin;
if (!plugin.dataFormats.get(format)) {
throw new Error(`Unknown density format: ${format}`);
}
if (options?.isLazy) {
const update = this.plugin.build();
update.toRoot().apply(StateTransforms.Data.LazyVolume, {
url,
format,
entryId: options?.entryId,
isBinary,
isovalues: isovalues.map(v => ({ alpha: 1, volumeIndex: 0, ...v }))
});
return update.commit();
}
return plugin.dataTransaction(async () => {
const data = await plugin.builders.data.download({ url, isBinary }, { state: { isGhost: true } });
const parsed = await plugin.dataFormats.get(format)!.parse(plugin, data, { entryId: options?.entryId });
const firstVolume = (parsed.volume || parsed.volumes[0]) as StateObjectSelector<PluginStateObject.Volume.Data>;
if (!firstVolume?.isOk) throw new Error('Failed to parse any volume.');
const repr = plugin.build();
for (const iso of isovalues) {
repr
.to(parsed.volumes?.[iso.volumeIndex ?? 0] ?? parsed.volume)
.apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(this.plugin, firstVolume.data!, {
type: 'isosurface',
typeParams: { alpha: iso.alpha ?? 1, isoValue: iso.type === 'absolute' ? { kind: 'absolute', absoluteValue: iso.value } : { kind: 'relative', relativeValue: iso.value } },
color: 'uniform',
colorParams: { value: iso.color }
}));
}
await repr.commit();
});
}
/**
* @example
* viewer.loadTrajectory({
* model: { kind: 'model-url', url: 'villin.gro', format: 'gro' },
* coordinates: { kind: 'coordinates-url', url: 'villin.xtc', format: 'xtc', isBinary: true },
* preset: 'all-models' // or 'default'
* });
*/
async loadTrajectory(params: LoadTrajectoryParams) {
const plugin = this.plugin;
let model: StateObjectSelector, coords: StateObjectSelector;
if (params.model.kind === 'model-data' || params.model.kind === 'model-url') {
const data = params.model.kind === 'model-data'
? await plugin.builders.data.rawData({ data: params.model.data, label: params.modelLabel })
: await plugin.builders.data.download({ url: params.model.url, isBinary: params.model.isBinary, label: params.modelLabel });
const trajectory = await plugin.builders.structure.parseTrajectory(data, params.model.format ?? 'mmcif');
model = await plugin.builders.structure.createModel(trajectory);
} else {
const data = params.model.kind === 'topology-data'
? await plugin.builders.data.rawData({ data: params.model.data, label: params.modelLabel })
: await plugin.builders.data.download({ url: params.model.url, isBinary: params.model.isBinary, label: params.modelLabel });
const provider = plugin.dataFormats.get(params.model.format);
model = await provider!.parse(plugin, data);
}
{
const data = params.coordinates.kind === 'coordinates-data'
? await plugin.builders.data.rawData({ data: params.coordinates.data, label: params.coordinatesLabel })
: await plugin.builders.data.download({ url: params.coordinates.url, isBinary: params.coordinates.isBinary, label: params.coordinatesLabel });
const provider = plugin.dataFormats.get(params.coordinates.format);
coords = await provider!.parse(plugin, data);
}
const trajectory = await plugin.build().toRoot()
.apply(TrajectoryFromModelAndCoordinates, {
modelRef: model.ref,
coordinatesRef: coords.ref
}, { dependsOn: [model.ref, coords.ref] })
.commit();
const preset = await plugin.builders.structure.hierarchy.applyPreset(trajectory, params.preset ?? 'default');
return { model, coords, preset };
}
handleResize() {
this.plugin.layout.events.updated.next(void 0);
}
}
export interface LoadStructureOptions {
representationParams?: StructureRepresentationPresetProvider.CommonParams
}
export interface VolumeIsovalueInfo {
type: 'absolute' | 'relative',
value: number,
color: Color,
alpha?: number,
volumeIndex?: number
}
export interface LoadTrajectoryParams {
model: { kind: 'model-url', url: string, format?: BuiltInTrajectoryFormat /* mmcif */, isBinary?: boolean }
| { kind: 'model-data', data: string | number[] | ArrayBuffer | Uint8Array, format?: BuiltInTrajectoryFormat /* mmcif */ }
| { kind: 'topology-url', url: string, format: BuildInStructureFormat, isBinary?: boolean }
| { kind: 'topology-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuildInStructureFormat },
modelLabel?: string,
coordinates: { kind: 'coordinates-url', url: string, format: BuildInStructureFormat, isBinary?: boolean }
| { kind: 'coordinates-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuildInStructureFormat },
coordinatesLabel?: string,
preset?: keyof PresetTrajectoryHierarchy
}
export const ViewerAutoPreset = StructureRepresentationPresetProvider({
id: 'preset-structure-representation-viewer-auto',
display: {
name: 'Automatic (w/ Annotation)', group: 'Annotation',
description: 'Show standard automatic representation but colored by quality assessment (if available in the model).'
},
isApplicable(a) {
return (
!!a.data.models.some(m => QualityAssessment.isApplicable(m, 'pLDDT')) ||
!!a.data.models.some(m => QualityAssessment.isApplicable(m, 'qmean'))
);
},
params: () => StructureRepresentationPresetProvider.CommonParams,
async apply(ref, params, plugin) {
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
const structure = structureCell?.obj?.data;
if (!structureCell || !structure) return {};
if (!!structure.models.some(m => QualityAssessment.isApplicable(m, 'pLDDT'))) {
return await QualityAssessmentPLDDTPreset.apply(ref, params, plugin);
} else if (!!structure.models.some(m => QualityAssessment.isApplicable(m, 'qmean'))) {
return await QualityAssessmentQmeanPreset.apply(ref, params, plugin);
} else {
return await PresetStructureRepresentations.auto.apply(ref, params, plugin);
}
}
});
export * from './app';

View File

@@ -97,7 +97,7 @@ export class AlphaOrbitalsExample {
}
readonly params = new BehaviorSubject<ParamDefinition.For<Params>>({} as any);
readonly state = new BehaviorSubject<Params>({ show: { name: 'orbital', params: { index: 32 } }, isoValue: 1, gpuSurface: false });
readonly state = new BehaviorSubject<Params>({ show: { name: 'orbital', params: { index: 32 } }, isoValue: 1, gpuSurface: true });
private selectors?: Selectors = void 0;
private basis?: StateObjectSelector<BasisAndOrbitals> = void 0;

View File

@@ -74,12 +74,20 @@ class BasicWrapper {
toggleSpin() {
if (!this.plugin.canvas3d) return;
const trackball = this.plugin.canvas3d.props.trackball;
PluginCommands.Canvas3D.SetSettings(this.plugin, {
settings: props => {
props.trackball.spin = !props.trackball.spin;
settings: {
trackball: {
...trackball,
animate: trackball.animate.name === 'spin'
? { name: 'off', params: {} }
: { name: 'spin', params: { speed: 1 } }
}
}
});
if (!this.plugin.canvas3d.props.trackball.spin) PluginCommands.Camera.Reset(this.plugin, {});
if (this.plugin.canvas3d.props.trackball.animate.name !== 'spin') {
PluginCommands.Camera.Reset(this.plugin, {});
}
}
private animateModelIndexTargetFps() {

View File

@@ -256,7 +256,16 @@ class MolStarProteopediaWrapper {
toggleSpin() {
if (!this.plugin.canvas3d) return;
const trackball = this.plugin.canvas3d.props.trackball;
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
PluginCommands.Canvas3D.SetSettings(this.plugin, {
settings: {
trackball: {
...trackball,
animate: trackball.animate.name === 'spin'
? { name: 'off', params: {} }
: { name: 'spin', params: { speed: 1 } }
}
}
});
}
viewport = {

View File

@@ -43,9 +43,9 @@ export type BilayerPlanesProps = PD.Values<BilayerPlanesParams>
const BilayerRimsParams = {
...Lines.Params,
...SharedParams,
lineSizeAttenuation: PD.Boolean(true),
linesSize: PD.Numeric(0.3, { min: 0.01, max: 50, step: 0.01 }),
dashedLines: PD.Boolean(true),
lineSizeAttenuation: PD.Boolean(false),
linesSize: PD.Numeric(0.5, { min: 0.01, max: 50, step: 0.01 }),
dashedLines: PD.Boolean(false),
};
export type BilayerRimsParams = typeof BilayerRimsParams
export type BilayerRimsProps = PD.Values<BilayerRimsParams>

View File

@@ -25,7 +25,7 @@ import { sizeDataFactor } from '../../mol-geo/geometry/size-data';
import { Vec3 } from '../../mol-math/linear-algebra';
import { RuntimeContext } from '../../mol-task';
import { Color } from '../../mol-util/color/color';
import { decodeFloatRGB } from '../../mol-util/float-packing';
import { unpackRGBToInt } from '../../mol-util/number-packing';
import { RenderObjectExporter, RenderObjectExportData } from './render-object-exporter';
import { readAlphaTexture, readTexture } from '../../mol-gl/compute/util';
@@ -65,7 +65,7 @@ export abstract class MeshExporter<D extends RenderObjectExportData> implements
const r = tSize.array[i * 3];
const g = tSize.array[i * 3 + 1];
const b = tSize.array[i * 3 + 2];
return decodeFloatRGB(r, g, b) / sizeDataFactor;
return unpackRGBToInt(r, g, b) / sizeDataFactor;
}
private static getSize(values: BaseValues & SizeValues, instanceIndex: number, group: number): number {
@@ -95,9 +95,9 @@ export abstract class MeshExporter<D extends RenderObjectExportData> implements
const g = groups[i4 + 1];
const b = groups[i4 + 2];
if (groups instanceof Float32Array) {
return decodeFloatRGB(r * 255 + 0.5, g * 255 + 0.5, b * 255 + 0.5);
return unpackRGBToInt(r * 255 + 0.5, g * 255 + 0.5, b * 255 + 0.5);
}
return decodeFloatRGB(r, g, b);
return unpackRGBToInt(r, g, b);
}
protected static getInterpolatedColors(webgl: WebGLContext, input: { vertices: Float32Array, vertexCount: number, values: BaseValues, stride: 3 | 4, colorType: 'volume' | 'volumeInstance' }) {

View File

@@ -26,7 +26,7 @@ export const MAQualityAssessment = PluginBehavior.create<{ autoAttach: boolean,
description: 'Data included in Model Archive files.'
},
ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean, showTooltip: boolean }> {
private provider = QualityAssessmentProvider
private provider = QualityAssessmentProvider;
private labelProvider = {
label: (loci: Loci): string | undefined => {
@@ -36,7 +36,7 @@ export const MAQualityAssessment = PluginBehavior.create<{ autoAttach: boolean,
qmeanLabel(loci),
].filter(l => !!l).join('</br>');
}
}
};
register(): void {
DefaultQueryRuntimeTable.addCustomProp(this.provider.descriptor);

View File

@@ -0,0 +1,87 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { utf8ByteCount, utf8Write } from '../../mol-io/common/utf8';
import { to_mmCIF, Unit } from '../../mol-model/structure';
import { PluginContext } from '../../mol-plugin/context';
import { Task } from '../../mol-task';
import { getFormattedTime } from '../../mol-util/date';
import { download } from '../../mol-util/download';
import { zip } from '../../mol-util/zip/zip';
export async function exportHierarchy(plugin: PluginContext, options?: { format?: 'cif' | 'bcif' }) {
try {
await plugin.runTask(_exportHierarchy(plugin, options), { useOverlay: true });
} catch (e) {
console.error(e);
plugin.log.error(`Model export failed. See console for details.`);
}
}
function _exportHierarchy(plugin: PluginContext, options?: { format?: 'cif' | 'bcif' }) {
return Task.create('Export', async ctx => {
await ctx.update({ message: 'Exporting...', isIndeterminate: true, canAbort: false });
const format = options?.format ?? 'cif';
const { structures } = plugin.managers.structure.hierarchy.current;
const files: [name: string, data: string | Uint8Array][] = [];
const entryMap = new Map<string, number>();
for (const _s of structures) {
const s = _s.transform?.cell.obj?.data ?? _s.cell.obj?.data;
if (!s) continue;
if (s.models.length > 1) {
plugin.log.warn(`[Export] Skipping ${_s.cell.obj?.label}: Multimodel exports not supported.`);
continue;
}
if (s.units.some(u => !Unit.isAtomic(u))) {
plugin.log.warn(`[Export] Skipping ${_s.cell.obj?.label}: Non-atomic model exports not supported.`);
continue;
}
const name = entryMap.has(s.model.entryId)
? `${s.model.entryId}_${entryMap.get(s.model.entryId)! + 1}.${format}`
: `${s.model.entryId}.${format}`;
entryMap.set(s.model.entryId, (entryMap.get(s.model.entryId) ?? 0) + 1);
await ctx.update({ message: `Exporting ${s.model.entryId}...`, isIndeterminate: true, canAbort: false });
if (s.elementCount > 100000) {
// Give UI chance to update, only needed for larger structures.
await new Promise(res => setTimeout(res, 50));
}
try {
files.push([name, to_mmCIF(s.model.entryId, s, format === 'bcif', { copyAllCategories: true })]);
} catch (e) {
if (format === 'cif' && s.elementCount > 2000000) {
plugin.log.warn(`[Export] The structure might be too big to be exported as Text CIF, consider using the BinaryCIF format instead.`);
}
throw e;
}
}
if (files.length === 1) {
download(new Blob([files[0][1]]), files[0][0]);
} else if (files.length > 1) {
const zipData: Record<string, Uint8Array> = {};
for (const [fn, data] of files) {
if (data instanceof Uint8Array) {
zipData[fn] = data;
} else {
const bytes = new Uint8Array(utf8ByteCount(data));
utf8Write(bytes, 0, data);
zipData[fn] = bytes;
}
}
await ctx.update({ message: `Compressing Data...`, isIndeterminate: true, canAbort: false });
const buffer = await zip(ctx, zipData);
download(new Blob([new Uint8Array(buffer, 0, buffer.byteLength)]), `structures_${getFormattedTime()}.zip`);
}
plugin.log.info(`[Export] Done.`);
});
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { PluginBehavior } from '../../mol-plugin/behavior/behavior';
import { ModelExportUI } from './ui';
export const ModelExport = PluginBehavior.create<{}>({
name: 'extension-model-export',
category: 'misc',
display: {
name: 'Model Export'
},
ctor: class extends PluginBehavior.Handler<{}> {
register(): void {
this.ctx.customStructureControls.set('model-export', ModelExportUI as any);
}
update() {
return false;
}
unregister() {
this.ctx.customStructureControls.delete('model-export');
}
},
params: () => ({})
});

View File

@@ -0,0 +1,69 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { useState } from 'react';
import { CollapsableControls, CollapsableState } from '../../mol-plugin-ui/base';
import { Button } from '../../mol-plugin-ui/controls/common';
import { GetAppSvg } from '../../mol-plugin-ui/controls/icons';
import { ParameterControls } from '../../mol-plugin-ui/controls/parameters';
import { useBehavior } from '../../mol-plugin-ui/hooks/use-behavior';
import { PluginContext } from '../../mol-plugin/context';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { exportHierarchy } from './export';
export class ModelExportUI extends CollapsableControls<{}, {}> {
protected defaultState(): CollapsableState {
return {
header: 'Export Models',
isCollapsed: true,
brand: { accent: 'cyan', svg: GetAppSvg }
};
}
protected renderControls(): JSX.Element | null {
return <ExportControls plugin={this.plugin} />;
}
}
const Params = {
format: PD.Select<'cif' | 'bcif'>('cif', [['cif', 'mmCIF'], ['bcif', 'Binary mmCIF']])
};
const DefaultParams = PD.getDefaultValues(Params);
function ExportControls({ plugin }: { plugin: PluginContext }) {
const [params, setParams] = useState(DefaultParams);
const [exporting, setExporting] = useState(false);
useBehavior(plugin.managers.structure.hierarchy.behaviors.selection); // triggers UI update
const isBusy = useBehavior(plugin.behaviors.state.isBusy);
const hierarchy = plugin.managers.structure.hierarchy.current;
let label: string = 'Nothing to Export';
if (hierarchy.structures.length === 1) {
label = 'Export';
} if (hierarchy.structures.length > 1) {
label = 'Export (as ZIP)';
}
const onExport = async () => {
setExporting(true);
try {
await exportHierarchy(plugin, { format: params.format });
} finally {
setExporting(false);
}
};
return <>
<ParameterControls params={Params} values={params} onChangeValues={setParams} isDisabled={isBusy || exporting} />
<Button
onClick={onExport}
style={{ marginTop: 1 }}
disabled={isBusy || hierarchy.structures.length === 0 || exporting}
commit={hierarchy.structures.length ? 'on' : 'off'}
>
{label}
</Button>
</>;
}

View File

@@ -73,7 +73,7 @@ export async function encodeMp4Animation<A extends PluginStateAnimation>(plugin:
await plugin.managers.animation.play(params.animation.definition, params.animation.params);
stoppedAnimation = false;
for (let i = 0; i <= N; i++) {
await loop.tick(i * dt, { isSynchronous: true, manualDraw: true });
await loop.tick(i * dt, { isSynchronous: true, animation: { currentFrame: i, frameCount: N }, manualDraw: true });
const image = params.pass.getImageData(width, height, normalizedViewport);
encoder.addFrameRgba(image.data);

View File

@@ -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 2021-11-25T14:34:23-08:00
// Generated on 2022-02-06T15:40:15-08:00
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
@@ -11113,11 +11113,11 @@ export type RcsbUniprotAlignments = {
export type RcsbUniprotAlignmentsCoreEntityAlignments = {
/** Aligned region */
readonly aligned_regions?: Maybe<ReadonlyArray<Maybe<CoreEntityAlignmentsAlignedRegions>>>;
readonly aligned_regions: ReadonlyArray<Maybe<CoreEntityAlignmentsAlignedRegions>>;
/** core_entity identifiers */
readonly core_entity_identifiers?: Maybe<CoreEntityAlignmentsCoreEntityIdentifiers>;
/** Alignment scores */
readonly scores?: Maybe<CoreEntityAlignmentsScores>;
readonly scores: CoreEntityAlignmentsScores;
};
export type RcsbUniprotAnnotation = {
@@ -13145,4 +13145,4 @@ export type AssemblySymmetryQueryVariables = Exact<{
}>;
export type AssemblySymmetryQuery = { readonly assembly?: { readonly rcsb_struct_symmetry?: ReadonlyArray<{ readonly kind: string, readonly oligomeric_state: string, readonly stoichiometry: ReadonlyArray<string | null | undefined>, readonly symbol: string, readonly type: string, readonly clusters: ReadonlyArray<{ readonly avg_rmsd?: number | null | undefined, readonly members: ReadonlyArray<{ readonly asym_id: string, readonly pdbx_struct_oper_list_ids?: ReadonlyArray<string | null | undefined> | null | undefined } | null | undefined> } | null | undefined>, readonly rotation_axes?: ReadonlyArray<{ readonly order?: number | null | undefined, readonly start: ReadonlyArray<number | null | undefined>, readonly end: ReadonlyArray<number | null | undefined> } | null | undefined> | null | undefined } | null | undefined> | null | undefined } | null | undefined };
export type AssemblySymmetryQuery = { readonly assembly?: { readonly rcsb_struct_symmetry?: ReadonlyArray<{ readonly kind: string, readonly oligomeric_state: string, readonly stoichiometry: ReadonlyArray<string | null>, readonly symbol: string, readonly type: string, readonly clusters: ReadonlyArray<{ readonly avg_rmsd?: number | null, readonly members: ReadonlyArray<{ readonly asym_id: string, readonly pdbx_struct_oper_list_ids?: ReadonlyArray<string | null> | null } | null> } | null>, readonly rotation_axes?: ReadonlyArray<{ readonly order?: number | null, readonly start: ReadonlyArray<number | null>, readonly end: ReadonlyArray<number | null> } | null> | null } | null> | null } | null };

View File

@@ -39,6 +39,7 @@ import { Helper } from './helper/helper';
import { Passes } from './passes/passes';
import { shallowEqual } from '../mol-util';
import { MarkingParams } from './passes/marking';
import { GraphicsRenderVariantsBlended, GraphicsRenderVariantsWboit } from '../mol-gl/webgl/render-item';
export const Canvas3DParams = {
camera: PD.Group({
@@ -222,7 +223,7 @@ interface Canvas3D {
clear(): void
syncVisibility(): void
requestDraw(force?: boolean): void
requestDraw(): void
/** Reset the timers, used by "animate" */
resetTime(t: number): void
@@ -296,7 +297,7 @@ namespace Canvas3D {
let height = 128;
updateViewport();
const scene = Scene.create(webgl);
const scene = Scene.create(webgl, passes.draw.wboitEnabled ? GraphicsRenderVariantsWboit : GraphicsRenderVariantsBlended);
const camera = new Camera({
position: Vec3.create(0, 0, 100),
@@ -354,17 +355,24 @@ namespace Canvas3D {
changed = helper.camera.mark(loci, action) || changed;
reprRenderObjects.forEach((_, _repr) => { changed = _repr.mark(loci, action) || changed; });
}
if (changed && !noDraw) {
scene.update(void 0, true);
helper.handle.scene.update(void 0, true);
helper.camera.scene.update(void 0, true);
const prevPickDirty = pickHelper.dirty;
draw(true);
pickHelper.dirty = prevPickDirty; // marking does not change picking buffers
if (changed) {
if (noDraw) {
// Even with `noDraw` make sure changes will be rendered.
// Note that with this calling mark (with or without `noDraw`) multiple times
// during a JS event loop iteration will only result in a single render call.
forceNextRender = true;
} else {
scene.update(void 0, true);
helper.handle.scene.update(void 0, true);
helper.camera.scene.update(void 0, true);
const prevPickDirty = pickHelper.dirty;
draw({ force: true, allowMulti: true });
pickHelper.dirty = prevPickDirty; // marking does not change picking buffers
}
}
}
function render(force: boolean) {
function render(force: boolean, allowMulti: boolean) {
if (webgl.isContextLost) return false;
let resized = false;
@@ -394,14 +402,12 @@ namespace Canvas3D {
cam = stereoCamera;
}
const ctx = { renderer, camera: cam, scene, helper };
if (MultiSamplePass.isEnabled(p.multiSample)) {
if (!cameraChanged) {
while (!multiSampleHelper.render(renderer, cam, scene, helper, true, p.transparentBackground, p));
} else {
multiSampleHelper.render(renderer, cam, scene, helper, true, p.transparentBackground, p);
}
const forceOn = !cameraChanged && allowMulti && !controls.isAnimating;
multiSampleHelper.render(ctx, p, true, forceOn);
} else {
passes.draw.render(renderer, cam, scene, helper, true, p.transparentBackground, p.postprocessing, p.marking);
passes.draw.render(ctx, p, true);
}
pickHelper.dirty = true;
didRender = true;
@@ -415,15 +421,15 @@ namespace Canvas3D {
let currentTime = 0;
let drawPaused = false;
function draw(force?: boolean) {
function draw(options?: { force?: boolean, allowMulti?: boolean }) {
if (drawPaused) return;
if (render(!!force) && notifyDidDraw) {
if (render(!!options?.force, !!options?.allowMulti) && notifyDidDraw) {
didDraw.next(now() - startTime as now.Timestamp);
}
}
function requestDraw(force?: boolean) {
forceNextRender = forceNextRender || !!force;
function requestDraw() {
forceNextRender = true;
}
let animationFrameHandle = 0;
@@ -437,7 +443,7 @@ namespace Canvas3D {
return;
}
draw(false);
draw();
if (!camera.transition.inTransition && !webgl.isContextLost) {
interactionHelper.tick(currentTime);
}
@@ -477,7 +483,7 @@ namespace Canvas3D {
resolveCameraReset();
if (forceDrawAfterAllCommited) {
if (helper.debug.isEnabled) helper.debug.update();
draw(true);
draw({ force: true });
forceDrawAfterAllCommited = false;
}
commited.next(now());
@@ -660,11 +666,11 @@ namespace Canvas3D {
const contextRestoredSub = contextRestored.subscribe(() => {
pickHelper.dirty = true;
draw(true);
draw({ force: true });
// Unclear why, but in Chrome with wboit enabled the first `draw` only clears
// the drawingBuffer. Note that in Firefox the drawingBuffer is preserved after
// context loss so it is unclear if it behaves the same.
draw(true);
draw({ force: true });
});
const resized = new BehaviorSubject<any>(0);
@@ -673,7 +679,7 @@ namespace Canvas3D {
passes.updateSize();
updateViewport();
syncViewport();
if (draw) requestDraw(true);
if (draw) requestDraw();
resized.next(+new Date());
}
@@ -698,7 +704,7 @@ namespace Canvas3D {
reprRenderObjects.clear();
scene.clear();
helper.debug.clear();
requestDraw(true);
requestDraw();
reprCount.next(reprRenderObjects.size);
},
syncVisibility: () => {
@@ -710,7 +716,7 @@ namespace Canvas3D {
if (scene.syncVisibility()) {
if (helper.debug.isEnabled) helper.debug.update();
}
requestDraw(true);
requestDraw();
},
requestDraw,
@@ -798,7 +804,7 @@ namespace Canvas3D {
}
if (!doNotRequestDraw) {
requestDraw(true);
requestDraw();
}
},
getImagePass: (props: Partial<ImageProps> = {}) => {

View File

@@ -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 Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
@@ -13,7 +13,7 @@ import { Viewport } from '../camera/util';
import { InputObserver, DragInput, WheelInput, PinchInput, ButtonsType, ModifiersKeys, GestureInput } from '../../mol-util/input/input-observer';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { Camera } from '../camera';
import { absMax } from '../../mol-math/misc';
import { absMax, degToRad } from '../../mol-math/misc';
import { Binding } from '../../mol-util/binding';
const B = ButtonsType;
@@ -40,8 +40,16 @@ export const TrackballControlsParams = {
zoomSpeed: PD.Numeric(7.0, { min: 1, max: 15, step: 1 }),
panSpeed: PD.Numeric(1.0, { min: 0.1, max: 5, step: 0.1 }),
spin: PD.Boolean(false, { description: 'Spin the 3D scene around the x-axis in view space' }),
spinSpeed: PD.Numeric(1, { min: -20, max: 20, step: 1 }),
animate: PD.MappedStatic('off', {
off: PD.EmptyGroup(),
spin: PD.Group({
speed: PD.Numeric(1, { min: -20, max: 20, step: 1 }),
}, { description: 'Spin the 3D scene around the x-axis in view space' }),
rock: PD.Group({
speed: PD.Numeric(0.3, { min: -5, max: 5, step: 0.1 }),
angle: PD.Numeric(10, { min: 0, max: 90, step: 1 }, { description: 'How many degrees to rotate in each direction.' }),
}, { description: 'Rock the 3D scene around the x-axis in view space' })
}),
staticMoving: PD.Boolean(true, { isHidden: true }),
dynamicDampingFactor: PD.Numeric(0.2, {}, { isHidden: true }),
@@ -72,7 +80,8 @@ export type TrackballControlsProps = PD.Values<typeof TrackballControlsParams>
export { TrackballControls };
interface TrackballControls {
viewport: Viewport
readonly viewport: Viewport
readonly isAnimating: boolean
readonly props: Readonly<TrackballControlsProps>
setProps: (props: Partial<TrackballControlsProps>) => void
@@ -144,6 +153,11 @@ namespace TrackballControls {
);
}
function getRotateFactor() {
const aspectRatio = input.width / input.height;
return p.rotateSpeed * input.pixelRatio * aspectRatio;
}
const rotAxis = Vec3();
const rotQuat = Quat();
const rotEyeDir = Vec3();
@@ -156,8 +170,7 @@ namespace TrackballControls {
const dy = _rotCurr[1] - _rotPrev[1];
Vec3.set(rotMoveDir, dx, dy, 0);
const aspectRatio = input.width / input.height;
const angle = Vec3.magnitude(rotMoveDir) * p.rotateSpeed * input.pixelRatio * aspectRatio;
const angle = Vec3.magnitude(rotMoveDir) * getRotateFactor();
if (angle) {
Vec3.sub(_eye, camera.position, camera.target);
@@ -306,7 +319,10 @@ namespace TrackballControls {
/** Update the object's position, direction and up vectors */
function update(t: number) {
if (lastUpdated === t) return;
if (p.spin && lastUpdated > 0) spin(t - lastUpdated);
if (lastUpdated > 0) {
if (p.animate.name === 'spin') spin(t - lastUpdated);
else if (p.animate.name === 'rock') rock(t - lastUpdated);
}
Vec3.sub(_eye, camera.position, camera.target);
@@ -345,6 +361,7 @@ namespace TrackballControls {
if (!isStart && !_isInteracting) return;
_isInteracting = true;
resetRock(); // start rocking from the center after interactions
const dragRotate = Binding.match(p.bindings.dragRotate, buttons, modifiers);
const dragRotateZ = Binding.match(p.bindings.dragRotateZ, buttons, modifiers);
@@ -434,11 +451,34 @@ namespace TrackballControls {
const _spinSpeed = Vec2.create(0.005, 0);
function spin(deltaT: number) {
if (p.spinSpeed === 0) return;
if (p.animate.name !== 'spin' || p.animate.params.speed === 0 || _isInteracting) return;
const frameSpeed = (p.spinSpeed || 0) / 1000;
const frameSpeed = p.animate.params.speed / 1000;
_spinSpeed[0] = 60 * Math.min(Math.abs(deltaT), 1000 / 8) / 1000 * frameSpeed;
if (!_isInteracting) Vec2.add(_rotCurr, _rotPrev, _spinSpeed);
Vec2.add(_rotCurr, _rotPrev, _spinSpeed);
}
let _rockPhase = 0;
const _rockSpeed = Vec2.create(0.005, 0);
function rock(deltaT: number) {
if (p.animate.name !== 'rock' || p.animate.params.speed === 0 || _isInteracting) return;
const dt = deltaT / 1000 * p.animate.params.speed;
const maxAngle = degToRad(p.animate.params.angle) / getRotateFactor();
const angleA = Math.sin(_rockPhase * Math.PI * 2) * maxAngle;
const angleB = Math.sin((_rockPhase + dt) * Math.PI * 2) * maxAngle;
_rockSpeed[0] = angleB - angleA;
Vec2.add(_rotCurr, _rotPrev, _rockSpeed);
_rockPhase += dt;
if (_rockPhase >= 1) {
_rockPhase = 0;
}
}
function resetRock() {
_rockPhase = 0;
}
function start(t: number) {
@@ -448,9 +488,13 @@ namespace TrackballControls {
return {
viewport,
get isAnimating() { return p.animate.name !== 'off'; },
get props() { return p as Readonly<TrackballControlsProps>; },
setProps: (props: Partial<TrackballControlsProps>) => {
if (props.animate?.name === 'rock' && p.animate.name !== 'rock') {
resetRock(); // start rocking from the center
}
Object.assign(p, props);
},

View File

@@ -18,6 +18,7 @@ import { TransformData } from '../../mol-geo/geometry/transform-data';
import { sphereVertexCount } from '../../mol-geo/primitive/sphere';
import { ValueCell } from '../../mol-util';
import { Geometry } from '../../mol-geo/geometry/geometry';
import { GraphicsRenderVariantsBlended } from '../../mol-gl/webgl/render-item';
export const DebugHelperParams = {
sceneBoundingSpheres: PD.Boolean(false, { description: 'Show full scene bounding spheres.' }),
@@ -41,7 +42,7 @@ export class BoundingSphereHelper {
private visibleSceneData: BoundingSphereData | undefined;
constructor(ctx: WebGLContext, parent: Scene, props: Partial<DebugHelperProps>) {
this.scene = Scene.create(ctx);
this.scene = Scene.create(ctx, GraphicsRenderVariantsBlended);
this.parent = parent;
this._props = { ...PD.getDefaultValues(DebugHelperParams), ...props };
}
@@ -160,5 +161,5 @@ const instanceMaterialId = getNextMaterialId();
function createBoundingSphereRenderObject(mesh: Mesh, color: Color, materialId: number, transform?: TransformData) {
const values = Mesh.Utils.createValuesSimple(mesh, { alpha: 0.1, doubleSided: false }, color, 1, transform);
return createRenderObject('mesh', values, { disposed: false, visible: true, alphaFactor: 1, pickable: false, colorOnly: false, opaque: false, writeDepth: false, noClip: false }, materialId);
return createRenderObject('mesh', values, { disposed: false, visible: true, alphaFactor: 1, pickable: false, colorOnly: false, opaque: false, writeDepth: false }, materialId);
}

View File

@@ -14,6 +14,7 @@ import { PickingId } from '../../mol-geo/geometry/picking';
import { GraphicsRenderObject } from '../../mol-gl/render-object';
import { Scene } from '../../mol-gl/scene';
import { WebGLContext } from '../../mol-gl/webgl/context';
import { GraphicsRenderVariantsBlended } from '../../mol-gl/webgl/render-item';
import { Sphere3D } from '../../mol-math/geometry';
import { Mat4, Vec3 } from '../../mol-math/linear-algebra';
import { DataLoci, EmptyLoci, Loci } from '../../mol-model/loci';
@@ -58,7 +59,7 @@ export class CameraHelper {
private renderObject: GraphicsRenderObject | undefined;
constructor(private webgl: WebGLContext, props: Partial<CameraHelperProps> = {}) {
this.scene = Scene.create(webgl);
this.scene = Scene.create(webgl, GraphicsRenderVariantsBlended);
this.camera = new Camera();
Vec3.set(this.camera.up, 0, 1, 0);
@@ -75,7 +76,6 @@ export class CameraHelper {
this.scene.clear();
const params = { ...props.axes.params, scale: props.axes.params.scale * this.webgl.pixelRatio };
this.renderObject = createAxesRenderObject(params);
this.renderObject.state.noClip = true;
this.scene.add(this.renderObject);
this.scene.commit();
@@ -201,7 +201,7 @@ function createAxesMesh(scale: number, mesh?: Mesh) {
const x = Vec3.scale(Vec3(), Vec3.unitX, scale);
const y = Vec3.scale(Vec3(), Vec3.unitY, scale);
const z = Vec3.scale(Vec3(), Vec3.unitZ, scale);
const cylinderProps = { radiusTop: radius, radiusBottom: radius, radialSegments: 32 };
const cylinderProps = { radiusTop: radius, radiusBottom: radius, radialSegments: 32 };
state.currentGroup = CameraHelperAxis.None;
addSphere(state, Vec3.origin, radius, 2);

View File

@@ -24,6 +24,7 @@ import { DataLoci, EmptyLoci, Loci } from '../../mol-model/loci';
import { MarkerAction, MarkerActions } from '../../mol-util/marker-action';
import { Visual } from '../../mol-repr/visual';
import { Interval } from '../../mol-data/int';
import { GraphicsRenderVariantsBlended } from '../../mol-gl/webgl/render-item';
const HandleParams = {
...Mesh.Params,
@@ -72,7 +73,6 @@ export class HandleHelper {
this.scene.clear();
const params = { ...props.handle.params, scale: props.handle.params.scale * this.webgl.pixelRatio };
this.renderObject = createHandleRenderObject(params);
this.renderObject.state.noClip = true;
this.scene.add(this.renderObject);
this.scene.commit();
@@ -127,7 +127,7 @@ export class HandleHelper {
}
constructor(private webgl: WebGLContext, props: Partial<HandleHelperProps> = {}) {
this.scene = Scene.create(webgl);
this.scene = Scene.create(webgl, GraphicsRenderVariantsBlended);
this.setProps(props);
}
}

View File

@@ -71,7 +71,7 @@ export class Canvas3dInteractionHelper {
const xyChanged = this.startX !== this.endX || this.startY !== this.endY;
if (e === InputEvent.Drag) {
if (xyChanged && !Representation.Loci.isEmpty(this.prevLoci)) {
if (xyChanged && !this.outsideViewport(this.startX, this.startY)) {
this.events.drag.next({ current: this.prevLoci, buttons: this.buttons, button: this.button, modifiers: this.modifiers, pageStart: Vec2.create(this.startX, this.startY), pageEnd: Vec2.create(this.endX, this.endY) });
this.startX = this.endX;

View File

@@ -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>
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
@@ -53,6 +53,19 @@ function getDepthMergeRenderable(ctx: WebGLContext, depthTexturePrimitives: Text
return createComputeRenderable(renderItem, values);
}
type Props = {
postprocessing: PostprocessingProps
marking: MarkingProps
transparentBackground: boolean;
}
type RenderContext = {
renderer: Renderer;
camera: Camera | StereoCamera;
scene: Scene;
helper: Helper;
}
export class DrawPass {
private readonly drawTarget: RenderTarget;
@@ -264,25 +277,25 @@ export class DrawPass {
renderer.renderBlendedTransparent(scene.primitives, camera, null);
}
private _render(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, toDrawingBuffer: boolean, transparentBackground: boolean, postprocessingProps: PostprocessingProps, markingProps: MarkingProps) {
private _render(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, toDrawingBuffer: boolean, props: Props) {
const volumeRendering = scene.volumes.renderables.length > 0;
const postprocessingEnabled = PostprocessingPass.isEnabled(postprocessingProps);
const antialiasingEnabled = AntialiasingPass.isEnabled(postprocessingProps);
const markingEnabled = MarkingPass.isEnabled(markingProps);
const postprocessingEnabled = PostprocessingPass.isEnabled(props.postprocessing);
const antialiasingEnabled = AntialiasingPass.isEnabled(props.postprocessing);
const markingEnabled = MarkingPass.isEnabled(props.marking);
const { x, y, width, height } = camera.viewport;
renderer.setViewport(x, y, width, height);
renderer.update(camera);
if (transparentBackground && !antialiasingEnabled && toDrawingBuffer) {
if (props.transparentBackground && !antialiasingEnabled && toDrawingBuffer) {
this.drawTarget.bind();
renderer.clear(false);
}
if (this.wboitEnabled) {
this._renderWboit(renderer, camera, scene, transparentBackground, postprocessingProps);
this._renderWboit(renderer, camera, scene, props.transparentBackground, props.postprocessing);
} else {
this._renderBlended(renderer, camera, scene, !volumeRendering && !postprocessingEnabled && !antialiasingEnabled && toDrawingBuffer, transparentBackground, postprocessingProps);
this._renderBlended(renderer, camera, scene, !volumeRendering && !postprocessingEnabled && !antialiasingEnabled && toDrawingBuffer, props.transparentBackground, props.postprocessing);
}
if (postprocessingEnabled) {
@@ -294,18 +307,18 @@ export class DrawPass {
}
if (markingEnabled) {
const markingDepthTest = markingProps.ghostEdgeStrength < 1;
const markingDepthTest = props.marking.ghostEdgeStrength < 1;
if (markingDepthTest) {
this.marking.depthTarget.bind();
renderer.clear(false);
renderer.clear(false, true);
renderer.renderMarkingDepth(scene.primitives, camera, null);
}
this.marking.maskTarget.bind();
renderer.clear(false);
renderer.clear(false, true);
renderer.renderMarkingMask(scene.primitives, camera, markingDepthTest ? this.marking.depthTarget.texture : null);
this.marking.update(markingProps);
this.marking.update(props.marking);
this.marking.render(camera.viewport, postprocessingEnabled ? this.postprocessing.target : this.colorTarget);
}
@@ -323,7 +336,7 @@ export class DrawPass {
}
if (antialiasingEnabled) {
this.antialiasing.render(camera, toDrawingBuffer, postprocessingProps);
this.antialiasing.render(camera, toDrawingBuffer, props.postprocessing);
} else if (toDrawingBuffer) {
this.drawTarget.bind();
@@ -338,16 +351,17 @@ export class DrawPass {
this.webgl.gl.flush();
}
render(renderer: Renderer, camera: Camera | StereoCamera, scene: Scene, helper: Helper, toDrawingBuffer: boolean, transparentBackground: boolean, postprocessingProps: PostprocessingProps, markingProps: MarkingProps) {
renderer.setTransparentBackground(transparentBackground);
render(ctx: RenderContext, props: Props, toDrawingBuffer: boolean) {
const { renderer, camera, scene, helper } = ctx;
renderer.setTransparentBackground(props.transparentBackground);
renderer.setDrawingBufferSize(this.colorTarget.getWidth(), this.colorTarget.getHeight());
renderer.setPixelRatio(this.webgl.pixelRatio);
if (StereoCamera.is(camera)) {
this._render(renderer, camera.left, scene, helper, toDrawingBuffer, transparentBackground, postprocessingProps, markingProps);
this._render(renderer, camera.right, scene, helper, toDrawingBuffer, transparentBackground, postprocessingProps, markingProps);
this._render(renderer, camera.left, scene, helper, toDrawingBuffer, props);
this._render(renderer, camera.right, scene, helper, toDrawingBuffer, props);
} else {
this._render(renderer, camera, scene, helper, toDrawingBuffer, transparentBackground, postprocessingProps, markingProps);
this._render(renderer, camera, scene, helper, toDrawingBuffer, props);
}
}

View File

@@ -83,11 +83,12 @@ export class ImagePass {
Viewport.set(this._camera.viewport, 0, 0, this._width, this._height);
this._camera.update();
const ctx = { renderer: this.renderer, camera: this._camera, scene: this.scene, helper: this.helper };
if (MultiSamplePass.isEnabled(this.props.multiSample)) {
this.multiSampleHelper.render(this.renderer, this._camera, this.scene, this.helper, false, this.props.transparentBackground, this.props);
this.multiSampleHelper.render(ctx, this.props, false);
this._colorTarget = this.multiSamplePass.colorTarget;
} else {
this.drawPass.render(this.renderer, this._camera, this.scene, this.helper, false, this.props.transparentBackground, this.props.postprocessing, this.props.marking);
this.drawPass.render(ctx, this.props, false);
this._colorTarget = this.drawPass.getColorTarget(this.props.postprocessing);
}
}

View File

@@ -22,7 +22,7 @@ import { Color } from '../../mol-util/color';
import { edge_frag } from '../../mol-gl/shader/marking/edge.frag';
export const MarkingParams = {
enabled: PD.Boolean(false),
enabled: PD.Boolean(true),
highlightEdgeColor: PD.Color(Color.darken(Color.fromNormalizedRgb(1.0, 0.4, 0.6), 1.0)),
selectEdgeColor: PD.Color(Color.darken(Color.fromNormalizedRgb(0.2, 1.0, 0.1), 1.0)),
edgeScale: PD.Numeric(1, { min: 1, max: 3, step: 1 }, { description: 'Thickness of the edge.' }),

View File

@@ -59,6 +59,14 @@ type Props = {
multiSample: MultiSampleProps
postprocessing: PostprocessingProps
marking: MarkingProps
transparentBackground: boolean;
}
type RenderContext = {
renderer: Renderer;
camera: Camera | StereoCamera;
scene: Scene;
helper: Helper;
}
export class MultiSamplePass {
@@ -97,12 +105,12 @@ export class MultiSamplePass {
}
}
render(sampleIndex: number, renderer: Renderer, camera: Camera | StereoCamera, scene: Scene, helper: Helper, toDrawingBuffer: boolean, transparentBackground: boolean, props: Props) {
if (props.multiSample.mode === 'temporal') {
return this.renderTemporalMultiSample(sampleIndex, renderer, camera, scene, helper, toDrawingBuffer, transparentBackground, props);
render(sampleIndex: number, ctx: RenderContext, props: Props, toDrawingBuffer: boolean, forceOn: boolean) {
if (props.multiSample.mode === 'temporal' && !forceOn) {
return this.renderTemporalMultiSample(sampleIndex, ctx, props, toDrawingBuffer);
} else {
this.renderMultiSample(renderer, camera, scene, helper, toDrawingBuffer, transparentBackground, props);
return sampleIndex;
this.renderMultiSample(ctx, toDrawingBuffer, props);
return -2;
}
}
@@ -114,7 +122,8 @@ export class MultiSamplePass {
}
}
private renderMultiSample(renderer: Renderer, camera: Camera | StereoCamera, scene: Scene, helper: Helper, toDrawingBuffer: boolean, transparentBackground: boolean, props: Props) {
private renderMultiSample(ctx: RenderContext, toDrawingBuffer: boolean, props: Props) {
const { camera } = ctx;
const { compose, composeTarget, drawPass, webgl } = this;
const { gl, state } = webgl;
@@ -148,7 +157,7 @@ export class MultiSamplePass {
ValueCell.update(compose.values.uWeight, sampleWeight);
// render scene
drawPass.render(renderer, camera, scene, helper, false, transparentBackground, props.postprocessing, props.marking);
drawPass.render(ctx, props, false);
// compose rendered scene with compose target
composeTarget.bind();
@@ -181,7 +190,8 @@ export class MultiSamplePass {
camera.update();
}
private renderTemporalMultiSample(sampleIndex: number, renderer: Renderer, camera: Camera | StereoCamera, scene: Scene, helper: Helper, toDrawingBuffer: boolean, transparentBackground: boolean, props: Props) {
private renderTemporalMultiSample(sampleIndex: number, ctx: RenderContext, props: Props, toDrawingBuffer: boolean) {
const { camera } = ctx;
const { compose, composeTarget, holdTarget, drawPass, webgl } = this;
const { gl, state } = webgl;
@@ -198,7 +208,7 @@ export class MultiSamplePass {
const sampleWeight = 1.0 / offsetList.length;
if (sampleIndex === -1) {
drawPass.render(renderer, camera, scene, helper, false, transparentBackground, props.postprocessing, props.marking);
drawPass.render(ctx, props, false);
ValueCell.update(compose.values.uWeight, 1.0);
ValueCell.update(compose.values.tColor, drawPass.getColorTarget(props.postprocessing).texture);
compose.update();
@@ -226,7 +236,7 @@ export class MultiSamplePass {
camera.update();
// render scene
drawPass.render(renderer, camera, scene, helper, false, transparentBackground, props.postprocessing, props.marking);
drawPass.render(ctx, props, false);
// compose rendered scene with compose target
composeTarget.bind();
@@ -325,8 +335,8 @@ export class MultiSampleHelper {
}
/** Return `true` while more samples are needed */
render(renderer: Renderer, camera: Camera | StereoCamera, scene: Scene, helper: Helper, toDrawingBuffer: boolean, transparentBackground: boolean, props: Props) {
this.sampleIndex = this.multiSamplePass.render(this.sampleIndex, renderer, camera, scene, helper, toDrawingBuffer, transparentBackground, props);
render(ctx: RenderContext, props: Props, toDrawingBuffer: boolean, forceOn?: boolean) {
this.sampleIndex = this.multiSamplePass.render(this.sampleIndex, ctx, props, toDrawingBuffer, !!forceOn);
return this.sampleIndex < 0;
}

View File

@@ -5,14 +5,14 @@
*/
import { PickingId } from '../../mol-geo/geometry/picking';
import { Renderer } from '../../mol-gl/renderer';
import { PickType, Renderer } from '../../mol-gl/renderer';
import { Scene } from '../../mol-gl/scene';
import { WebGLContext } from '../../mol-gl/webgl/context';
import { GraphicsRenderVariant } from '../../mol-gl/webgl/render-item';
import { RenderTarget } from '../../mol-gl/webgl/render-target';
import { Vec3 } from '../../mol-math/linear-algebra';
import { spiral2d } from '../../mol-math/misc';
import { decodeFloatRGB, unpackRGBAToDepth } from '../../mol-util/float-packing';
import { unpackRGBToInt, unpackRGBAToDepth } from '../../mol-util/number-packing';
import { Camera, ICamera } from '../camera';
import { StereoCamera } from '../camera/stereo';
import { cameraUnproject } from '../camera/util';
@@ -64,35 +64,35 @@ export class PickPass {
}
}
private renderVariant(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, variant: GraphicsRenderVariant) {
private renderVariant(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, variant: GraphicsRenderVariant, pickType: number) {
const depth = this.drawPass.depthTexturePrimitives;
renderer.clear(false);
renderer.update(camera);
renderer.renderPick(scene.primitives, camera, variant, null);
renderer.renderPick(scene.volumes, camera, variant, depth);
renderer.renderPick(helper.handle.scene, camera, variant, null);
renderer.renderPick(scene.primitives, camera, variant, null, pickType);
renderer.renderPick(scene.volumes, camera, variant, depth, pickType);
renderer.renderPick(helper.handle.scene, camera, variant, null, pickType);
if (helper.camera.isEnabled) {
helper.camera.update(camera);
renderer.update(helper.camera.camera);
renderer.renderPick(helper.camera.scene, helper.camera.camera, variant, null);
renderer.renderPick(helper.camera.scene, helper.camera.camera, variant, null, pickType);
}
}
render(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper) {
this.objectPickTarget.bind();
this.renderVariant(renderer, camera, scene, helper, 'pickObject');
this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Object);
this.instancePickTarget.bind();
this.renderVariant(renderer, camera, scene, helper, 'pickInstance');
this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Instance);
this.groupPickTarget.bind();
this.renderVariant(renderer, camera, scene, helper, 'pickGroup');
this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Group);
// printTexture(this.webgl, this.groupPickTarget.texture, { id: 'group' })
this.depthPickTarget.bind();
this.renderVariant(renderer, camera, scene, helper, 'depth');
this.renderVariant(renderer, camera, scene, helper, 'depth', PickType.None);
}
}
@@ -174,7 +174,7 @@ export class PickHelper {
private getId(x: number, y: number, buffer: Uint8Array) {
const idx = this.getBufferIdx(x, y);
return decodeFloatRGB(buffer[idx], buffer[idx + 1], buffer[idx + 2]);
return unpackRGBToInt(buffer[idx], buffer[idx + 1], buffer[idx + 2]);
}
private render(camera: Camera | StereoCamera) {

View File

@@ -86,7 +86,7 @@ function getSsaoRenderable(ctx: WebGLContext, depthTexture: Texture): SsaoRender
tDepth: ValueCell.create(depthTexture),
uSamples: ValueCell.create([0.0, 0.0, 1.0]),
dNSamples: ValueCell.create(1),
dNSamples: ValueCell.create(32),
uProjection: ValueCell.create(Mat4.identity()),
uInvProjection: ValueCell.create(Mat4.identity()),
@@ -133,7 +133,7 @@ function getSsaoBlurRenderable(ctx: WebGLContext, ssaoDepthTexture: Texture, dir
uTexSize: ValueCell.create(Vec2.create(ssaoDepthTexture.getWidth(), ssaoDepthTexture.getHeight())),
uKernel: ValueCell.create([0.0]),
dOcclusionKernelSize: ValueCell.create(1),
dOcclusionKernelSize: ValueCell.create(15),
uBlurDirectionX: ValueCell.create(direction === 'horizontal' ? 1 : 0),
uBlurDirectionY: ValueCell.create(direction === 'vertical' ? 1 : 0),
@@ -226,7 +226,7 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d
uMaxPossibleViewZDiff: ValueCell.create(0.5),
dOcclusionEnable: ValueCell.create(false),
dOcclusionEnable: ValueCell.create(true),
dOutlineEnable: ValueCell.create(false),
dOutlineScale: ValueCell.create(1),

View File

@@ -201,7 +201,7 @@ function getWeightsRenderable(ctx: WebGLContext, edgesTexture: Texture): Weights
uTexSizeInv: ValueCell.create(Vec2.create(1 / width, 1 / height)),
uViewport: ValueCell.create(Vec4()),
dMaxSearchSteps: ValueCell.create(8),
dMaxSearchSteps: ValueCell.create(16),
};
// Note: loading image textures requires `HTMLImageElement` to be available

View File

@@ -0,0 +1,16 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { getMarkersAverage } from '../marker-data';
describe('marker-data', () => {
it('getMarkersAverage', () => {
expect(getMarkersAverage(new Uint8Array([0, 0, 0, 0]), 3)).toBe(0);
expect(getMarkersAverage(new Uint8Array([0, 0, 1, 0]), 3)).toBe(1 / 3);
expect(getMarkersAverage(new Uint8Array([0, 0, 0, 0]), 4)).toBe(0);
expect(getMarkersAverage(new Uint8Array([0, 0, 1, 0]), 4)).toBe(1 / 4);
});
});

View File

@@ -17,6 +17,7 @@ import { UniformColorTheme } from '../../mol-theme/color/uniform';
import { UniformSizeTheme } from '../../mol-theme/size/uniform';
import { smoothstep } from '../../mol-math/interpolate';
import { Material } from '../../mol-util/material';
import { Clip } from '../../mol-util/clip';
export const VisualQualityInfo = {
'custom': {},
@@ -81,6 +82,7 @@ export namespace BaseGeometry {
alpha: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }, { label: 'Opacity', isEssential: true, description: 'How opaque/transparent the representation is rendered.' }),
quality: PD.Select<VisualQuality>('auto', VisualQualityOptions, { isEssential: true, description: 'Visual/rendering quality of the representation.' }),
material: Material.getParam(),
clip: PD.Group(Clip.Params),
};
export type Params = typeof Params
@@ -97,6 +99,7 @@ export namespace BaseGeometry {
}
export function createValues(props: PD.Values<Params>, counts: Counts) {
const clip = Clip.getClip(props.clip);
return {
alpha: ValueCell.create(props.alpha),
uAlpha: ValueCell.create(props.alpha),
@@ -107,6 +110,14 @@ export namespace BaseGeometry {
uRoughness: ValueCell.create(props.material.roughness),
uBumpiness: ValueCell.create(props.material.bumpiness),
dLightCount: ValueCell.create(1),
dClipObjectCount: ValueCell.create(clip.objects.count),
dClipVariant: ValueCell.create(clip.variant),
uClipObjectType: ValueCell.create(clip.objects.type),
uClipObjectInvert: ValueCell.create(clip.objects.invert),
uClipObjectPosition: ValueCell.create(clip.objects.position),
uClipObjectRotation: ValueCell.create(clip.objects.rotation),
uClipObjectScale: ValueCell.create(clip.objects.scale),
};
}
@@ -115,6 +126,15 @@ export namespace BaseGeometry {
ValueCell.updateIfChanged(values.uMetalness, props.material.metalness);
ValueCell.updateIfChanged(values.uRoughness, props.material.roughness);
ValueCell.updateIfChanged(values.uBumpiness, props.material.bumpiness);
const clip = Clip.getClip(props.clip);
ValueCell.update(values.dClipObjectCount, clip.objects.count);
ValueCell.update(values.dClipVariant, clip.variant);
ValueCell.update(values.uClipObjectType, clip.objects.type);
ValueCell.update(values.uClipObjectInvert, clip.objects.invert);
ValueCell.update(values.uClipObjectPosition, clip.objects.position);
ValueCell.update(values.uClipObjectRotation, clip.objects.rotation);
ValueCell.update(values.uClipObjectScale, clip.objects.scale);
}
export function createRenderableState(props: Partial<PD.Values<Params>> = {}): RenderableState {
@@ -127,7 +147,6 @@ export namespace BaseGeometry {
colorOnly: false,
opaque,
writeDepth: opaque,
noClip: false,
};
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -10,18 +10,13 @@ import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
import { Clipping } from '../../mol-theme/clipping';
export type ClippingData = {
dClipObjectCount: ValueCell<number>,
dClipVariant: ValueCell<string>,
tClipping: ValueCell<TextureImage<Uint8Array>>
uClippingTexDim: ValueCell<Vec2>
dClipping: ValueCell<boolean>,
}
export function applyClippingGroups(array: Uint8Array, start: number, end: number, groups: Clipping.Groups) {
for (let i = start; i < end; ++i) {
array[i] = groups;
}
array.fill(groups, start, end);
return true;
}
@@ -38,9 +33,6 @@ export function createClipping(count: number, clippingData?: ClippingData): Clip
return clippingData;
} else {
return {
dClipObjectCount: ValueCell.create(0),
dClipVariant: ValueCell.create('instance'),
tClipping: ValueCell.create(clipping),
uClippingTexDim: ValueCell.create(Vec2.create(clipping.width, clipping.height)),
dClipping: ValueCell.create(count > 0),
@@ -53,12 +45,10 @@ export function createEmptyClipping(clippingData?: ClippingData): ClippingData {
if (clippingData) {
ValueCell.update(clippingData.tClipping, emptyClippingTexture);
ValueCell.update(clippingData.uClippingTexDim, Vec2.create(1, 1));
ValueCell.updateIfChanged(clippingData.dClipping, false);
return clippingData;
} else {
return {
dClipObjectCount: ValueCell.create(0),
dClipVariant: ValueCell.create('instance'),
tClipping: ValueCell.create(emptyClippingTexture),
uClippingTexDim: ValueCell.create(Vec2.create(1, 1)),
dClipping: ValueCell.create(false),

View File

@@ -15,7 +15,7 @@ import { LocationColor, ColorTheme } from '../../mol-theme/color';
import { Geometry } from './geometry';
import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
export type ColorType = 'uniform' | 'instance' | 'group' | 'groupInstance' | 'vertex' | 'vertexInstance' | 'volume' | 'volumeInstance'
export type ColorType = 'uniform' | 'instance' | 'group' | 'groupInstance' | 'vertex' | 'vertexInstance' | 'volume' | 'volumeInstance' | 'direct'
export type ColorData = {
uColor: ValueCell<Vec3>,
@@ -50,6 +50,7 @@ function _createColors(locationIt: LocationIterator, positionIt: LocationIterato
case 'vertexInstance': return createVertexInstanceColor(positionIt, colorTheme.color, colorData);
case 'volume': return createGridColor((colorTheme as any).grid, 'volume', colorData);
case 'volumeInstance': return createGridColor((colorTheme as any).grid, 'volumeInstance', colorData);
case 'direct': return createDirectColor(colorData);
}
}
@@ -237,3 +238,25 @@ export function createGridColor(grid: ColorVolume, type: ColorType, colorData?:
};
}
}
//
/** Creates direct color */
function createDirectColor(colorData?: ColorData): ColorData {
if (colorData) {
ValueCell.updateIfChanged(colorData.dColorType, 'direct');
return colorData;
} else {
return {
uColor: ValueCell.create(Vec3()),
tColor: ValueCell.create({ array: new Uint8Array(3), width: 1, height: 1 }),
tColorGrid: ValueCell.create(createNullTexture()),
tPalette: ValueCell.create({ array: new Uint8Array(3), width: 1, height: 1 }),
uColorTexDim: ValueCell.create(Vec2.create(1, 1)),
uColorGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
uColorGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
dColorType: ValueCell.create('direct'),
dUsePalette: ValueCell.create(false),
};
}
}

View File

@@ -213,6 +213,8 @@ export namespace Cylinders {
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
return {
dGeometryType: ValueCell.create('cylinders'),
aMapping: cylinders.mappingBuffer,
aGroup: cylinders.groupBuffer,
aStart: cylinders.startBuffer,
@@ -236,7 +238,7 @@ export namespace Cylinders {
...BaseGeometry.createValues(props, counts),
uSizeFactor: ValueCell.create(props.sizeFactor * props.sizeAspectRatio),
dDoubleSided: ValueCell.create(props.doubleSided),
uDoubleSided: ValueCell.create(props.doubleSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
uBumpFrequency: ValueCell.create(props.bumpFrequency),
@@ -253,7 +255,7 @@ export namespace Cylinders {
function updateValues(values: CylindersValues, props: PD.Values<Params>) {
BaseGeometry.updateValues(values, props);
ValueCell.updateIfChanged(values.uSizeFactor, props.sizeFactor * props.sizeAspectRatio);
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -26,8 +26,7 @@ import { TransformData } from '../transform-data';
import { createEmptyTransparency } from '../transparency-data';
import { createTransferFunctionTexture, getControlPointsFromVec2Array } from './transfer-function';
import { createEmptyClipping } from '../clipping-data';
import { Grid, Volume } from '../../../mol-model/volume';
import { ColorNames } from '../../../mol-util/color/names';
import { Grid } from '../../../mol-model/volume';
import { createEmptySubstance } from '../substance-data';
const VolumeBox = Box();
@@ -48,6 +47,7 @@ export interface DirectVolume {
readonly unitToCartn: ValueCell<Mat4>
readonly cartnToUnit: ValueCell<Mat4>
readonly packedGroup: ValueCell<boolean>
readonly axisOrder: ValueCell<Vec3>
/** Bounding sphere of the volume */
readonly boundingSphere: Sphere3D
@@ -56,10 +56,10 @@ export interface DirectVolume {
}
export namespace DirectVolume {
export function create(bbox: Box3D, gridDimension: Vec3, transform: Mat4, unitToCartn: Mat4, cellDim: Vec3, texture: Texture, stats: Grid['stats'], packedGroup: boolean, directVolume?: DirectVolume): DirectVolume {
export function create(bbox: Box3D, gridDimension: Vec3, transform: Mat4, unitToCartn: Mat4, cellDim: Vec3, texture: Texture, stats: Grid['stats'], packedGroup: boolean, axisOrder: Vec3, directVolume?: DirectVolume): DirectVolume {
return directVolume ?
update(bbox, gridDimension, transform, unitToCartn, cellDim, texture, stats, packedGroup, directVolume) :
fromData(bbox, gridDimension, transform, unitToCartn, cellDim, texture, stats, packedGroup);
update(bbox, gridDimension, transform, unitToCartn, cellDim, texture, stats, packedGroup, axisOrder, directVolume) :
fromData(bbox, gridDimension, transform, unitToCartn, cellDim, texture, stats, packedGroup, axisOrder);
}
function hashCode(directVolume: DirectVolume) {
@@ -70,7 +70,7 @@ export namespace DirectVolume {
]);
}
function fromData(bbox: Box3D, gridDimension: Vec3, transform: Mat4, unitToCartn: Mat4, cellDim: Vec3, texture: Texture, stats: Grid['stats'], packedGroup: boolean): DirectVolume {
function fromData(bbox: Box3D, gridDimension: Vec3, transform: Mat4, unitToCartn: Mat4, cellDim: Vec3, texture: Texture, stats: Grid['stats'], packedGroup: boolean, axisOrder: Vec3): DirectVolume {
const boundingSphere = Sphere3D();
let currentHash = -1;
@@ -101,6 +101,7 @@ export namespace DirectVolume {
return boundingSphere;
},
packedGroup: ValueCell.create(packedGroup),
axisOrder: ValueCell.create(axisOrder),
setBoundingSphere(sphere: Sphere3D) {
Sphere3D.copy(boundingSphere, sphere);
currentHash = hashCode(directVolume);
@@ -109,7 +110,7 @@ export namespace DirectVolume {
return directVolume;
}
function update(bbox: Box3D, gridDimension: Vec3, transform: Mat4, unitToCartn: Mat4, cellDim: Vec3, texture: Texture, stats: Grid['stats'], packedGroup: boolean, directVolume: DirectVolume): DirectVolume {
function update(bbox: Box3D, gridDimension: Vec3, transform: Mat4, unitToCartn: Mat4, cellDim: Vec3, texture: Texture, stats: Grid['stats'], packedGroup: boolean, axisOrder: Vec3, directVolume: DirectVolume): DirectVolume {
const width = texture.getWidth();
const height = texture.getHeight();
const depth = texture.getDepth();
@@ -126,6 +127,7 @@ export namespace DirectVolume {
ValueCell.update(directVolume.unitToCartn, unitToCartn);
ValueCell.update(directVolume.cartnToUnit, Mat4.invert(Mat4(), unitToCartn));
ValueCell.updateIfChanged(directVolume.packedGroup, packedGroup);
ValueCell.updateIfChanged(directVolume.axisOrder, Vec3.fromArray(directVolume.axisOrder.ref.value, axisOrder, 0));
return directVolume;
}
@@ -138,47 +140,19 @@ export namespace DirectVolume {
const texture = createNullTexture();
const stats = Grid.One.stats;
const packedGroup = false;
return create(bbox, gridDimension, transform, unitToCartn, cellDim, texture, stats, packedGroup, directVolume);
}
export function createRenderModeParam(stats?: Grid['stats']) {
const isoValueParam = stats
? Volume.createIsoValueParam(Volume.IsoValue.relative(2), stats)
: Volume.IsoValueParam;
return PD.MappedStatic('volume', {
isosurface: PD.Group({
isoValue: isoValueParam,
singleLayer: PD.Boolean(false, { isEssential: true }),
}, { isFlat: true }),
volume: PD.Group({
controlPoints: PD.LineGraph([
Vec2.create(0.19, 0.0), Vec2.create(0.2, 0.05), Vec2.create(0.25, 0.05), Vec2.create(0.26, 0.0),
Vec2.create(0.79, 0.0), Vec2.create(0.8, 0.05), Vec2.create(0.85, 0.05), Vec2.create(0.86, 0.0),
]),
list: PD.ColorList({
kind: 'interpolate',
colors: [
[ColorNames.white, 0],
[ColorNames.red, 0.25],
[ColorNames.white, 0.5],
[ColorNames.blue, 0.75],
[ColorNames.white, 1]
]
}, { offsets: true }),
}, { isFlat: true })
}, { isEssential: true });
const axisOrder = Vec3.create(0, 1, 2);
return create(bbox, gridDimension, transform, unitToCartn, cellDim, texture, stats, packedGroup, axisOrder, directVolume);
}
export const Params = {
...BaseGeometry.Params,
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
flipSided: PD.Boolean(false, BaseGeometry.ShadingCategory),
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
renderMode: createRenderModeParam(),
stepsPerCell: PD.Numeric(5, { min: 1, max: 20, step: 1 }),
controlPoints: PD.LineGraph([
Vec2.create(0.19, 0.0), Vec2.create(0.2, 0.05), Vec2.create(0.25, 0.05), Vec2.create(0.26, 0.0),
Vec2.create(0.79, 0.0), Vec2.create(0.8, 0.05), Vec2.create(0.85, 0.05), Vec2.create(0.86, 0.0),
], { isEssential: true }),
stepsPerCell: PD.Numeric(3, { min: 1, max: 10, step: 1 }),
jumpLength: PD.Numeric(0, { min: 0, max: 20, step: 0.1 }),
};
export type Params = typeof Params
@@ -217,13 +191,6 @@ export namespace DirectVolume {
return LocationIterator(groupCount, instanceCount, 1, getLocation);
}
function getNormalizedIsoValue(out: Vec2, isoValue: Volume.IsoValue, stats: Vec4) {
const [min, max, mean, sigma] = stats;
const value = Volume.IsoValue.toAbsolute(isoValue, { min, max, mean, sigma }).absoluteValue;
Vec2.set(out, (value - min) / (max - min), (0 - min) / (max - min));
return out;
}
function getMaxSteps(gridDim: Vec3, stepsPerCell: number) {
return Math.ceil(Vec3.magnitude(gridDim) * stepsPerCell);
}
@@ -256,18 +223,12 @@ export namespace DirectVolume {
const invariantBoundingSphere = Sphere3D.clone(directVolume.boundingSphere);
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
const controlPoints = props.renderMode.name === 'volume' ? getControlPointsFromVec2Array(props.renderMode.params.controlPoints) : [];
const transferTex = createTransferFunctionTexture(controlPoints, props.renderMode.name === 'volume' ? props.renderMode.params.list.colors : []);
const isoValue = props.renderMode.name === 'isosurface'
? props.renderMode.params.isoValue
: Volume.IsoValue.relative(2);
const singleLayer = props.renderMode.name === 'isosurface'
? props.renderMode.params.singleLayer
: false;
const controlPoints = getControlPointsFromVec2Array(props.controlPoints);
const transferTex = createTransferFunctionTexture(controlPoints);
return {
dGeometryType: ValueCell.create('directVolume'),
...color,
...marker,
...overpaint,
@@ -283,7 +244,6 @@ export namespace DirectVolume {
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
uIsoValue: ValueCell.create(getNormalizedIsoValue(Vec2(), isoValue, directVolume.gridStats.ref.value)),
uBboxMin: bboxMin,
uBboxMax: bboxMax,
uBboxSize: bboxSize,
@@ -292,7 +252,6 @@ export namespace DirectVolume {
uJumpLength: ValueCell.create(props.jumpLength),
uTransform: gridTransform,
uGridDim: gridDimension,
dRenderMode: ValueCell.create(props.renderMode.name),
tTransferTex: transferTex,
uTransferScale: ValueCell.create(getTransferScale(props.stepsPerCell)),
@@ -305,11 +264,8 @@ export namespace DirectVolume {
uCartnToUnit: directVolume.cartnToUnit,
uUnitToCartn: directVolume.unitToCartn,
dPackedGroup: directVolume.packedGroup,
dSingleLayer: ValueCell.create(singleLayer),
dAxisOrder: ValueCell.create(directVolume.axisOrder.ref.value.join('')),
dDoubleSided: ValueCell.create(props.doubleSided),
dFlatShaded: ValueCell.create(props.flatShaded),
dFlipSided: ValueCell.create(props.flipSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
};
@@ -323,20 +279,11 @@ export namespace DirectVolume {
function updateValues(values: DirectVolumeValues, props: PD.Values<Params>) {
BaseGeometry.updateValues(values, props);
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded);
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
ValueCell.updateIfChanged(values.dRenderMode, props.renderMode.name);
if (props.renderMode.name === 'isosurface') {
ValueCell.updateIfChanged(values.uIsoValue, getNormalizedIsoValue(values.uIsoValue.ref.value, props.renderMode.params.isoValue, values.uGridStats.ref.value));
ValueCell.updateIfChanged(values.dSingleLayer, props.renderMode.params.singleLayer);
} else if (props.renderMode.name === 'volume') {
const controlPoints = getControlPointsFromVec2Array(props.renderMode.params.controlPoints);
createTransferFunctionTexture(controlPoints, props.renderMode.params.list.colors, values.tTransferTex);
}
const controlPoints = getControlPointsFromVec2Array(props.controlPoints);
createTransferFunctionTexture(controlPoints, values.tTransferTex);
ValueCell.updateIfChanged(values.uMaxSteps, getMaxSteps(values.uGridDim.ref.value, props.stepsPerCell));
ValueCell.updateIfChanged(values.uStepScale, getStepScale(values.uCellDim.ref.value, props.stepsPerCell));
@@ -360,14 +307,14 @@ export namespace DirectVolume {
function createRenderableState(props: PD.Values<Params>): RenderableState {
const state = BaseGeometry.createRenderableState(props);
state.opaque = false;
state.writeDepth = props.renderMode.name === 'isosurface';
state.writeDepth = false;
return state;
}
function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
BaseGeometry.updateRenderableState(state, props);
state.opaque = false;
state.writeDepth = props.renderMode.name === 'isosurface';
state.writeDepth = false;
}
}

View File

@@ -1,16 +1,13 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { TextureImage } from '../../../mol-gl/renderable/util';
import { spline } from '../../../mol-math/interpolate';
import { ColorScale } from '../../../mol-util/color';
import { ValueCell } from '../../../mol-util';
import { Vec2 } from '../../../mol-math/linear-algebra';
import { ColorListName } from '../../../mol-util/color/lists';
import { ColorListEntry } from '../../../mol-util/color/color';
export interface ControlPoint { x: number, alpha: number }
@@ -25,7 +22,7 @@ export function getControlPointsFromVec2Array(array: Vec2[]): ControlPoint[] {
return array.map(v => ({ x: v[0], alpha: v[1] }));
}
export function createTransferFunctionTexture(controlPoints: ControlPoint[], listOrName: ColorListEntry[] | ColorListName, texture?: ValueCell<TextureImage<Uint8Array>>): ValueCell<TextureImage<Uint8Array>> {
export function createTransferFunctionTexture(controlPoints: ControlPoint[], texture?: ValueCell<TextureImage<Uint8Array>>): ValueCell<TextureImage<Uint8Array>> {
const cp = [
{ x: 0, alpha: 0 },
{ x: 0, alpha: 0 },
@@ -33,10 +30,9 @@ export function createTransferFunctionTexture(controlPoints: ControlPoint[], lis
{ x: 1, alpha: 0 },
{ x: 1, alpha: 0 },
];
const scale = ColorScale.create({ domain: [0, 1], listOrName });
const n = 256;
const array = texture ? texture.ref.value.array : new Uint8Array(n * 4);
const array = texture ? texture.ref.value.array : new Uint8Array(n);
let k = 0;
let x1: number, x2: number;
@@ -55,8 +51,7 @@ export function createTransferFunctionTexture(controlPoints: ControlPoint[], lis
const jl = Math.round((x2 - x1) * n);
for (let j = 0; j < jl; ++j) {
const t = j / jl;
array[k * 4 + 3] = Math.max(0, spline(a0, a1, a2, a3, t, 0.5) * 255);
scale.colorToArray(k / 255, array, k * 4);
array[k] = Math.max(0, spline(a0, a1, a2, a3, t, 0.5) * 255);
++k;
}
}

View File

@@ -155,6 +155,8 @@ namespace Image {
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
return {
dGeometryType: ValueCell.create('image'),
...color,
...marker,
...overpaint,

View File

@@ -220,6 +220,8 @@ export namespace Lines {
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
return {
dGeometryType: ValueCell.create('lines'),
aMapping: lines.mappingBuffer,
aGroup: lines.groupBuffer,
aStart: lines.startBuffer,
@@ -240,7 +242,7 @@ export namespace Lines {
...BaseGeometry.createValues(props, counts),
uSizeFactor: ValueCell.create(props.sizeFactor),
dLineSizeAttenuation: ValueCell.create(props.lineSizeAttenuation),
dDoubleSided: ValueCell.create(true),
uDoubleSided: ValueCell.create(true),
dFlipSided: ValueCell.create(false),
};
}

View File

@@ -12,7 +12,6 @@ export type MarkerData = {
uMarker: ValueCell<number>,
tMarker: ValueCell<TextureImage<Uint8Array>>
uMarkerTexDim: ValueCell<Vec2>
dMarkerType: ValueCell<string>,
markerAverage: ValueCell<number>
markerStatus: ValueCell<number>
}
@@ -48,12 +47,19 @@ export function getMarkersAverage(array: Uint8Array, count: number): number {
const backStart = 4 * viewEnd;
let sum = 0;
for (let i = 0; i < viewEnd; ++i) {
const v = view[i];
sum += MarkerCountLut[v & 0xFFFF] + MarkerCountLut[v >> 16];
}
for (let i = backStart; i < count; ++i) {
sum += array[i] && 1;
if (viewEnd < 0) {
// avoid edge cases with small arrays
for (let i = 0; i < count; ++i) {
sum += array[i] && 1;
}
} else {
for (let i = 0; i < viewEnd; ++i) {
const v = view[i];
sum += MarkerCountLut[v & 0xFFFF] + MarkerCountLut[v >> 16];
}
for (let i = backStart; i < count; ++i) {
sum += array[i] && 1;
}
}
return sum / count;
}
@@ -66,7 +72,6 @@ export function createMarkers(count: number, markerData?: MarkerData): MarkerDat
ValueCell.updateIfChanged(markerData.uMarker, 0);
ValueCell.update(markerData.tMarker, markers);
ValueCell.update(markerData.uMarkerTexDim, Vec2.create(markers.width, markers.height));
ValueCell.updateIfChanged(markerData.dMarkerType, status === -1 ? 'groupInstance' : 'uniform');
ValueCell.updateIfChanged(markerData.markerAverage, average);
ValueCell.updateIfChanged(markerData.markerStatus, status);
return markerData;
@@ -77,7 +82,6 @@ export function createMarkers(count: number, markerData?: MarkerData): MarkerDat
uMarkerTexDim: ValueCell.create(Vec2.create(markers.width, markers.height)),
markerAverage: ValueCell.create(average),
markerStatus: ValueCell.create(status),
dMarkerType: ValueCell.create('uniform'),
};
}
}
@@ -88,7 +92,6 @@ export function createEmptyMarkers(markerData?: MarkerData): MarkerData {
ValueCell.updateIfChanged(markerData.uMarker, 0);
ValueCell.update(markerData.tMarker, emptyMarkerTexture);
ValueCell.update(markerData.uMarkerTexDim, Vec2.create(1, 1));
ValueCell.updateIfChanged(markerData.dMarkerType, 'uniform');
ValueCell.updateIfChanged(markerData.markerAverage, 0);
ValueCell.updateIfChanged(markerData.markerStatus, 0);
return markerData;
@@ -99,7 +102,6 @@ export function createEmptyMarkers(markerData?: MarkerData): MarkerData {
uMarkerTexDim: ValueCell.create(Vec2.create(1, 1)),
markerAverage: ValueCell.create(0),
markerStatus: ValueCell.create(0),
dMarkerType: ValueCell.create('uniform'),
};
}
}

View File

@@ -677,6 +677,8 @@ export namespace Mesh {
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
return {
dGeometryType: ValueCell.create('mesh'),
aPosition: mesh.vertexBuffer,
aNormal: mesh.normalBuffer,
aGroup: mesh.groupBuffer,
@@ -693,7 +695,7 @@ export namespace Mesh {
...transform,
...BaseGeometry.createValues(props, counts),
dDoubleSided: ValueCell.create(props.doubleSided),
uDoubleSided: ValueCell.create(props.doubleSided),
dFlatShaded: ValueCell.create(props.flatShaded),
dFlipSided: ValueCell.create(props.flipSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
@@ -713,7 +715,7 @@ export namespace Mesh {
function updateValues(values: MeshValues, props: PD.Values<Params>) {
BaseGeometry.updateValues(values, props);
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided);
ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded);
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);

View File

@@ -182,6 +182,8 @@ export namespace Points {
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
return {
dGeometryType: ValueCell.create('points'),
aPosition: points.centerBuffer,
aGroup: points.groupBuffer,
boundingSphere: ValueCell.create(boundingSphere),

View File

@@ -11,7 +11,7 @@ import { LocationIterator } from '../util/location-iterator';
import { Location, NullLocation } from '../../mol-model/location';
import { SizeTheme } from '../../mol-theme/size';
import { Geometry } from './geometry';
import { decodeFloatRGB, encodeFloatRGBtoArray } from '../../mol-util/float-packing';
import { unpackRGBToInt, packIntToRGBArray } from '../../mol-util/number-packing';
export type SizeType = 'uniform' | 'instance' | 'group' | 'groupInstance'
@@ -44,7 +44,7 @@ export function getMaxSize(sizeData: SizeData): number {
let maxSize = 0;
const array = sizeData.tSize.ref.value.array;
for (let i = 0, il = array.length; i < il; i += 3) {
const value = decodeFloatRGB(array[i], array[i + 1], array[i + 2]);
const value = unpackRGBToInt(array[i], array[i + 1], array[i + 2]);
if (maxSize < value) maxSize = value;
}
return maxSize / sizeDataFactor;
@@ -103,7 +103,7 @@ export function createInstanceSize(locationIt: LocationIterator, sizeFn: Locatio
locationIt.reset();
while (locationIt.hasNext && !locationIt.isNextNewInstance) {
const v = locationIt.move();
encodeFloatRGBtoArray(sizeFn(v.location) * sizeDataFactor, sizes.array, v.instanceIndex * 3);
packIntToRGBArray(sizeFn(v.location) * sizeDataFactor, sizes.array, v.instanceIndex * 3);
locationIt.skipInstance();
}
return createTextureSize(sizes, 'instance', sizeData);
@@ -116,7 +116,7 @@ export function createGroupSize(locationIt: LocationIterator, sizeFn: LocationSi
locationIt.reset();
while (locationIt.hasNext && !locationIt.isNextNewInstance) {
const v = locationIt.move();
encodeFloatRGBtoArray(sizeFn(v.location) * sizeDataFactor, sizes.array, v.groupIndex * 3);
packIntToRGBArray(sizeFn(v.location) * sizeDataFactor, sizes.array, v.groupIndex * 3);
}
return createTextureSize(sizes, 'group', sizeData);
}
@@ -129,7 +129,7 @@ export function createGroupInstanceSize(locationIt: LocationIterator, sizeFn: Lo
locationIt.reset();
while (locationIt.hasNext) {
const v = locationIt.move();
encodeFloatRGBtoArray(sizeFn(v.location) * sizeDataFactor, sizes.array, v.index * 3);
packIntToRGBArray(sizeFn(v.location) * sizeDataFactor, sizes.array, v.index * 3);
}
return createTextureSize(sizes, 'groupInstance', sizeData);
}

View File

@@ -183,6 +183,8 @@ export namespace Spheres {
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
return {
dGeometryType: ValueCell.create('spheres'),
aPosition: spheres.centerBuffer,
aMapping: spheres.mappingBuffer,
aGroup: spheres.groupBuffer,
@@ -203,7 +205,7 @@ export namespace Spheres {
...BaseGeometry.createValues(props, counts),
uSizeFactor: ValueCell.create(props.sizeFactor),
dDoubleSided: ValueCell.create(props.doubleSided),
uDoubleSided: ValueCell.create(props.doubleSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
uBumpFrequency: ValueCell.create(props.bumpFrequency),
@@ -220,7 +222,7 @@ export namespace Spheres {
function updateValues(values: SpheresValues, props: PD.Values<Params>) {
BaseGeometry.updateValues(values, props);
ValueCell.updateIfChanged(values.uSizeFactor, props.sizeFactor);
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);

View File

@@ -224,6 +224,8 @@ export namespace Text {
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
return {
dGeometryType: ValueCell.create('text'),
aPosition: text.centerBuffer,
aMapping: text.mappingBuffer,
aDepth: text.depthBuffer,

View File

@@ -147,6 +147,8 @@ export namespace TextureMesh {
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
return {
dGeometryType: ValueCell.create('textureMesh'),
uGeoTexDim: textureMesh.geoTextureDim,
tPosition: textureMesh.vertexTexture,
tGroup: textureMesh.groupTexture,
@@ -165,14 +167,13 @@ export namespace TextureMesh {
...transform,
...BaseGeometry.createValues(props, counts),
dDoubleSided: ValueCell.create(props.doubleSided),
uDoubleSided: ValueCell.create(props.doubleSided),
dFlatShaded: ValueCell.create(props.flatShaded),
dFlipSided: ValueCell.create(props.flipSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
uBumpFrequency: ValueCell.create(props.bumpFrequency),
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
dGeoTexture: ValueCell.create(true),
meta: ValueCell.create(textureMesh.meta),
};
@@ -186,7 +187,7 @@ export namespace TextureMesh {
function updateValues(values: TextureMeshValues, props: PD.Values<Params>) {
BaseGeometry.updateValues(values, props);
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided);
ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded);
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);

View File

@@ -53,17 +53,17 @@ describe('renderer', () => {
scene.commit();
expect(ctx.stats.resourceCounts.attribute).toBe(ctx.isWebGL2 ? 4 : 5);
expect(ctx.stats.resourceCounts.texture).toBe(8);
expect(ctx.stats.resourceCounts.vertexArray).toBe(ctx.extensions.vertexArrayObject ? 8 : 0);
expect(ctx.stats.resourceCounts.program).toBe(8);
expect(ctx.stats.resourceCounts.shader).toBe(16);
expect(ctx.stats.resourceCounts.vertexArray).toBe(ctx.extensions.vertexArrayObject ? 5 : 0);
expect(ctx.stats.resourceCounts.program).toBe(5);
expect(ctx.stats.resourceCounts.shader).toBe(10);
scene.remove(points);
scene.commit();
expect(ctx.stats.resourceCounts.attribute).toBe(0);
expect(ctx.stats.resourceCounts.texture).toBe(0);
expect(ctx.stats.resourceCounts.vertexArray).toBe(0);
expect(ctx.stats.resourceCounts.program).toBe(8);
expect(ctx.stats.resourceCounts.shader).toBe(16);
expect(ctx.stats.resourceCounts.program).toBe(5);
expect(ctx.stats.resourceCounts.shader).toBe(10);
ctx.resources.destroy();
expect(ctx.stats.resourceCounts.program).toBe(0);

View File

@@ -11,7 +11,7 @@ import { Values, TextureSpec } from '../../renderable/schema';
import { Texture } from '../../../mol-gl/webgl/texture';
import { ShaderCode } from '../../../mol-gl/shader-code';
import { ValueCell } from '../../../mol-util';
import { decodeFloatRGB } from '../../../mol-util/float-packing';
import { unpackRGBToInt } from '../../../mol-util/number-packing';
import { QuadSchema, QuadValues } from '../util';
import { quad_vert } from '../../../mol-gl/shader/quad.vert';
import { sum_frag } from '../../../mol-gl/shader/histogram-pyramid/sum.frag';
@@ -96,5 +96,5 @@ export function getHistopyramidSum(ctx: WebGLContext, pyramidTopTexture: Texture
return isWebGL2(gl)
? sumInts[0]
: decodeFloatRGB(sumBytes[0], sumBytes[1], sumBytes[2]);
: unpackRGBToInt(sumBytes[0], sumBytes[1], sumBytes[2]);
}

View File

@@ -39,13 +39,14 @@ const IsosurfaceSchema = {
uGridTransform: UniformSpec('m4'),
uScale: UniformSpec('v2'),
dPackedGroup: DefineSpec('boolean')
dPackedGroup: DefineSpec('boolean'),
dAxisOrder: DefineSpec('string', ['012', '021', '102', '120', '201', '210']),
};
type IsosurfaceValues = Values<typeof IsosurfaceSchema>
const IsosurfaceName = 'isosurface';
function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean): ComputeRenderable<IsosurfaceValues> {
function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3): ComputeRenderable<IsosurfaceValues> {
if (ctx.namedComputeRenderables[IsosurfaceName]) {
const v = ctx.namedComputeRenderables[IsosurfaceName].values as IsosurfaceValues;
@@ -65,15 +66,16 @@ function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture
ValueCell.update(v.uScale, scale);
ValueCell.update(v.dPackedGroup, packedGroup);
ValueCell.updateIfChanged(v.dAxisOrder, axisOrder.join(''));
ctx.namedComputeRenderables[IsosurfaceName].update();
} else {
ctx.namedComputeRenderables[IsosurfaceName] = createIsosurfaceRenderable(ctx, activeVoxelsPyramid, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup);
ctx.namedComputeRenderables[IsosurfaceName] = createIsosurfaceRenderable(ctx, activeVoxelsPyramid, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder);
}
return ctx.namedComputeRenderables[IsosurfaceName];
}
function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean) {
function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3) {
// console.log('uSize', Math.pow(2, levels))
const values: IsosurfaceValues = {
...QuadValues,
@@ -94,7 +96,8 @@ function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Text
uGridTransform: ValueCell.create(transform),
uScale: ValueCell.create(scale),
dPackedGroup: ValueCell.create(packedGroup)
dPackedGroup: ValueCell.create(packedGroup),
dAxisOrder: ValueCell.create(axisOrder.join('')),
};
const schema = { ...IsosurfaceSchema };
@@ -115,7 +118,7 @@ function setRenderingDefaults(ctx: WebGLContext) {
state.clearColor(0, 0, 0, 0);
}
export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
const { drawBuffers } = ctx.extensions;
if (!drawBuffers) throw new Error('need WebGL draw buffers');
@@ -173,7 +176,7 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
groupTexture.attachFramebuffer(framebuffer, 1);
normalTexture.attachFramebuffer(framebuffer, 2);
const renderable = getIsosurfaceRenderable(ctx, pyramidTex, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup);
const renderable = getIsosurfaceRenderable(ctx, pyramidTex, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder);
ctx.state.currentRenderItemId = -1;
framebuffer.bind();
@@ -204,7 +207,7 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
*
* Implementation based on http://www.miaumiau.cat/2016/10/stream-compaction-in-webgl/
*/
export function extractIsosurface(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, gridTexScale: Vec2, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
export function extractIsosurface(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, gridTexScale: Vec2, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
// console.time('calcActiveVoxels');
const activeVoxelsTex = calcActiveVoxels(ctx, volumeData, gridDim, gridTexDim, isoValue, gridTexScale);
// ctx.waitForGpuCommandsCompleteSync();
@@ -216,7 +219,7 @@ export function extractIsosurface(ctx: WebGLContext, volumeData: Texture, gridDi
// console.timeEnd('createHistogramPyramid');
// console.time('createIsosurfaceBuffers');
const gv = createIsosurfaceBuffers(ctx, activeVoxelsTex, volumeData, compacted, gridDim, gridTexDim, transform, isoValue, invert, packedGroup, vertexTexture, groupTexture, normalTexture);
const gv = createIsosurfaceBuffers(ctx, activeVoxelsTex, volumeData, compacted, gridDim, gridTexDim, transform, isoValue, invert, packedGroup, axisOrder, vertexTexture, groupTexture, normalTexture);
// ctx.waitForGpuCommandsCompleteSync();
// console.timeEnd('createIsosurfaceBuffers');

View File

@@ -16,6 +16,7 @@ import { TextValues, TextRenderable } from './renderable/text';
import { TextureMeshValues, TextureMeshRenderable } from './renderable/texture-mesh';
import { ImageValues, ImageRenderable } from './renderable/image';
import { CylindersRenderable, CylindersValues } from './renderable/cylinders';
import { GraphicsRenderVariant } from './webgl/render-item';
const getNextId = idFactory(0, 0x7FFFFFFF);
@@ -48,17 +49,17 @@ export function createRenderObject<T extends RenderObjectType>(type: T, values:
return { id: getNextId(), type, values, state, materialId } as GraphicsRenderObject<T>;
}
export function createRenderable<T extends RenderObjectType>(ctx: WebGLContext, o: GraphicsRenderObject<T>): Renderable<any> {
export function createRenderable<T extends RenderObjectType>(ctx: WebGLContext, o: GraphicsRenderObject<T>, variants: GraphicsRenderVariant[]): Renderable<any> {
switch (o.type) {
case 'mesh': return MeshRenderable(ctx, o.id, o.values as MeshValues, o.state, o.materialId);
case 'points': return PointsRenderable(ctx, o.id, o.values as PointsValues, o.state, o.materialId);
case 'spheres': return SpheresRenderable(ctx, o.id, o.values as SpheresValues, o.state, o.materialId);
case 'cylinders': return CylindersRenderable(ctx, o.id, o.values as CylindersValues, o.state, o.materialId);
case 'text': return TextRenderable(ctx, o.id, o.values as TextValues, o.state, o.materialId);
case 'lines': return LinesRenderable(ctx, o.id, o.values as LinesValues, o.state, o.materialId);
case 'direct-volume': return DirectVolumeRenderable(ctx, o.id, o.values as DirectVolumeValues, o.state, o.materialId);
case 'image': return ImageRenderable(ctx, o.id, o.values as ImageValues, o.state, o.materialId);
case 'texture-mesh': return TextureMeshRenderable(ctx, o.id, o.values as TextureMeshValues, o.state, o.materialId);
case 'mesh': return MeshRenderable(ctx, o.id, o.values as MeshValues, o.state, o.materialId, variants);
case 'points': return PointsRenderable(ctx, o.id, o.values as PointsValues, o.state, o.materialId, variants);
case 'spheres': return SpheresRenderable(ctx, o.id, o.values as SpheresValues, o.state, o.materialId, variants);
case 'cylinders': return CylindersRenderable(ctx, o.id, o.values as CylindersValues, o.state, o.materialId, variants);
case 'text': return TextRenderable(ctx, o.id, o.values as TextValues, o.state, o.materialId, variants);
case 'lines': return LinesRenderable(ctx, o.id, o.values as LinesValues, o.state, o.materialId, variants);
case 'direct-volume': return DirectVolumeRenderable(ctx, o.id, o.values as DirectVolumeValues, o.state, o.materialId, variants);
case 'image': return ImageRenderable(ctx, o.id, o.values as ImageValues, o.state, o.materialId, variants);
case 'texture-mesh': return TextureMeshRenderable(ctx, o.id, o.values as TextureMeshValues, o.state, o.materialId, variants);
}
throw new Error('unsupported type');
}

View File

@@ -22,7 +22,6 @@ export type RenderableState = {
colorOnly: boolean
opaque: boolean
writeDepth: boolean
noClip: boolean
}
export interface Renderable<T extends RenderableValues> {

View File

@@ -6,7 +6,7 @@
import { Renderable, RenderableState, createRenderable } from '../renderable';
import { WebGLContext } from '../webgl/context';
import { createGraphicsRenderItem } from '../webgl/render-item';
import { createGraphicsRenderItem, GraphicsRenderVariant } from '../webgl/render-item';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, Values, InternalSchema, SizeSchema, InternalValues, ElementsSpec, ValueSpec, DefineSpec, GlobalTextureSchema, UniformSpec } from './schema';
import { CylindersShaderCode } from '../shader-code';
import { ValueCell } from '../../mol-util';
@@ -23,7 +23,7 @@ export const CylindersSchema = {
elements: ElementsSpec('uint32'),
padding: ValueSpec('number'),
dDoubleSided: DefineSpec('boolean'),
uDoubleSided: UniformSpec('b'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
uBumpFrequency: UniformSpec('f'),
@@ -32,12 +32,12 @@ export const CylindersSchema = {
export type CylindersSchema = typeof CylindersSchema
export type CylindersValues = Values<CylindersSchema>
export function CylindersRenderable(ctx: WebGLContext, id: number, values: CylindersValues, state: RenderableState, materialId: number): Renderable<CylindersValues> {
export function CylindersRenderable(ctx: WebGLContext, id: number, values: CylindersValues, state: RenderableState, materialId: number, variants: GraphicsRenderVariant[]): Renderable<CylindersValues> {
const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...CylindersSchema };
const internalValues: InternalValues = {
uObjectId: ValueCell.create(id),
};
const shaderCode = CylindersShaderCode;
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId, variants);
return createRenderable(renderItem, values, state);
}

View File

@@ -6,7 +6,7 @@
import { Renderable, RenderableState, createRenderable } from '../renderable';
import { WebGLContext } from '../webgl/context';
import { createGraphicsRenderItem } from '../webgl/render-item';
import { createGraphicsRenderItem, GraphicsRenderVariant } from '../webgl/render-item';
import { AttributeSpec, Values, UniformSpec, GlobalUniformSchema, InternalSchema, TextureSpec, ElementsSpec, DefineSpec, InternalValues, GlobalTextureSchema, BaseSchema } from './schema';
import { DirectVolumeShaderCode } from '../shader-code';
import { ValueCell } from '../../mol-util';
@@ -17,12 +17,6 @@ export const DirectVolumeSchema = {
aPosition: AttributeSpec('float32', 3, 0),
elements: ElementsSpec('uint32'),
uColor: UniformSpec('v3'),
uColorTexDim: UniformSpec('v2'),
tColor: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'),
dColorType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'groupInstance', 'vertex', 'vertexInstance']),
uIsoValue: UniformSpec('v2'),
uBboxMin: UniformSpec('v3'),
uBboxMax: UniformSpec('v3'),
uBboxSize: UniformSpec('v3'),
@@ -31,9 +25,7 @@ export const DirectVolumeSchema = {
uJumpLength: UniformSpec('f'),
uTransform: UniformSpec('m4'),
uGridDim: UniformSpec('v3'),
dRenderMode: DefineSpec('string', ['isosurface', 'volume']),
dSingleLayer: DefineSpec('boolean'),
tTransferTex: TextureSpec('image-uint8', 'rgba', 'ubyte', 'linear'),
tTransferTex: TextureSpec('image-uint8', 'alpha', 'ubyte', 'linear'),
uTransferScale: UniformSpec('f'),
dGridTexType: DefineSpec('string', ['2d', '3d']),
@@ -45,17 +37,15 @@ export const DirectVolumeSchema = {
uCartnToUnit: UniformSpec('m4'),
uUnitToCartn: UniformSpec('m4'),
dPackedGroup: DefineSpec('boolean'),
dAxisOrder: DefineSpec('string', ['012', '021', '102', '120', '201', '210']),
dDoubleSided: DefineSpec('boolean'),
dFlipSided: DefineSpec('boolean'),
dFlatShaded: DefineSpec('boolean'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
};
export type DirectVolumeSchema = typeof DirectVolumeSchema
export type DirectVolumeValues = Values<DirectVolumeSchema>
export function DirectVolumeRenderable(ctx: WebGLContext, id: number, values: DirectVolumeValues, state: RenderableState, materialId: number): Renderable<DirectVolumeValues> {
export function DirectVolumeRenderable(ctx: WebGLContext, id: number, values: DirectVolumeValues, state: RenderableState, materialId: number, variants: GraphicsRenderVariant[]): Renderable<DirectVolumeValues> {
const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...DirectVolumeSchema };
if (!ctx.isWebGL2) {
// workaround for webgl1 limitation that loop counters need to be `const`
@@ -65,6 +55,6 @@ export function DirectVolumeRenderable(ctx: WebGLContext, id: number, values: Di
uObjectId: ValueCell.create(id),
};
const shaderCode = DirectVolumeShaderCode;
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId, variants);
return createRenderable(renderItem, values, state);
}

View File

@@ -6,7 +6,7 @@
import { Renderable, RenderableState, createRenderable } from '../renderable';
import { WebGLContext } from '../webgl/context';
import { createGraphicsRenderItem } from '../webgl/render-item';
import { createGraphicsRenderItem, GraphicsRenderVariant } from '../webgl/render-item';
import { AttributeSpec, Values, GlobalUniformSchema, InternalSchema, TextureSpec, ElementsSpec, DefineSpec, InternalValues, BaseSchema, UniformSpec, GlobalTextureSchema } from './schema';
import { ImageShaderCode } from '../shader-code';
import { ValueCell } from '../../mol-util';
@@ -29,12 +29,12 @@ export const ImageSchema = {
export type ImageSchema = typeof ImageSchema
export type ImageValues = Values<ImageSchema>
export function ImageRenderable(ctx: WebGLContext, id: number, values: ImageValues, state: RenderableState, materialId: number): Renderable<ImageValues> {
export function ImageRenderable(ctx: WebGLContext, id: number, values: ImageValues, state: RenderableState, materialId: number, variants: GraphicsRenderVariant[]): Renderable<ImageValues> {
const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...ImageSchema };
const internalValues: InternalValues = {
uObjectId: ValueCell.create(id),
};
const shaderCode = ImageShaderCode;
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId, variants);
return createRenderable(renderItem, values, state);
}

View File

@@ -6,8 +6,8 @@
import { Renderable, RenderableState, createRenderable } from '../renderable';
import { WebGLContext } from '../webgl/context';
import { createGraphicsRenderItem } from '../webgl/render-item';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, DefineSpec, Values, InternalSchema, SizeSchema, ElementsSpec, InternalValues, GlobalTextureSchema } from './schema';
import { createGraphicsRenderItem, GraphicsRenderVariant } from '../webgl/render-item';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, DefineSpec, Values, InternalSchema, SizeSchema, ElementsSpec, InternalValues, GlobalTextureSchema, UniformSpec } from './schema';
import { ValueCell } from '../../mol-util';
import { LinesShaderCode } from '../shader-code';
@@ -20,19 +20,19 @@ export const LinesSchema = {
aEnd: AttributeSpec('float32', 3, 0),
elements: ElementsSpec('uint32'),
dLineSizeAttenuation: DefineSpec('boolean'),
dDoubleSided: DefineSpec('boolean'),
uDoubleSided: UniformSpec('b'),
dFlipSided: DefineSpec('boolean'),
};
export type LinesSchema = typeof LinesSchema
export type LinesValues = Values<LinesSchema>
export function LinesRenderable(ctx: WebGLContext, id: number, values: LinesValues, state: RenderableState, materialId: number): Renderable<LinesValues> {
export function LinesRenderable(ctx: WebGLContext, id: number, values: LinesValues, state: RenderableState, materialId: number, variants: GraphicsRenderVariant[]): Renderable<LinesValues> {
const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...LinesSchema };
const internalValues: InternalValues = {
uObjectId: ValueCell.create(id),
};
const shaderCode = LinesShaderCode;
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId, variants);
return createRenderable(renderItem, values, state);
}

View File

@@ -6,7 +6,7 @@
import { Renderable, RenderableState, createRenderable } from '../renderable';
import { WebGLContext } from '../webgl/context';
import { createGraphicsRenderItem } from '../webgl/render-item';
import { createGraphicsRenderItem, GraphicsRenderVariant } from '../webgl/render-item';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, ElementsSpec, DefineSpec, Values, InternalSchema, InternalValues, GlobalTextureSchema, ValueSpec, UniformSpec } from './schema';
import { MeshShaderCode } from '../shader-code';
import { ValueCell } from '../../mol-util';
@@ -18,7 +18,7 @@ export const MeshSchema = {
aNormal: AttributeSpec('float32', 3, 0),
elements: ElementsSpec('uint32'),
dFlatShaded: DefineSpec('boolean'),
dDoubleSided: DefineSpec('boolean'),
uDoubleSided: UniformSpec('b'),
dFlipSided: DefineSpec('boolean'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
@@ -29,13 +29,13 @@ export const MeshSchema = {
export type MeshSchema = typeof MeshSchema
export type MeshValues = Values<MeshSchema>
export function MeshRenderable(ctx: WebGLContext, id: number, values: MeshValues, state: RenderableState, materialId: number): Renderable<MeshValues> {
export function MeshRenderable(ctx: WebGLContext, id: number, values: MeshValues, state: RenderableState, materialId: number, variants: GraphicsRenderVariant[]): Renderable<MeshValues> {
const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...MeshSchema };
const internalValues: InternalValues = {
uObjectId: ValueCell.create(id),
};
const shaderCode = MeshShaderCode;
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId, variants);
return createRenderable(renderItem, values, state);
}

View File

@@ -6,7 +6,7 @@
import { Renderable, RenderableState, createRenderable } from '../renderable';
import { WebGLContext } from '../webgl/context';
import { createGraphicsRenderItem } from '../webgl/render-item';
import { createGraphicsRenderItem, GraphicsRenderVariant } from '../webgl/render-item';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, DefineSpec, Values, InternalSchema, SizeSchema, InternalValues, GlobalTextureSchema } from './schema';
import { PointsShaderCode } from '../shader-code';
import { ValueCell } from '../../mol-util';
@@ -22,12 +22,12 @@ export const PointsSchema = {
export type PointsSchema = typeof PointsSchema
export type PointsValues = Values<PointsSchema>
export function PointsRenderable(ctx: WebGLContext, id: number, values: PointsValues, state: RenderableState, materialId: number): Renderable<PointsValues> {
export function PointsRenderable(ctx: WebGLContext, id: number, values: PointsValues, state: RenderableState, materialId: number, variants: GraphicsRenderVariant[]): Renderable<PointsValues> {
const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...PointsSchema };
const internalValues: InternalValues = {
uObjectId: ValueCell.create(id),
};
const shaderCode = PointsShaderCode;
const renderItem = createGraphicsRenderItem(ctx, 'points', shaderCode, schema, { ...values, ...internalValues }, materialId);
const renderItem = createGraphicsRenderItem(ctx, 'points', shaderCode, schema, { ...values, ...internalValues }, materialId, variants);
return createRenderable(renderItem, values, state);
}

View File

@@ -135,12 +135,6 @@ export const GlobalUniformSchema = {
uTransparentBackground: UniformSpec('b'),
uClipObjectType: UniformSpec('i[]'),
uClipObjectInvert: UniformSpec('b[]'),
uClipObjectPosition: UniformSpec('v3[]'),
uClipObjectRotation: UniformSpec('v4[]'),
uClipObjectScale: UniformSpec('v3[]'),
uLightDirection: UniformSpec('v3[]'),
uLightColor: UniformSpec('v3[]'),
uAmbientColor: UniformSpec('v3'),
@@ -161,6 +155,8 @@ export const GlobalUniformSchema = {
uRenderWboit: UniformSpec('b'),
uMarkingDepthTest: UniformSpec('b'),
uMarkingType: UniformSpec('i'),
uPickType: UniformSpec('i'),
} as const;
export type GlobalUniformSchema = typeof GlobalUniformSchema
export type GlobalUniformValues = Values<GlobalUniformSchema>
@@ -186,7 +182,7 @@ export const ColorSchema = {
tColor: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'),
tPalette: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'),
tColorGrid: TextureSpec('texture', 'rgb', 'ubyte', 'linear'),
dColorType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'groupInstance', 'vertex', 'vertexInstance', 'volume', 'volumeInstance']),
dColorType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'groupInstance', 'vertex', 'vertexInstance', 'volume', 'volumeInstance', 'direct']),
dUsePalette: DefineSpec('boolean'),
} as const;
export type ColorSchema = typeof ColorSchema
@@ -207,7 +203,6 @@ export const MarkerSchema = {
uMarker: UniformSpec('f'),
uMarkerTexDim: UniformSpec('v2'),
tMarker: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
dMarkerType: DefineSpec('string', ['uniform', 'groupInstance']),
markerAverage: ValueSpec('number'),
markerStatus: ValueSpec('number'),
} as const;
@@ -255,9 +250,6 @@ export type SubstanceSchema = typeof SubstanceSchema
export type SubstanceValues = Values<SubstanceSchema>
export const ClippingSchema = {
dClipObjectCount: DefineSpec('number'),
dClipVariant: DefineSpec('string', ['instance', 'pixel']),
uClippingTexDim: UniformSpec('v2'),
tClipping: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
dClipping: DefineSpec('boolean'),
@@ -266,6 +258,8 @@ export type ClippingSchema = typeof ClippingSchema
export type ClippingValues = Values<ClippingSchema>
export const BaseSchema = {
dGeometryType: DefineSpec('string', ['cylinders', 'directVolume', 'image', 'lines', 'mesh', 'points', 'spheres', 'text', 'textureMesh']),
...ColorSchema,
...MarkerSchema,
...OverpaintSchema,
@@ -275,6 +269,14 @@ export const BaseSchema = {
dLightCount: DefineSpec('number'),
dClipObjectCount: DefineSpec('number'),
dClipVariant: DefineSpec('string', ['instance', 'pixel']),
uClipObjectType: UniformSpec('i[]'),
uClipObjectInvert: UniformSpec('b[]'),
uClipObjectPosition: UniformSpec('v3[]'),
uClipObjectRotation: UniformSpec('v4[]'),
uClipObjectScale: UniformSpec('v3[]'),
aInstance: AttributeSpec('float32', 1, 1),
/**
* final per-instance transform calculated for instance `i` as

View File

@@ -6,7 +6,7 @@
import { Renderable, RenderableState, createRenderable } from '../renderable';
import { WebGLContext } from '../webgl/context';
import { createGraphicsRenderItem } from '../webgl/render-item';
import { createGraphicsRenderItem, GraphicsRenderVariant } from '../webgl/render-item';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, Values, InternalSchema, SizeSchema, InternalValues, ElementsSpec, ValueSpec, DefineSpec, GlobalTextureSchema, UniformSpec } from './schema';
import { SpheresShaderCode } from '../shader-code';
import { ValueCell } from '../../mol-util';
@@ -20,7 +20,7 @@ export const SpheresSchema = {
elements: ElementsSpec('uint32'),
padding: ValueSpec('number'),
dDoubleSided: DefineSpec('boolean'),
uDoubleSided: UniformSpec('b'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
uBumpFrequency: UniformSpec('f'),
@@ -29,12 +29,12 @@ export const SpheresSchema = {
export type SpheresSchema = typeof SpheresSchema
export type SpheresValues = Values<SpheresSchema>
export function SpheresRenderable(ctx: WebGLContext, id: number, values: SpheresValues, state: RenderableState, materialId: number): Renderable<SpheresValues> {
export function SpheresRenderable(ctx: WebGLContext, id: number, values: SpheresValues, state: RenderableState, materialId: number, variants: GraphicsRenderVariant[]): Renderable<SpheresValues> {
const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...SpheresSchema };
const internalValues: InternalValues = {
uObjectId: ValueCell.create(id),
};
const shaderCode = SpheresShaderCode;
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId, variants);
return createRenderable(renderItem, values, state);
}

View File

@@ -6,7 +6,7 @@
import { Renderable, RenderableState, createRenderable } from '../renderable';
import { WebGLContext } from '../webgl/context';
import { createGraphicsRenderItem } from '../webgl/render-item';
import { createGraphicsRenderItem, GraphicsRenderVariant } from '../webgl/render-item';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, UniformSpec, Values, InternalSchema, SizeSchema, InternalValues, TextureSpec, ElementsSpec, ValueSpec, GlobalTextureSchema } from './schema';
import { TextShaderCode } from '../shader-code';
import { ValueCell } from '../../mol-util';
@@ -35,12 +35,12 @@ export const TextSchema = {
export type TextSchema = typeof TextSchema
export type TextValues = Values<TextSchema>
export function TextRenderable(ctx: WebGLContext, id: number, values: TextValues, state: RenderableState, materialId: number): Renderable<TextValues> {
export function TextRenderable(ctx: WebGLContext, id: number, values: TextValues, state: RenderableState, materialId: number, variants: GraphicsRenderVariant[]): Renderable<TextValues> {
const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...TextSchema };
const internalValues: InternalValues = {
uObjectId: ValueCell.create(id),
};
const shaderCode = TextShaderCode;
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId, variants);
return createRenderable(renderItem, values, state);
}

View File

@@ -6,7 +6,7 @@
import { Renderable, RenderableState, createRenderable } from '../renderable';
import { WebGLContext } from '../webgl/context';
import { createGraphicsRenderItem } from '../webgl/render-item';
import { createGraphicsRenderItem, GraphicsRenderVariant } from '../webgl/render-item';
import { GlobalUniformSchema, BaseSchema, DefineSpec, Values, InternalSchema, InternalValues, UniformSpec, TextureSpec, GlobalTextureSchema, ValueSpec } from './schema';
import { MeshShaderCode } from '../shader-code';
import { ValueCell } from '../../mol-util';
@@ -19,11 +19,10 @@ export const TextureMeshSchema = {
tNormal: TextureSpec('texture', 'rgb', 'float', 'nearest'),
dFlatShaded: DefineSpec('boolean'),
dDoubleSided: DefineSpec('boolean'),
uDoubleSided: UniformSpec('b'),
dFlipSided: DefineSpec('boolean'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
dGeoTexture: DefineSpec('boolean'),
uBumpFrequency: UniformSpec('f'),
uBumpAmplitude: UniformSpec('f'),
meta: ValueSpec('unknown')
@@ -31,13 +30,13 @@ export const TextureMeshSchema = {
export type TextureMeshSchema = typeof TextureMeshSchema
export type TextureMeshValues = Values<TextureMeshSchema>
export function TextureMeshRenderable(ctx: WebGLContext, id: number, values: TextureMeshValues, state: RenderableState, materialId: number): Renderable<TextureMeshValues> {
export function TextureMeshRenderable(ctx: WebGLContext, id: number, values: TextureMeshValues, state: RenderableState, materialId: number, variants: GraphicsRenderVariant[]): Renderable<TextureMeshValues> {
const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...TextureMeshSchema };
const internalValues: InternalValues = {
uObjectId: ValueCell.create(id),
};
const shaderCode = MeshShaderCode;
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId, variants);
return createRenderable(renderItem, values, state);
}

View File

@@ -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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -8,15 +8,13 @@ import { Viewport } from '../mol-canvas3d/camera/util';
import { ICamera } from '../mol-canvas3d/camera';
import { Scene } from './scene';
import { WebGLContext } from './webgl/context';
import { Mat4, Vec3, Vec4, Vec2, Quat } from '../mol-math/linear-algebra';
import { Mat4, Vec3, Vec4, Vec2 } from '../mol-math/linear-algebra';
import { GraphicsRenderable } from './renderable';
import { Color } from '../mol-util/color';
import { ValueCell, deepEqual } from '../mol-util';
import { GlobalUniformValues } from './renderable/schema';
import { GraphicsRenderVariant } from './webgl/render-item';
import { ParamDefinition as PD } from '../mol-util/param-definition';
import { Clipping } from '../mol-theme/clipping';
import { stringToWords } from '../mol-util/string';
import { degToRad } from '../mol-math/misc';
import { createNullTexture, Texture, Textures } from './webgl/texture';
import { arrayMapUpsert } from '../mol-util/array';
@@ -38,15 +36,28 @@ export interface RendererStats {
instancedDrawCount: number
}
export const enum PickType {
None = 0,
Object = 1,
Instance = 2,
Group = 3,
}
export const enum MarkingType {
None = 0,
Depth = 1,
Mask = 2,
}
interface Renderer {
readonly stats: RendererStats
readonly props: Readonly<RendererProps>
clear: (toBackgroundColor: boolean) => void
clear: (toBackgroundColor: boolean, ignoreTransparentBackground?: boolean) => void
clearDepth: () => void
update: (camera: ICamera) => void
renderPick: (group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, depthTexture: Texture | null) => void
renderPick: (group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, depthTexture: Texture | null, pickType: PickType) => void
renderDepth: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderMarkingDepth: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderMarkingMask: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
@@ -78,8 +89,8 @@ export const RendererParams = {
highlightColor: PD.Color(Color.fromNormalizedRgb(1.0, 0.4, 0.6)),
selectColor: PD.Color(Color.fromNormalizedRgb(0.2, 1.0, 0.1)),
highlightStrength: PD.Numeric(0.7, { min: 0.0, max: 1.0, step: 0.1 }),
selectStrength: PD.Numeric(0.7, { min: 0.0, max: 1.0, step: 0.1 }),
highlightStrength: PD.Numeric(0.3, { min: 0.0, max: 1.0, step: 0.1 }),
selectStrength: PD.Numeric(0.3, { min: 0.0, max: 1.0, step: 0.1 }),
markerPriority: PD.Select(1, [[1, 'Highlight'], [2, 'Select']]),
xrayEdgeFalloff: PD.Numeric(1, { min: 0.0, max: 3.0, step: 0.1 }),
@@ -97,20 +108,6 @@ export const RendererParams = {
}] }),
ambientColor: PD.Color(Color.fromNormalizedRgb(1.0, 1.0, 1.0)),
ambientIntensity: PD.Numeric(0.4, { min: 0.0, max: 1.0, step: 0.01 }),
clip: PD.Group({
variant: PD.Select('instance', PD.arrayToOptions<Clipping.Variant>(['instance', 'pixel'])),
objects: PD.ObjectList({
type: PD.Select('plane', PD.objectToOptions(Clipping.Type, t => stringToWords(t))),
invert: PD.Boolean(false),
position: PD.Vec3(Vec3()),
rotation: PD.Group({
axis: PD.Vec3(Vec3.create(1, 0, 0)),
angle: PD.Numeric(0, { min: -180, max: 180, step: 1 }, { description: 'Angle in Degrees' }),
}, { isExpanded: true }),
scale: PD.Vec3(Vec3.create(1, 1, 1)),
}, o => stringToWords(o.type))
})
};
export type RendererProps = PD.Values<typeof RendererParams>
@@ -137,47 +134,11 @@ function getLight(props: RendererProps['light'], light?: Light): Light {
return { count: props.length, direction, color };
}
type Clip = {
variant: Clipping.Variant
objects: {
count: number
type: number[]
invert: boolean[]
position: number[]
rotation: number[]
scale: number[]
}
}
const tmpQuat = Quat();
function getClip(props: RendererProps['clip'], clip?: Clip): Clip {
const { type, invert, position, rotation, scale } = clip?.objects || {
type: (new Array(5)).fill(1),
invert: (new Array(5)).fill(false),
position: (new Array(5 * 3)).fill(0),
rotation: (new Array(5 * 4)).fill(0),
scale: (new Array(5 * 3)).fill(1),
};
for (let i = 0, il = props.objects.length; i < il; ++i) {
const p = props.objects[i];
type[i] = Clipping.Type[p.type];
invert[i] = p.invert;
Vec3.toArray(p.position, position, i * 3);
Quat.toArray(Quat.setAxisAngle(tmpQuat, p.rotation.axis, degToRad(p.rotation.angle)), rotation, i * 4);
Vec3.toArray(p.scale, scale, i * 3);
}
return {
variant: props.variant,
objects: { count: props.objects.length, type, invert, position, rotation, scale }
};
}
namespace Renderer {
export function create(ctx: WebGLContext, props: Partial<RendererProps> = {}): Renderer {
const { gl, state, stats, extensions: { fragDepth } } = ctx;
const { gl, state, stats } = ctx;
const p = PD.merge(RendererParams, PD.getDefaultValues(RendererParams), props);
const light = getLight(p.light);
const clip = getClip(p.clip);
const viewport = Viewport();
const drawingBufferSize = Vec2.create(gl.drawingBufferWidth, gl.drawingBufferHeight);
@@ -232,15 +193,11 @@ namespace Renderer {
uRenderWboit: ValueCell.create(false),
uMarkingDepthTest: ValueCell.create(false),
uPickType: ValueCell.create(PickType.None),
uMarkingType: ValueCell.create(MarkingType.None),
uTransparentBackground: ValueCell.create(false),
uClipObjectType: ValueCell.create(clip.objects.type),
uClipObjectInvert: ValueCell.create(clip.objects.invert),
uClipObjectPosition: ValueCell.create(clip.objects.position),
uClipObjectRotation: ValueCell.create(clip.objects.rotation),
uClipObjectScale: ValueCell.create(clip.objects.scale),
uLightDirection: ValueCell.create(light.direction),
uLightColor: ValueCell.create(light.color),
uAmbientColor: ValueCell.create(ambientColor),
@@ -264,26 +221,11 @@ namespace Renderer {
let globalUniformsNeedUpdate = true;
const renderObject = (r: GraphicsRenderable, variant: GraphicsRenderVariant) => {
if (r.state.disposed || !r.state.visible || (!r.state.pickable && variant[0] === 'p')) {
if (r.state.disposed || !r.state.visible || (!r.state.pickable && variant === 'pick')) {
return;
}
let definesNeedUpdate = false;
if (r.state.noClip) {
if (r.values.dClipObjectCount.ref.value !== 0) {
ValueCell.update(r.values.dClipObjectCount, 0);
definesNeedUpdate = true;
}
} else {
if (r.values.dClipObjectCount.ref.value !== clip.objects.count) {
ValueCell.update(r.values.dClipObjectCount, clip.objects.count);
definesNeedUpdate = true;
}
if (r.values.dClipVariant.ref.value !== clip.variant) {
ValueCell.update(r.values.dClipVariant, clip.variant);
definesNeedUpdate = true;
}
}
if (r.values.dLightCount.ref.value !== light.count) {
ValueCell.update(r.values.dLightCount, light.count);
definesNeedUpdate = true;
@@ -303,9 +245,9 @@ namespace Renderer {
globalUniformsNeedUpdate = false;
}
if (r.values.dRenderMode) { // indicates direct-volume
if ((variant[0] === 'p' || variant === 'depth') && r.values.dRenderMode.ref.value === 'volume') {
return; // no picking/depth in volume mode
if (r.values.dGeometryType.ref.value === 'directVolume') {
if (variant !== 'colorWboit' && variant !== 'colorBlended') {
return; // only color supported
}
// culling done in fragment shader
@@ -314,18 +256,12 @@ namespace Renderer {
if (variant === 'colorBlended') {
// depth test done manually in shader against `depthTexture`
// still need to enable when fragDepth can be used to write depth
if (r.values.dRenderMode.ref.value === 'volume' || !fragDepth) {
state.disable(gl.DEPTH_TEST);
state.depthMask(false);
} else {
state.enable(gl.DEPTH_TEST);
state.depthMask(r.values.uAlpha.ref.value === 1.0);
}
state.disable(gl.DEPTH_TEST);
state.depthMask(false);
}
} else {
if (r.values.dDoubleSided) {
if (r.values.dDoubleSided.ref.value || r.values.hasReflection.ref.value) {
if (r.values.uDoubleSided) {
if (r.values.uDoubleSided.ref.value || r.values.hasReflection.ref.value) {
state.disable(gl.CULL_FACE);
} else {
state.enable(gl.CULL_FACE);
@@ -395,12 +331,13 @@ namespace Renderer {
state.currentRenderItemId = -1;
};
const renderPick = (group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, depthTexture: Texture | null) => {
const renderPick = (group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, depthTexture: Texture | null, pickType: PickType) => {
state.disable(gl.BLEND);
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
updateInternal(group, camera, depthTexture, false, false);
ValueCell.updateIfChanged(globalUniforms.uPickType, pickType);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
@@ -429,13 +366,14 @@ namespace Renderer {
state.depthMask(true);
updateInternal(group, camera, depthTexture, false, false);
ValueCell.updateIfChanged(globalUniforms.uMarkingType, MarkingType.Depth);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i];
if (r.values.markerAverage.ref.value !== 1) {
renderObject(renderables[i], 'markingDepth');
renderObject(renderables[i], 'marking');
}
}
};
@@ -446,13 +384,14 @@ namespace Renderer {
state.depthMask(true);
updateInternal(group, camera, depthTexture, false, !!depthTexture);
ValueCell.updateIfChanged(globalUniforms.uMarkingType, MarkingType.Mask);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i];
if (r.values.markerAverage.ref.value > 0) {
renderObject(renderables[i], 'markingMask');
renderObject(renderables[i], 'marking');
}
}
};
@@ -561,7 +500,7 @@ namespace Renderer {
// TODO: simplify, handle in renderable.state???
// uAlpha is updated in "render" so we need to recompute it here
const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
if (alpha === 1 && r.values.transparencyAverage.ref.value !== 1 && r.values.dRenderMode?.ref.value !== 'volume' && r.values.dPointStyle?.ref.value !== 'fuzzy' && !r.values.dXrayShaded?.ref.value) {
if (alpha === 1 && r.values.transparencyAverage.ref.value !== 1 && r.values.dGeometryType.ref.value !== 'directVolume' && r.values.dPointStyle?.ref.value !== 'fuzzy' && !r.values.dXrayShaded?.ref.value) {
renderObject(r, 'colorWboit');
}
}
@@ -577,20 +516,20 @@ namespace Renderer {
// TODO: simplify, handle in renderable.state???
// uAlpha is updated in "render" so we need to recompute it here
const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
if (alpha < 1 || r.values.transparencyAverage.ref.value > 0 || r.values.dRenderMode?.ref.value === 'volume' || r.values.dPointStyle?.ref.value === 'fuzzy' || !!r.values.uBackgroundColor || r.values.dXrayShaded?.ref.value) {
if (alpha < 1 || r.values.transparencyAverage.ref.value > 0 || r.values.dGeometryType.ref.value === 'directVolume' || r.values.dPointStyle?.ref.value === 'fuzzy' || !!r.values.uBackgroundColor || r.values.dXrayShaded?.ref.value) {
renderObject(r, 'colorWboit');
}
}
};
return {
clear: (toBackgroundColor: boolean) => {
clear: (toBackgroundColor: boolean, ignoreTransparentBackground?: boolean) => {
state.enable(gl.SCISSOR_TEST);
state.enable(gl.DEPTH_TEST);
state.colorMask(true, true, true, true);
state.depthMask(true);
if (transparentBackground) {
if (transparentBackground && !ignoreTransparentBackground) {
state.clearColor(0, 0, 0, 0);
} else if (toBackgroundColor) {
state.clearColor(bgColor[0], bgColor[1], bgColor[2], 1);
@@ -686,15 +625,6 @@ namespace Renderer {
Vec3.scale(ambientColor, Color.toArrayNormalized(p.ambientColor, ambientColor, 0), p.ambientIntensity);
ValueCell.update(globalUniforms.uAmbientColor, ambientColor);
}
if (props.clip !== undefined && !deepEqual(props.clip, p.clip)) {
p.clip = props.clip;
Object.assign(clip, getClip(props.clip, clip));
ValueCell.update(globalUniforms.uClipObjectPosition, clip.objects.position);
ValueCell.update(globalUniforms.uClipObjectRotation, clip.objects.rotation);
ValueCell.update(globalUniforms.uClipObjectScale, clip.objects.scale);
ValueCell.update(globalUniforms.uClipObjectType, clip.objects.type);
}
},
setViewport: (x: number, y: number, width: number, height: number) => {
gl.viewport(x, y, width, height);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
@@ -15,6 +15,7 @@ import { arraySetRemove } from '../mol-util/array';
import { BoundaryHelper } from '../mol-math/geometry/boundary-helper';
import { hash1 } from '../mol-data/util';
import { GraphicsRenderable } from './renderable';
import { GraphicsRenderVariants } from './webgl/render-item';
const boundaryHelper = new BoundaryHelper('98');
@@ -43,8 +44,8 @@ function calculateBoundingSphere(renderables: GraphicsRenderable[], boundingSphe
}
function renderableSort(a: GraphicsRenderable, b: GraphicsRenderable) {
const drawProgramIdA = a.getProgram('colorBlended').id;
const drawProgramIdB = b.getProgram('colorBlended').id;
const drawProgramIdA = (a.getProgram('colorBlended') || a.getProgram('colorWboit')).id;
const drawProgramIdB = (b.getProgram('colorBlended') || a.getProgram('colorWboit')).id;
const materialIdA = a.materialId;
const materialIdB = b.materialId;
@@ -85,7 +86,7 @@ namespace Scene {
readonly renderables: ReadonlyArray<GraphicsRenderable>
}
export function create(ctx: WebGLContext): Scene {
export function create(ctx: WebGLContext, variants = GraphicsRenderVariants): Scene {
const renderableMap = new Map<GraphicsRenderObject, GraphicsRenderable>();
const renderables: GraphicsRenderable[] = [];
const boundingSphere = Sphere3D();
@@ -102,7 +103,7 @@ namespace Scene {
function add(o: GraphicsRenderObject) {
if (!renderableMap.has(o)) {
const renderable = createRenderable(ctx, o);
const renderable = createRenderable(ctx, o, variants);
renderables.push(renderable);
if (o.type === 'direct-volume') {
volumes.push(renderable);

View File

@@ -23,6 +23,7 @@ export interface ShaderExtensions {
}
type FragOutTypes = { [k in number]: 'vec4' | 'ivec4' }
type IgnoreDefine = (name: string, variant: string, defines: ShaderDefines) => boolean
export interface ShaderCode {
readonly id: number
@@ -32,6 +33,7 @@ export interface ShaderCode {
readonly extensions: ShaderExtensions
/** Fragment shader output type only applicable for webgl2 */
readonly outTypes: FragOutTypes
readonly ignoreDefine?: IgnoreDefine
}
import { apply_fog } from './shader/chunks/apply-fog.glsl';
@@ -143,43 +145,64 @@ function preprocess(str: string, defines: ShaderDefines) {
return unrollLoops(replaceCounts(str, defines));
}
export function ShaderCode(name: string, vert: string, frag: string, extensions: ShaderExtensions = {}, outTypes: FragOutTypes = {}): ShaderCode {
return { id: shaderCodeId(), name, vert: addIncludes(vert), frag: addIncludes(frag), extensions, outTypes };
export function ShaderCode(name: string, vert: string, frag: string, extensions: ShaderExtensions = {}, outTypes: FragOutTypes = {}, ignoreDefine?: IgnoreDefine): ShaderCode {
return { id: shaderCodeId(), name, vert: addIncludes(vert), frag: addIncludes(frag), extensions, outTypes, ignoreDefine };
}
// Note: `drawBuffers` need to be 'optional' for wboit
function ignoreDefine(name: string, variant: string, defines: ShaderDefines): boolean {
if (variant.startsWith('color')) {
if (name === 'dLightCount') {
return !!defines.dIgnoreLight?.ref.value;
}
} else {
return [
'dColorType', 'dUsePalette',
'dLightCount',
'dOverpaintType', 'dOverpaint',
'dSubstanceType', 'dSubstance',
].includes(name);
}
return false;
};
function ignoreDefineUnlit(name: string, variant: string, defines: ShaderDefines): boolean {
if (name === 'dLightCount') return true;
return ignoreDefine(name, variant, defines);
};
import { points_vert } from './shader/points.vert';
import { points_frag } from './shader/points.frag';
export const PointsShaderCode = ShaderCode('points', points_vert, points_frag, { drawBuffers: 'optional' });
export const PointsShaderCode = ShaderCode('points', points_vert, points_frag, { drawBuffers: 'optional' }, {}, ignoreDefineUnlit);
import { spheres_vert } from './shader/spheres.vert';
import { spheres_frag } from './shader/spheres.frag';
export const SpheresShaderCode = ShaderCode('spheres', spheres_vert, spheres_frag, { fragDepth: 'required', drawBuffers: 'optional' });
export const SpheresShaderCode = ShaderCode('spheres', spheres_vert, spheres_frag, { fragDepth: 'required', drawBuffers: 'optional' }, {}, ignoreDefine);
import { cylinders_vert } from './shader/cylinders.vert';
import { cylinders_frag } from './shader/cylinders.frag';
export const CylindersShaderCode = ShaderCode('cylinders', cylinders_vert, cylinders_frag, { fragDepth: 'required', drawBuffers: 'optional' });
export const CylindersShaderCode = ShaderCode('cylinders', cylinders_vert, cylinders_frag, { fragDepth: 'required', drawBuffers: 'optional' }, {}, ignoreDefine);
import { text_vert } from './shader/text.vert';
import { text_frag } from './shader/text.frag';
export const TextShaderCode = ShaderCode('text', text_vert, text_frag, { drawBuffers: 'optional' });
export const TextShaderCode = ShaderCode('text', text_vert, text_frag, { drawBuffers: 'optional' }, {}, ignoreDefineUnlit);
import { lines_vert } from './shader/lines.vert';
import { lines_frag } from './shader/lines.frag';
export const LinesShaderCode = ShaderCode('lines', lines_vert, lines_frag, { drawBuffers: 'optional' });
export const LinesShaderCode = ShaderCode('lines', lines_vert, lines_frag, { drawBuffers: 'optional' }, {}, ignoreDefineUnlit);
import { mesh_vert } from './shader/mesh.vert';
import { mesh_frag } from './shader/mesh.frag';
export const MeshShaderCode = ShaderCode('mesh', mesh_vert, mesh_frag, { drawBuffers: 'optional' });
export const MeshShaderCode = ShaderCode('mesh', mesh_vert, mesh_frag, { drawBuffers: 'optional' }, {}, ignoreDefine);
import { directVolume_vert } from './shader/direct-volume.vert';
import { directVolume_frag } from './shader/direct-volume.frag';
export const DirectVolumeShaderCode = ShaderCode('direct-volume', directVolume_vert, directVolume_frag, { fragDepth: 'optional', drawBuffers: 'optional' });
export const DirectVolumeShaderCode = ShaderCode('direct-volume', directVolume_vert, directVolume_frag, { fragDepth: 'optional', drawBuffers: 'optional' }, {}, ignoreDefine);
import { image_vert } from './shader/image.vert';
import { image_frag } from './shader/image.frag';
export const ImageShaderCode = ShaderCode('image', image_vert, image_frag, { drawBuffers: 'optional' });
export const ImageShaderCode = ShaderCode('image', image_vert, image_frag, { drawBuffers: 'optional' }, {}, ignoreDefineUnlit);
//
@@ -187,10 +210,14 @@ export type ShaderDefines = {
[k: string]: ValueCell<DefineType>
}
function getDefinesCode(defines: ShaderDefines) {
function getDefinesCode(defines: ShaderDefines, ignore?: IgnoreDefine) {
if (defines === undefined) return '';
const variant = (defines.dRenderVariant?.ref.value || '') as string;
const lines = [];
for (const name in defines) {
if (ignore?.(name, variant, defines)) continue;
const define = defines[name];
const v = define.ref.value;
if (v !== undefined) {
@@ -288,7 +315,8 @@ function transformGlsl300Frag(frag: string) {
}
export function addShaderDefines(gl: GLRenderingContext, extensions: WebGLExtensions, defines: ShaderDefines, shaders: ShaderCode): ShaderCode {
const header = getDefinesCode(defines);
const vertHeader = getDefinesCode(defines, shaders.ignoreDefine);
const fragHeader = getDefinesCode(defines, shaders.ignoreDefine);
const vertPrefix = isWebGL2(gl) ? glsl300VertPrefix : '';
const fragPrefix = isWebGL2(gl)
? getGlsl300FragPrefix(gl, extensions, shaders.extensions, shaders.outTypes)
@@ -297,8 +325,8 @@ export function addShaderDefines(gl: GLRenderingContext, extensions: WebGLExtens
return {
id: shaderCodeId(),
name: shaders.name,
vert: `${vertPrefix}${header}${preprocess(shaders.vert, defines)}`,
frag: `${fragPrefix}${header}${preprocess(frag, defines)}`,
vert: `${vertPrefix}${vertHeader}${preprocess(shaders.vert, defines)}`,
frag: `${fragPrefix}${fragHeader}${preprocess(frag, defines)}`,
extensions: shaders.extensions,
outTypes: shaders.outTypes
};

View File

@@ -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 Alexander Rose <alexander.rose@weirdbyte.de>
*
@@ -8,59 +8,65 @@
*/
export const apply_light_color = `
#ifdef bumpEnabled
if (uBumpFrequency > 0.0 && uBumpAmplitude > 0.0) {
vec3 bumpNormal = perturbNormal(-vViewPosition, normal, fbm(vModelPosition * uBumpFrequency), (uBumpAmplitude * bumpiness) / uBumpFrequency);
#ifdef enabledFragDepth
if (!any(isNaN(bumpNormal))) normal = bumpNormal;
#else
normal = bumpNormal;
#endif
}
#endif
vec4 color = material;
ReflectedLight reflectedLight = ReflectedLight(vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0));
PhysicalMaterial physicalMaterial;
physicalMaterial.diffuseColor = color.rgb * (1.0 - metalness);
#ifdef enabledFragDepth
physicalMaterial.roughness = min(max(roughness, 0.0525), 1.0);
#ifdef dIgnoreLight
gl_FragColor = material;
#else
vec3 dxy = max(abs(dFdx(normal)), abs(dFdy(normal)));
float geometryRoughness = max(max(dxy.x, dxy.y), dxy.z);
physicalMaterial.roughness = min(max(roughness, 0.0525) + geometryRoughness, 1.0);
#ifdef bumpEnabled
if (uBumpFrequency > 0.0 && uBumpAmplitude > 0.0) {
vec3 bumpNormal = perturbNormal(-vViewPosition, normal, fbm(vModelPosition * uBumpFrequency), (uBumpAmplitude * bumpiness) / uBumpFrequency);
#ifdef enabledFragDepth
if (!isNaN(bumpNormal.x) && !isNaN(bumpNormal.y) && !isNaN(bumpNormal.z)) {
normal = bumpNormal;
}
#else
normal = bumpNormal;
#endif
}
#endif
vec4 color = material;
ReflectedLight reflectedLight = ReflectedLight(vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0));
PhysicalMaterial physicalMaterial;
physicalMaterial.diffuseColor = color.rgb * (1.0 - metalness);
#ifdef enabledFragDepth
physicalMaterial.roughness = min(max(roughness, 0.0525), 1.0);
#else
vec3 dxy = max(abs(dFdx(normal)), abs(dFdy(normal)));
float geometryRoughness = max(max(dxy.x, dxy.y), dxy.z);
physicalMaterial.roughness = min(max(roughness, 0.0525) + geometryRoughness, 1.0);
#endif
physicalMaterial.specularColor = mix(vec3(0.04), color.rgb, metalness);
physicalMaterial.specularF90 = 1.0;
GeometricContext geometry;
geometry.position = -vViewPosition;
geometry.normal = normal;
geometry.viewDir = normalize(vViewPosition);
IncidentLight directLight;
#pragma unroll_loop_start
for (int i = 0; i < dLightCount; ++i) {
directLight.direction = uLightDirection[i];
directLight.color = uLightColor[i] * PI; // * PI for punctual light
RE_Direct_Physical(directLight, geometry, physicalMaterial, reflectedLight);
}
#pragma unroll_loop_end
vec3 irradiance = uAmbientColor * PI; // * PI for punctual light
RE_IndirectDiffuse_Physical(irradiance, geometry, physicalMaterial, reflectedLight);
// indirect specular only metals
vec3 radiance = uAmbientColor * metalness;
vec3 iblIrradiance = uAmbientColor * metalness;
vec3 clearcoatRadiance = vec3(0.0);
RE_IndirectSpecular_Physical(radiance, iblIrradiance, clearcoatRadiance, geometry, physicalMaterial, reflectedLight);
vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular;
gl_FragColor = vec4(outgoingLight, color.a);
#endif
physicalMaterial.specularColor = mix(vec3(0.04), color.rgb, metalness);
physicalMaterial.specularF90 = 1.0;
GeometricContext geometry;
geometry.position = -vViewPosition;
geometry.normal = normal;
geometry.viewDir = normalize(vViewPosition);
IncidentLight directLight;
#pragma unroll_loop_start
for (int i = 0; i < dLightCount; ++i) {
directLight.direction = uLightDirection[i];
directLight.color = uLightColor[i] * PI; // * PI for punctual light
RE_Direct_Physical(directLight, geometry, physicalMaterial, reflectedLight);
}
#pragma unroll_loop_end
vec3 irradiance = uAmbientColor * PI; // * PI for punctual light
RE_IndirectDiffuse_Physical(irradiance, geometry, physicalMaterial, reflectedLight);
// indirect specular only metals
vec3 radiance = uAmbientColor * metalness;
vec3 iblIrradiance = uAmbientColor * metalness;
vec3 clearcoatRadiance = vec3(0.0);
RE_IndirectSpecular_Physical(radiance, iblIrradiance, clearcoatRadiance, geometry, physicalMaterial, reflectedLight);
vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular;
gl_FragColor = vec4(outgoingLight, color.a);
#ifdef dXrayShaded
gl_FragColor.a *= 1.0 - pow(abs(dot(normal, vec3(0.0, 0.0, 1.0))), uXrayEdgeFalloff);

View File

@@ -56,13 +56,13 @@ export const assign_color_varying = `
vSubstance.rgb = mix(vec3(uMetalness, uRoughness, uBumpiness), vSubstance.rgb, vSubstance.a);
#endif
#elif defined(dRenderVariant_pick)
#if defined(dRenderVariant_pickObject)
vColor = vec4(encodeFloatRGB(float(uObjectId)), 1.0);
#elif defined(dRenderVariant_pickInstance)
vColor = vec4(encodeFloatRGB(aInstance), 1.0);
#elif defined(dRenderVariant_pickGroup)
vColor = vec4(encodeFloatRGB(group), 1.0);
#endif
if (uPickType == 1) {
vColor = vec4(packIntToRGB(float(uObjectId)), 1.0);
} else if (uPickType == 2) {
vColor = vec4(packIntToRGB(aInstance), 1.0);
} else {
vColor = vec4(packIntToRGB(group), 1.0);
}
#endif
#ifdef dTransparency

View File

@@ -1,6 +1,6 @@
export const assign_group = `
#ifdef dGeoTexture
float group = decodeFloatRGB(readFromTexture(tGroup, VertexID, uGeoTexDim).rgb);
#ifdef dGeometryType_textureMesh
float group = unpackRGBToInt(readFromTexture(tGroup, VertexID, uGeoTexDim).rgb);
#else
float group = aGroup;
#endif

View File

@@ -1,5 +1,5 @@
export const assign_marker_varying = `
#if defined(dMarkerType_groupInstance)
#if defined(dRenderVariant_color) || defined(dRenderVariant_marking)
vMarker = readFromTexture(tMarker, aInstance * float(uGroupCount) + group, uMarkerTexDim).a;
#endif
`;

View File

@@ -1,10 +1,9 @@
export const assign_material_color = `
#if defined(dRenderVariant_color) || defined(dRenderVariant_marking)
#if defined(dMarkerType_uniform)
float marker = uMarker;
#elif defined(dMarkerType_groupInstance)
float marker = floor(vMarker * 255.0 + 0.5); // rounding required to work on some cards on win
#endif
float marker = uMarker;
if (uMarker == -1.0) {
marker = floor(vMarker * 255.0 + 0.5); // rounding required to work on some cards on win
}
#endif
#if defined(dRenderVariant_color)
@@ -37,27 +36,30 @@ export const assign_material_color = `
#else
vec4 material = packDepthToRGBA(gl_FragCoord.z);
#endif
#elif defined(dRenderVariant_markingDepth)
if (marker > 0.0)
discard;
#ifdef enabledFragDepth
vec4 material = packDepthToRGBA(gl_FragDepthEXT);
#else
vec4 material = packDepthToRGBA(gl_FragCoord.z);
#endif
#elif defined(dRenderVariant_markingMask)
if (marker == 0.0)
discard;
float depthTest = 1.0;
if (uMarkingDepthTest) {
depthTest = (fragmentDepth >= getDepth(gl_FragCoord.xy / uDrawingBufferSize)) ? 1.0 : 0.0;
#elif defined(dRenderVariant_marking)
vec4 material;
if(uMarkingType == 1) {
if (marker > 0.0)
discard;
#ifdef enabledFragDepth
material = packDepthToRGBA(gl_FragDepthEXT);
#else
material = packDepthToRGBA(gl_FragCoord.z);
#endif
} else {
if (marker == 0.0)
discard;
float depthTest = 1.0;
if (uMarkingDepthTest) {
depthTest = (fragmentDepth >= getDepth(gl_FragCoord.xy / uDrawingBufferSize)) ? 1.0 : 0.0;
}
bool isHighlight = intMod(marker, 2.0) > 0.1;
float viewZ = depthToViewZ(uIsOrtho, fragmentDepth, uNear, uFar);
float fogFactor = smoothstep(uFogNear, uFogFar, abs(viewZ));
if (fogFactor == 1.0)
discard;
material = vec4(0.0, depthTest, isHighlight ? 1.0 : 0.0, 1.0 - fogFactor);
}
bool isHighlight = intMod(marker, 2.0) > 0.1;
float viewZ = depthToViewZ(uIsOrtho, fragmentDepth, uNear, uFar);
float fogFactor = smoothstep(uFogNear, uFogFar, abs(viewZ));
if (fogFactor == 1.0)
discard;
vec4 material = vec4(0.0, depthTest, isHighlight ? 1.0 : 0.0, 1.0 - fogFactor);
#endif
// apply screendoor transparency

View File

@@ -1,7 +1,7 @@
export const assign_position = `
mat4 model = uModel * aTransform;
mat4 modelView = uView * model;
#ifdef dGeoTexture
#ifdef dGeometryType_textureMesh
vec3 position = readFromTexture(tPosition, VertexID, uGeoTexDim).xyz;
#else
vec3 position = aPosition;

View File

@@ -4,11 +4,11 @@ export const assign_size = `
#elif defined(dSizeType_attribute)
float size = aSize;
#elif defined(dSizeType_instance)
float size = decodeFloatRGB(readFromTexture(tSize, aInstance, uSizeTexDim).rgb);
float size = unpackRGBToInt(readFromTexture(tSize, aInstance, uSizeTexDim).rgb);
#elif defined(dSizeType_group)
float size = decodeFloatRGB(readFromTexture(tSize, group, uSizeTexDim).rgb);
float size = unpackRGBToInt(readFromTexture(tSize, group, uSizeTexDim).rgb);
#elif defined(dSizeType_groupInstance)
float size = decodeFloatRGB(readFromTexture(tSize, aInstance * float(uGroupCount) + group, uSizeTexDim).rgb);
float size = unpackRGBToInt(readFromTexture(tSize, aInstance * float(uGroupCount) + group, uSizeTexDim).rgb);
#endif
#if defined(dSizeType_instance) || defined(dSizeType_group) || defined(dSizeType_groupInstance)

View File

@@ -14,6 +14,11 @@ uniform float uBumpiness;
varying vec4 vColor;
#endif
#ifdef dUsePalette
uniform sampler2D tPalette;
varying float vPaletteV;
#endif
#ifdef dOverpaint
varying vec4 vOverpaint;
#endif
@@ -33,9 +38,4 @@ uniform float uBumpiness;
varying float vGroup;
varying float vTransparency;
#endif
#ifdef dUsePalette
uniform sampler2D tPalette;
varying float vPaletteV;
#endif
`;

View File

@@ -21,6 +21,10 @@ uniform float uBumpiness;
uniform sampler2D tColorGrid;
#endif
#ifdef dUsePalette
varying float vPaletteV;
#endif
#ifdef dOverpaint
#if defined(dOverpaintType_groupInstance) || defined(dOverpaintType_vertexInstance)
varying vec4 vOverpaint;
@@ -70,8 +74,4 @@ uniform float uBumpiness;
uniform sampler2D tTransparencyGrid;
#endif
#endif
#ifdef dUsePalette
varying float vPaletteV;
#endif
`;

View File

@@ -3,6 +3,9 @@ uniform int uObjectId;
uniform int uInstanceCount;
uniform int uGroupCount;
uniform int uPickType;
uniform int uMarkingType;
#if dClipObjectCount != 0
uniform int uClipObjectType[dClipObjectCount];
uniform bool uClipObjectInvert[dClipObjectCount];
@@ -25,9 +28,8 @@ uniform float uHighlightStrength;
uniform float uSelectStrength;
uniform int uMarkerPriority;
#if defined(dMarkerType_uniform)
#if defined(dRenderVariant_color) || defined(dRenderVariant_marking)
uniform float uMarker;
#elif defined(dMarkerType_groupInstance)
#if __VERSION__ == 100
varying float vMarker;
#else
@@ -52,6 +54,7 @@ uniform float uAlpha;
uniform float uPickingAlphaThreshold;
uniform bool uTransparentBackground;
uniform bool uDoubleSided;
uniform float uInteriorDarkening;
uniform bool uInteriorColorFlag;
uniform vec3 uInteriorColor;

View File

@@ -8,6 +8,9 @@ uniform int uInstanceCount;
uniform int uGroupCount;
uniform vec4 uInvariantBoundingSphere;
uniform bool uDoubleSided;
uniform int uPickType;
#if dClipObjectCount != 0
uniform int uClipObjectType[dClipObjectCount];
uniform bool uClipObjectInvert[dClipObjectCount];
@@ -26,9 +29,8 @@ uniform vec4 uInvariantBoundingSphere;
#endif
#endif
#if defined(dMarkerType_uniform)
#if defined(dRenderVariant_color) || defined(dRenderVariant_marking)
uniform float uMarker;
#elif defined(dMarkerType_groupInstance)
uniform vec2 uMarkerTexDim;
uniform sampler2D tMarker;
#if __VERSION__ == 100

View File

@@ -5,14 +5,6 @@ export const common = `
#define dRenderVariant_color
#endif
#if defined(dRenderVariant_pickObject) || defined(dRenderVariant_pickInstance) || defined(dRenderVariant_pickGroup)
#define dRenderVariant_pick
#endif
#if defined(dRenderVariant_markingDepth) || defined(dRenderVariant_markingMask)
#define dRenderVariant_marking
#endif
#if defined(dColorType_instance) || defined(dColorType_group) || defined(dColorType_groupInstance) || defined(dColorType_vertex) || defined(dColorType_vertexInstance)
#define dColorType_texture
#endif
@@ -33,6 +25,10 @@ export const common = `
#define saturate(a) clamp(a, 0.0, 1.0)
#if __VERSION__ == 100
#define round(x) floor((x) + 0.5)
#endif
float intDiv(const in float a, const in float b) { return float(int(a) / int(b)); }
vec2 ivec2Div(const in vec2 a, const in vec2 b) { return vec2(ivec2(a) / ivec2(b)); }
float intMod(const in float a, const in float b) { return a - b * float(int(a) / int(b)); }
@@ -40,13 +36,8 @@ int imod(const in int a, const in int b) { return a - b * (a / b); }
float pow2(const in float x) { return x * x; }
const float maxFloat = 10000.0; // NOTE constant also set in TypeScript
const float floatLogFactor = 9.210440366976517; // log(maxFloat + 1.0);
float encodeFloatLog(const in float value) { return log(value + 1.0) / floatLogFactor; }
float decodeFloatLog(const in float value) { return exp(value * floatLogFactor) - 1.0; }
vec3 encodeFloatRGB(in float value) {
value = clamp(value, 0.0, 16777216.0 - 1.0) + 1.0;
vec3 packIntToRGB(in float value) {
value = clamp(round(value), 0.0, 16777216.0 - 1.0) + 1.0;
vec3 c = vec3(0.0);
c.b = mod(value, 256.0);
value = floor(value / 256.0);
@@ -55,8 +46,8 @@ vec3 encodeFloatRGB(in float value) {
c.r = mod(value, 256.0);
return c / 255.0;
}
float decodeFloatRGB(const in vec3 rgb) {
return (rgb.r * 256.0 * 256.0 * 255.0 + rgb.g * 256.0 * 255.0 + rgb.b * 255.0) - 1.0;
float unpackRGBToInt(const in vec3 rgb) {
return (floor(rgb.r * 255.0 + 0.5) * 256.0 * 256.0 + floor(rgb.g * 255.0 + 0.5) * 256.0 + floor(rgb.b * 255.0 + 0.5)) - 1.0;
}
vec2 packUnitIntervalToRG(const in float v) {
@@ -215,8 +206,8 @@ float depthToViewZ(const in float isOrtho, const in float linearClipZ, const in
a20 * b03 - a21 * b01 + a22 * b00) / det;
}
#define isNaN(x) ( (x) != (x) )
#define isInf(x) ( (x) == (x)+1. )
#define isNaN(x) ((x) != (x))
#define isInf(x) ((x) == (x) + 1.0)
#else
#define transpose2(m) transpose(m)
#define transpose3(m) transpose(m)

View File

@@ -18,7 +18,7 @@ export const wboit_write = `
float wboitWeight = alpha * clamp(pow(1.0 - fragmentDepth, 2.0), 0.01, 1.0);
gl_FragColor = vec4(gl_FragColor.rgb * alpha * wboitWeight, alpha);
// extra alpha is to handle pre-multiplied alpha
#if !defined(dRenderMode_volume) && !defined(dRenderMode_isosurface)
#ifndef dGeometryType_directVolume
gl_FragData[1] = vec4((uTransparentBackground ? alpha : 1.0) * alpha * wboitWeight);
#else
gl_FragData[1] = vec4(alpha * alpha * wboitWeight);

View File

@@ -35,7 +35,7 @@ uniform float uResolution;
void main() {
vec3 position = readFromTexture(tPosition, SampleID, uGeoTexDim).xyz;
float group = decodeFloatRGB(readFromTexture(tGroup, SampleID, uGeoTexDim).rgb);
float group = unpackRGBToInt(readFromTexture(tGroup, SampleID, uGeoTexDim).rgb);
position = (aTransform * vec4(position, 1.0)).xyz;
gl_PointSize = 7.0;

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -80,7 +80,7 @@ bool CylinderImpostor(
}
}
#ifdef dDoubleSided
if (uDoubleSided) {
// body inside
h = -h;
t = (-k1 - h) / k2;
@@ -92,7 +92,7 @@ bool CylinderImpostor(
}
// TODO: handle inside caps???
#endif
}
return false;
}
@@ -127,13 +127,9 @@ void main() {
#elif defined(dRenderVariant_marking)
gl_FragColor = material;
#elif defined(dRenderVariant_color)
#ifdef dIgnoreLight
gl_FragColor = material;
#else
mat3 normalMatrix = transpose3(inverse3(mat3(uView)));
vec3 normal = normalize(normalMatrix * -normalize(intersection.yzw));
#include apply_light_color
#endif
mat3 normalMatrix = transpose3(inverse3(mat3(uView)));
vec3 normal = normalize(normalMatrix * -normalize(intersection.yzw));
#include apply_light_color
#include apply_interior_color
#include apply_marker_color

View File

@@ -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 Alexander Rose <alexander.rose@weirdbyte.de>
* @author Michael Krone <michael.krone@uni-tuebingen.de>
@@ -38,7 +38,6 @@ varying vec4 vBoundingSphere;
varying mat4 vTransform;
uniform mat4 uInvView;
uniform vec2 uIsoValue;
uniform vec3 uGridDim;
uniform vec3 uBboxSize;
uniform sampler2D tTransferTex;
@@ -57,12 +56,9 @@ uniform float uHighlightStrength;
uniform float uSelectStrength;
uniform int uMarkerPriority;
#if defined(dMarkerType_uniform)
uniform float uMarker;
#elif defined(dMarkerType_groupInstance)
uniform vec2 uMarkerTexDim;
uniform sampler2D tMarker;
#endif
uniform float uMarker;
uniform vec2 uMarkerTexDim;
uniform sampler2D tMarker;
uniform float uMetalness;
uniform float uRoughness;
@@ -79,9 +75,9 @@ uniform float uXrayEdgeFalloff;
uniform float uInteriorDarkening;
uniform bool uInteriorColorFlag;
uniform vec3 uInteriorColor;
bool interior;
uniform bool uRenderWboit;
uniform bool uDoubleSided;
uniform float uNear;
uniform float uFar;
@@ -105,27 +101,22 @@ uniform mat4 uCartnToUnit;
uniform sampler3D tGridTex;
#endif
#if defined(dRenderVariant_color)
#if defined(dColorType_uniform)
uniform vec3 uColor;
#elif defined(dColorType_texture)
uniform vec2 uColorTexDim;
uniform sampler2D tColor;
#endif
#if defined(dColorType_uniform)
uniform vec3 uColor;
#elif defined(dColorType_texture)
uniform vec2 uColorTexDim;
uniform sampler2D tColor;
#endif
#ifdef dOverpaint
#if defined(dOverpaintType_groupInstance) || defined(dOverpaintType_vertexInstance)
uniform vec2 uOverpaintTexDim;
uniform sampler2D tOverpaint;
#endif
#ifdef dOverpaint
#if defined(dOverpaintType_groupInstance) || defined(dOverpaintType_vertexInstance)
uniform vec2 uOverpaintTexDim;
uniform sampler2D tOverpaint;
#endif
#endif
#ifdef dSubstance
#if defined(dSubstanceType_groupInstance) || defined(dSubstanceType_vertexInstance)
uniform vec2 uSubstanceTexDim;
uniform sampler2D tSubstance;
#endif
#endif
#ifdef dUsePalette
uniform sampler2D tPalette;
#endif
#if defined(dGridTexType_2d)
@@ -149,8 +140,8 @@ float calcDepth(const in vec3 pos) {
return 0.5 + 0.5 * clipZW.x / clipZW.y;
}
vec4 transferFunction(float value) {
return texture2D(tTransferTex, vec2(value, 0.0));
float transferFunction(float value) {
return texture2D(tTransferTex, vec2(value, 0.0)).a;
}
float getDepth(const in vec2 coords) {
@@ -175,9 +166,7 @@ vec3 v3m4(vec3 p, mat4 m) {
float preFogAlphaBlended = 0.0;
vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
#if defined(dRenderVariant_color) && !defined(dIgnoreLight)
mat3 normalMatrix = transpose3(inverse3(mat3(uModelView * vTransform)));
#endif
mat3 normalMatrix = transpose3(inverse3(mat3(uModelView * vTransform)));
mat4 cartnToUnit = uCartnToUnit * inverse4(vTransform);
#if defined(dClipVariant_pixel) && dClipObjectCount != 0
mat4 modelTransform = uModel * vTransform * uTransform;
@@ -191,21 +180,18 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
float value = 0.0;
vec4 src = vec4(0.0);
vec4 dst = vec4(0.0);
bool hit = false;
float fragmentDepth;
vec3 posMin = vec3(0.0);
vec3 posMax = vec3(1.0) - vec3(1.0) / uGridDim;
vec3 unitPos;
vec3 isoPos;
vec3 nextPos;
float nextValue;
vec3 color = vec3(0.45, 0.55, 0.8);
vec4 overpaint = vec4(0.0);
vec3 substance = vec3(0.0);
vec4 material;
vec4 overpaint;
float metalness = uMetalness;
float roughness = uRoughness;
@@ -228,7 +214,6 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
if (unitPos.x > posMax.x || unitPos.y > posMax.y || unitPos.z > posMax.z ||
unitPos.x < posMin.x || unitPos.y < posMin.y || unitPos.z < posMin.z
) {
if (hit) break;
prevValue = value;
pos += step;
continue;
@@ -247,226 +232,104 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
}
}
#if defined(dRenderMode_isosurface)
if (prevValue > 0.0 && ( // there was a prev Value
(prevValue < uIsoValue.x && value > uIsoValue.x) || // entering isosurface
(prevValue > uIsoValue.x && value < uIsoValue.x) // leaving isosurface
)) {
isoPos = v3m4(mix(pos - step, pos, ((prevValue - uIsoValue.x) / ((prevValue - uIsoValue.x) - (value - uIsoValue.x)))), cartnToUnit);
vec4 mvPosition = modelViewTransform * vec4(unitPos * uGridDim, 1.0);
if (calcDepth(mvPosition.xyz) > getDepth(gl_FragCoord.xy / uDrawingBufferSize))
break;
vec4 mvPosition = modelViewTransform * vec4(isoPos * uGridDim, 1.0);
#if defined(dClipVariant_pixel) && dClipObjectCount != 0
vec3 vModelPosition = v3m4(isoPos * uGridDim, modelTransform);
if (clipTest(vec4(vModelPosition, 0.0), 0)) {
prevValue = value;
pos += step;
continue;
}
#endif
float depth = calcDepth(mvPosition.xyz);
if (depth > getDepth(gl_FragCoord.xy / uDrawingBufferSize))
break;
#ifdef enabledFragDepth
if (!hit) {
gl_FragDepthEXT = depth;
}
#endif
#if defined(dRenderVariant_pickObject)
return vec4(encodeFloatRGB(float(uObjectId)), 1.0);
#elif defined(dRenderVariant_pickInstance)
return vec4(encodeFloatRGB(vInstance), 1.0);
#elif defined(dRenderVariant_pickGroup)
#ifdef dPackedGroup
return vec4(textureGroup(floor(isoPos * uGridDim + 0.5) / uGridDim).rgb, 1.0);
#else
vec3 g = floor(isoPos * uGridDim + 0.5);
return vec4(encodeFloatRGB(g.z + g.y * uGridDim.z + g.x * uGridDim.z * uGridDim.y), 1.0);
#endif
#elif defined(dRenderVariant_depth)
#ifdef enabledFragDepth
return packDepthToRGBA(gl_FragDepthEXT);
#else
return packDepthToRGBA(depth);
#endif
#elif defined(dRenderVariant_color)
#ifdef dPackedGroup
float group = decodeFloatRGB(textureGroup(floor(isoPos * uGridDim + 0.5) / uGridDim).rgb);
#else
vec3 g = floor(isoPos * uGridDim + 0.5);
float group = g.z + g.y * uGridDim.z + g.x * uGridDim.z * uGridDim.y;
#endif
#if defined(dColorType_uniform)
color = uColor;
#elif defined(dColorType_instance)
color = readFromTexture(tColor, vInstance, uColorTexDim).rgb;
#elif defined(dColorType_group)
color = readFromTexture(tColor, group, uColorTexDim).rgb;
#elif defined(dColorType_groupInstance)
color = readFromTexture(tColor, vInstance * float(uGroupCount) + group, uColorTexDim).rgb;
#elif defined(dColorType_vertex)
color = texture3dFrom1dTrilinear(tColor, isoPos, uGridDim, uColorTexDim, 0.0).rgb;
#elif defined(dColorType_vertexInstance)
color = texture3dFrom1dTrilinear(tColor, isoPos, uGridDim, uColorTexDim, vInstance * float(uVertexCount)).rgb;
#endif
#ifdef dOverpaint
#if defined(dOverpaintType_groupInstance)
overpaint = readFromTexture(tOverpaint, vInstance * float(uGroupCount) + group, uOverpaintTexDim);
#elif defined(dOverpaintType_vertexInstance)
overpaint = texture3dFrom1dTrilinear(tOverpaint, isoPos, uGridDim, uOverpaintTexDim, vInstance * float(uVertexCount));
#endif
color = mix(color, overpaint.rgb, overpaint.a);
#endif
// handle flipping and negative isosurfaces
#ifdef dFlipSided
bool flipped = value < uIsoValue.y; // flipped
#else
bool flipped = value > uIsoValue.y;
#endif
interior = value < uIsoValue.x && flipped;
#ifndef dDoubleSided
if (interior) {
prevValue = value;
pos += step;
continue;
}
#endif
vec3 vViewPosition = mvPosition.xyz;
vec4 material = vec4(color, uAlpha);
#ifdef dIgnoreLight
gl_FragColor = material;
#else
#if defined(dFlatShaded)
// nearest grid point
isoPos = floor(isoPos * uGridDim + 0.5) / uGridDim;
#endif
#ifdef dPackedGroup
// compute gradient by central differences
gradient.x = textureVal(isoPos - dx).a - textureVal(isoPos + dx).a;
gradient.y = textureVal(isoPos - dy).a - textureVal(isoPos + dy).a;
gradient.z = textureVal(isoPos - dz).a - textureVal(isoPos + dz).a;
#else
gradient = textureVal(isoPos).xyz * 2.0 - 1.0;
#endif
vec3 normal = -normalize(normalMatrix * normalize(gradient));
normal = normal * (float(flipped) * 2.0 - 1.0);
normal = normal * -(float(interior) * 2.0 - 1.0);
#ifdef dSubstance
#if defined(dSubstanceType_groupInstance)
substance = readFromTexture(tSubstance, vInstance * float(uGroupCount) + group, uSubstanceTexDim).rgb;
#elif defined(dSubstanceType_vertexInstance)
substance = texture3dFrom1dTrilinear(tSubstance, isoPos, uGridDim, uSubstanceTexDim, vInstance * float(uVertexCount)).rgb;
#endif
metalness = mix(metalness, substance.r, substance.b);
roughness = mix(roughness, substance.g, substance.b);
#endif
#include apply_light_color
#endif
#if defined(dMarkerType_uniform)
float marker = uMarker;
#elif defined(dMarkerType_groupInstance)
float marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
marker = floor(marker * 255.0 + 0.5); // rounding required to work on some cards on win
#endif
#include apply_interior_color
#include apply_marker_color
preFogAlphaBlended = (1.0 - preFogAlphaBlended) * gl_FragColor.a + preFogAlphaBlended;
fragmentDepth = depth;
#include apply_fog
src = gl_FragColor;
if (!uTransparentBackground) {
// done in 'apply_fog' otherwise
src.rgb *= src.a;
}
dst = (1.0 - dst.a) * src + dst; // standard blending
#endif
#ifdef dSingleLayer
break;
#endif
hit = true;
#if defined(dClipVariant_pixel) && dClipObjectCount != 0
vec3 vModelPosition = v3m4(unitPos * uGridDim, modelTransform);
if (clipTest(vec4(vModelPosition, 0.0), 0)) {
prevValue = value;
pos += step;
continue;
}
prevValue = value;
#elif defined(dRenderMode_volume)
vec4 mvPosition = modelViewTransform * vec4(unitPos * uGridDim, 1.0);
if (calcDepth(mvPosition.xyz) > getDepth(gl_FragCoord.xy / uDrawingBufferSize))
break;
#endif
#if defined(dClipVariant_pixel) && dClipObjectCount != 0
vec3 vModelPosition = v3m4(unitPos * uGridDim, modelTransform);
if (clipTest(vec4(vModelPosition, 0.0), 0)) {
prevValue = value;
pos += step;
continue;
}
#endif
vec3 vViewPosition = mvPosition.xyz;
material.a = transferFunction(value);
#if defined(dRenderVariant_color)
vec3 vViewPosition = mvPosition.xyz;
vec4 material = transferFunction(value);
#ifdef dIgnoreLight
gl_FragColor.rgb = material.rgb;
#else
if (material.a >= 0.01) {
#ifdef dPackedGroup
// compute gradient by central differences
gradient.x = textureVal(unitPos - dx).a - textureVal(unitPos + dx).a;
gradient.y = textureVal(unitPos - dy).a - textureVal(unitPos + dy).a;
gradient.z = textureVal(unitPos - dz).a - textureVal(unitPos + dz).a;
#else
gradient = cell.xyz * 2.0 - 1.0;
#endif
vec3 normal = -normalize(normalMatrix * normalize(gradient));
#include apply_light_color
} else {
gl_FragColor.rgb = material.rgb;
}
#endif
gl_FragColor.a = material.a * uAlpha * uTransferScale;
#if defined(dMarkerType_uniform)
float marker = uMarker;
#elif defined(dMarkerType_groupInstance)
#ifdef dPackedGroup
float group = decodeFloatRGB(textureGroup(floor(unitPos * uGridDim + 0.5) / uGridDim).rgb);
#else
vec3 g = floor(unitPos * uGridDim + 0.5);
float group = g.z + g.y * uGridDim.z + g.x * uGridDim.z * uGridDim.y;
#endif
float marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
marker = floor(marker * 255.0 + 0.5); // rounding required to work on some cards on win
#endif
#include apply_marker_color
preFogAlphaBlended = (1.0 - preFogAlphaBlended) * gl_FragColor.a + preFogAlphaBlended;
fragmentDepth = calcDepth(mvPosition.xyz);
#include apply_fog
src = gl_FragColor;
if (!uTransparentBackground) {
// done in 'apply_fog' otherwise
src.rgb *= src.a;
}
dst = (1.0 - dst.a) * src + dst; // standard blending
#ifdef dPackedGroup
float group = unpackRGBToInt(textureGroup(floor(unitPos * uGridDim + 0.5) / uGridDim).rgb);
#else
vec3 g = floor(unitPos * uGridDim + 0.5);
// note that we swap x and z because the texture is flipped around y
#if defined(dAxisOrder_012)
float group = g.z + g.y * uGridDim.z + g.x * uGridDim.z * uGridDim.y; // 210
#elif defined(dAxisOrder_021)
float group = g.y + g.z * uGridDim.y + g.x * uGridDim.y * uGridDim.z; // 120
#elif defined(dAxisOrder_102)
float group = g.z + g.x * uGridDim.z + g.y * uGridDim.z * uGridDim.x; // 201
#elif defined(dAxisOrder_120)
float group = g.x + g.z * uGridDim.x + g.y * uGridDim.x * uGridDim.z; // 021
#elif defined(dAxisOrder_201)
float group = g.y + g.x * uGridDim.y + g.z * uGridDim.y * uGridDim.x; // 102
#elif defined(dAxisOrder_210)
float group = g.x + g.y * uGridDim.x + g.z * uGridDim.x * uGridDim.y; // 012
#endif
#endif
#if defined(dColorType_direct) && defined(dUsePalette)
material.rgb = texture2D(tPalette, vec2(value, 0.0)).rgb;
#elif defined(dColorType_uniform)
material.rgb = uColor;
#elif defined(dColorType_instance)
material.rgb = readFromTexture(tColor, vInstance, uColorTexDim).rgb;
#elif defined(dColorType_group)
material.rgb = readFromTexture(tColor, group, uColorTexDim).rgb;
#elif defined(dColorType_groupInstance)
material.rgb = readFromTexture(tColor, vInstance * float(uGroupCount) + group, uColorTexDim).rgb;
#elif defined(dColorType_vertex)
material.rgb = texture3dFrom1dTrilinear(tColor, isoPos, uGridDim, uColorTexDim, 0.0).rgb;
#elif defined(dColorType_vertexInstance)
material.rgb = texture3dFrom1dTrilinear(tColor, isoPos, uGridDim, uColorTexDim, vInstance * float(uVertexCount)).rgb;
#endif
#ifdef dOverpaint
#if defined(dOverpaintType_groupInstance)
overpaint = readFromTexture(tOverpaint, vInstance * float(uGroupCount) + group, uOverpaintTexDim);
#elif defined(dOverpaintType_vertexInstance)
overpaint = texture3dFrom1dTrilinear(tOverpaint, isoPos, uGridDim, uOverpaintTexDim, vInstance * float(uVertexCount));
#endif
material.rgb = mix(material.rgb, overpaint.rgb, overpaint.a);
#endif
if (material.a >= 0.01) {
#ifdef dPackedGroup
// compute gradient by central differences
gradient.x = textureVal(unitPos - dx).a - textureVal(unitPos + dx).a;
gradient.y = textureVal(unitPos - dy).a - textureVal(unitPos + dy).a;
gradient.z = textureVal(unitPos - dz).a - textureVal(unitPos + dz).a;
#else
gradient = cell.xyz * 2.0 - 1.0;
#endif
vec3 normal = -normalize(normalMatrix * normalize(gradient));
#include apply_light_color
} else {
gl_FragColor.rgb = material.rgb;
}
gl_FragColor.a = material.a * uAlpha * uTransferScale;
float marker = uMarker;
if (uMarker == -1.0) {
marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
marker = floor(marker * 255.0 + 0.5); // rounding required to work on some cards on win
}
#include apply_marker_color
preFogAlphaBlended = (1.0 - preFogAlphaBlended) * gl_FragColor.a + preFogAlphaBlended;
fragmentDepth = calcDepth(mvPosition.xyz);
#include apply_fog
src = gl_FragColor;
if (!uTransparentBackground) {
// done in 'apply_fog' otherwise
src.rgb *= src.a;
}
dst = (1.0 - dst.a) * src + dst; // standard blending
// break if the color is opaque enough
if (dst.a > 0.95)
break;
@@ -474,12 +337,6 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
pos += step;
}
#if defined(dRenderMode_isosurface) && defined(enabledFragDepth)
// ensure depth is written everywhere
if (!hit)
gl_FragDepthEXT = 1.0;
#endif
return dst;
}
@@ -490,21 +347,6 @@ void main() {
if (gl_FrontFacing)
discard;
#ifdef dRenderVariant_marking
// not supported
discard;
#endif
#if defined(dRenderVariant_pick) || defined(dRenderVariant_depth)
#if defined(dRenderMode_volume)
// always ignore pick & depth for volume
discard;
#elif defined(dRenderMode_isosurface)
if (uAlpha < uPickingAlphaThreshold)
discard; // ignore so the element below can be picked
#endif
#endif
vec3 rayDir = mix(normalize(vOrigPos - uCameraPosition), uCameraDir, uIsOrtho);
vec3 step = rayDir * uStepScale;
@@ -513,21 +355,9 @@ void main() {
vec3 start = mix(uCameraPosition, vOrigPos, uIsOrtho) + (d * rayDir);
gl_FragColor = raymarch(start, step, rayDir);
#if defined(dRenderVariant_pick) || defined(dRenderVariant_depth)
// discard when nothing was hit
if (gl_FragColor == vec4(0.0))
discard;
#endif
#if defined(dRenderVariant_color)
#if defined(dRenderMode_isosurface) && defined(enabledFragDepth)
float fragmentDepth = gl_FragDepthEXT;
#else
float fragmentDepth = calcDepth((uModelView * vec4(start, 1.0)).xyz);
#endif
float preFogAlpha = clamp(preFogAlphaBlended, 0.0, 1.0);
interior = false;
#include wboit_write
#endif
float fragmentDepth = calcDepth((uModelView * vec4(start, 1.0)).xyz);
float preFogAlpha = clamp(preFogAlphaBlended, 0.0, 1.0);
bool interior = false;
#include wboit_write
}
`;

View File

@@ -51,7 +51,7 @@ void main() {
#endif
if (dist * uRadiusFactorInv > minDistance + uResolution * 0.05)
discard;
gl_FragColor.rgb = encodeFloatRGB(vGroup);
gl_FragColor.rgb = packIntToRGB(vGroup);
#endif
}
`;

View File

@@ -33,12 +33,12 @@ void main(void) {
c = texture2D(tInputLevel, position + vec2(0.0, k)).r * 255.0;
d = texture2D(tInputLevel, position + vec2(k, k)).r * 255.0;
} else {
a = decodeFloatRGB(texture2D(tPreviousLevel, position).rgb);
b = decodeFloatRGB(texture2D(tPreviousLevel, position + vec2(k, 0.0)).rgb);
c = decodeFloatRGB(texture2D(tPreviousLevel, position + vec2(0.0, k)).rgb);
d = decodeFloatRGB(texture2D(tPreviousLevel, position + vec2(k, k)).rgb);
a = unpackRGBToInt(texture2D(tPreviousLevel, position).rgb);
b = unpackRGBToInt(texture2D(tPreviousLevel, position + vec2(k, 0.0)).rgb);
c = unpackRGBToInt(texture2D(tPreviousLevel, position + vec2(0.0, k)).rgb);
d = unpackRGBToInt(texture2D(tPreviousLevel, position + vec2(k, k)).rgb);
}
gl_FragColor = vec4(encodeFloatRGB(a + b + c + d), 1.0);
gl_FragColor = vec4(packIntToRGB(a + b + c + d), 1.0);
#else
int a, b, c, d;

View File

@@ -104,30 +104,29 @@ void main() {
#if defined(dRenderVariant_pick)
if (imageData.a < 0.3)
discard;
#if defined(dRenderVariant_pickObject)
gl_FragColor = vec4(encodeFloatRGB(float(uObjectId)), 1.0);
#elif defined(dRenderVariant_pickInstance)
gl_FragColor = vec4(encodeFloatRGB(vInstance), 1.0);
#elif defined(dRenderVariant_pickGroup)
if (uPickType == 1) {
gl_FragColor = vec4(packIntToRGB(float(uObjectId)), 1.0);
} else if (uPickType == 2) {
gl_FragColor = vec4(packIntToRGB(vInstance), 1.0);
} else {
gl_FragColor = vec4(texture2D(tGroupTex, vUv).rgb, 1.0);
#endif
}
#elif defined(dRenderVariant_depth)
if (imageData.a < 0.05)
discard;
gl_FragColor = packDepthToRGBA(gl_FragCoord.z);
#elif defined(dRenderVariant_marking)
#if defined(dMarkerType_uniform)
float marker = uMarker;
#elif defined(dMarkerType_groupInstance)
float group = decodeFloatRGB(texture2D(tGroupTex, vUv).rgb);
float marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
float marker = uMarker;
if (uMarker == -1.0) {
float group = unpackRGBToInt(texture2D(tGroupTex, vUv).rgb);
marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
marker = floor(marker * 255.0 + 0.5); // rounding required to work on some cards on win
#endif
#if defined(dRenderVariant_markingDepth)
}
if (uMarkingType == 1) {
if (marker > 0.0 || imageData.a < 0.05)
discard;
gl_FragColor = packDepthToRGBA(gl_FragCoord.z);
#elif defined(dRenderVariant_markingMask)
} else {
if (marker == 0.0 || imageData.a < 0.05)
discard;
float depthTest = 1.0;
@@ -136,20 +135,19 @@ void main() {
}
bool isHighlight = intMod(marker, 2.0) > 0.1;
gl_FragColor = vec4(0.0, depthTest, isHighlight ? 1.0 : 0.0, 1.0);
#endif
}
#elif defined(dRenderVariant_color)
if (imageData.a < 0.05)
discard;
gl_FragColor = imageData;
gl_FragColor.a *= uAlpha;
#if defined(dMarkerType_uniform)
float marker = uMarker;
#elif defined(dMarkerType_groupInstance)
float group = decodeFloatRGB(texture2D(tGroupTex, vUv).rgb);
float marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
float marker = uMarker;
if (uMarker == -1.0) {
float group = unpackRGBToInt(texture2D(tGroupTex, vUv).rgb);
marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
marker = floor(marker * 255.0 + 0.5); // rounding required to work on some cards on win
#endif
}
#include apply_marker_color
#include apply_fog

View File

@@ -74,7 +74,7 @@ int idot4(const in ivec4 a, const in ivec4 b) {
#if __VERSION__ == 100
int pyramidVoxel(vec2 pos) {
return int(decodeFloatRGB(texture2D(tActiveVoxelsPyramid, pos / (vec2(1.0, 0.5) * uSize)).rgb));
return int(unpackRGBToInt(texture2D(tActiveVoxelsPyramid, pos / (vec2(1.0, 0.5) * uSize)).rgb));
}
#else
int pyramidVoxel(vec2 pos) {
@@ -86,6 +86,25 @@ vec4 baseVoxel(vec2 pos) {
return texture2D(tActiveVoxelsBase, pos / uSize);
}
vec4 getGroup(const in vec3 p) {
vec3 gridDim = uGridDim - vec3(1.0, 1.0, 0.0); // remove xy padding
// note that we swap x and z because the texture is flipped around y
#if defined(dAxisOrder_012)
float group = p.z + p.y * gridDim.z + p.x * gridDim.z * gridDim.y; // 210
#elif defined(dAxisOrder_021)
float group = p.y + p.z * gridDim.y + p.x * gridDim.y * gridDim.z; // 120
#elif defined(dAxisOrder_102)
float group = p.z + p.x * gridDim.z + p.y * gridDim.z * gridDim.x; // 201
#elif defined(dAxisOrder_120)
float group = p.x + p.z * gridDim.x + p.y * gridDim.x * gridDim.z; // 021
#elif defined(dAxisOrder_201)
float group = p.y + p.x * gridDim.y + p.z * gridDim.y * gridDim.x; // 102
#elif defined(dAxisOrder_210)
float group = p.x + p.y * gridDim.x + p.z * gridDim.x * gridDim.y; // 012
#endif
return vec4(group > 16777215.5 ? vec3(1.0) : packIntToRGB(group), 1.0);
}
void main(void) {
// get 1D index
int vI = int(gl_FragCoord.x) + int(gl_FragCoord.y) * int(uSize);
@@ -255,18 +274,13 @@ void main(void) {
#ifdef dPackedGroup
gl_FragData[1] = vec4(voxel(coord3d).rgb, 1.0);
#else
vec3 gridDim = uGridDim - vec3(1.0, 1.0, 0.0); // remove xy padding
float group = coord3d.z + coord3d.y * gridDim.z + coord3d.x * gridDim.z * gridDim.y;
gl_FragData[1] = vec4(group > 16777215.5 ? vec3(1.0) : encodeFloatRGB(group), 1.0);
gl_FragData[1] = getGroup(coord3d);
#endif
#else
#ifdef dPackedGroup
gl_FragData[1] = vec4(t < 0.5 ? d0.rgb : d1.rgb, 1.0);
#else
vec3 b = t < 0.5 ? b0 : b1;
vec3 gridDim = uGridDim - vec3(1.0, 1.0, 0.0); // remove xy padding
float group = b.z + b.y * gridDim.z + b.x * gridDim.z * gridDim.y;
gl_FragData[1] = vec4(group > 16777215.5 ? vec3(1.0) : encodeFloatRGB(group), 1.0);
gl_FragData[1] = getGroup(t < 0.5 ? b0 : b1);
#endif
#endif

View File

@@ -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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -43,19 +43,13 @@ void main() {
#elif defined(dRenderVariant_marking)
gl_FragColor = material;
#elif defined(dRenderVariant_color)
#ifdef dIgnoreLight
gl_FragColor = material;
#if defined(dFlatShaded)
vec3 normal = -faceNormal;
#else
#if defined(dFlatShaded)
vec3 normal = -faceNormal;
#else
vec3 normal = -normalize(vNormal);
#ifdef dDoubleSided
normal = normal * (float(frontFacing) * 2.0 - 1.0);
#endif
#endif
#include apply_light_color
vec3 normal = -normalize(vNormal);
if (uDoubleSided) normal *= float(frontFacing) * 2.0 - 1.0;
#endif
#include apply_light_color
#include apply_interior_color
#include apply_marker_color

View File

@@ -16,7 +16,7 @@ precision highp sampler2D;
#include common_clip
#include texture3d_from_2d_linear
#ifdef dGeoTexture
#ifdef dGeometryType_textureMesh
uniform vec2 uGeoTexDim;
uniform sampler2D tPosition;
uniform sampler2D tGroup;
@@ -39,15 +39,17 @@ void main(){
#include assign_color_varying
#include clip_instance
#ifdef dGeoTexture
#ifdef dGeometryType_textureMesh
vec3 normal = readFromTexture(tNormal, VertexID, uGeoTexDim).xyz;
#else
vec3 normal = aNormal;
#endif
mat3 normalMatrix = transpose3(inverse3(mat3(modelView)));
vec3 transformedNormal = normalize(normalMatrix * normalize(normal));
#if defined(dFlipSided) && !defined(dDoubleSided) // TODO checking dDoubleSided should not be required, ASR
transformedNormal = -transformedNormal;
#if defined(dFlipSided)
if (!uDoubleSided) { // TODO checking uDoubleSided should not be required, ASR
transformedNormal = -transformedNormal;
}
#endif
vNormal = transformedNormal;
}

View File

@@ -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>
*/
@@ -66,10 +66,9 @@ void main(void){
#include clip_pixel
bool flag = Impostor(cameraPos, cameraNormal);
#ifndef dDoubleSided
if (interior)
discard;
#endif
if (!uDoubleSided) {
if (interior) discard;
}
vec3 vViewPosition = cameraPos;
gl_FragDepthEXT = calcDepth(vViewPosition);
@@ -93,12 +92,8 @@ void main(void){
#elif defined(dRenderVariant_marking)
gl_FragColor = material;
#elif defined(dRenderVariant_color)
#ifdef dIgnoreLight
gl_FragColor = material;
#else
vec3 normal = -cameraNormal;
#include apply_light_color
#endif
vec3 normal = -cameraNormal;
#include apply_light_color
#include apply_interior_color
#include apply_marker_color

View File

@@ -49,13 +49,15 @@ export interface RenderItem<T extends string> {
//
const GraphicsRenderVariant = { 'colorBlended': '', 'colorWboit': '', 'pickObject': '', 'pickInstance': '', 'pickGroup': '', 'depth': '', 'markingDepth': '', 'markingMask': '' };
const GraphicsRenderVariant = { colorBlended: '', colorWboit: '', pick: '', depth: '', marking: '' };
export type GraphicsRenderVariant = keyof typeof GraphicsRenderVariant
const GraphicsRenderVariants = Object.keys(GraphicsRenderVariant) as GraphicsRenderVariant[];
export const GraphicsRenderVariants = Object.keys(GraphicsRenderVariant) as GraphicsRenderVariant[];
export const GraphicsRenderVariantsBlended = GraphicsRenderVariants.filter(v => v !== 'colorWboit');
export const GraphicsRenderVariantsWboit = GraphicsRenderVariants.filter(v => v !== 'colorBlended');
const ComputeRenderVariant = { 'compute': '' };
const ComputeRenderVariant = { compute: '' };
export type ComputeRenderVariant = keyof typeof ComputeRenderVariant
const ComputeRenderVariants = Object.keys(ComputeRenderVariant) as ComputeRenderVariant[];
export const ComputeRenderVariants = Object.keys(ComputeRenderVariant) as ComputeRenderVariant[];
function createProgramVariant(ctx: WebGLContext, variant: string, defineValues: DefineValues, shaderCode: ShaderCode, schema: RenderableSchema) {
defineValues = { ...defineValues, dRenderVariant: ValueCell.create(variant) };
@@ -90,8 +92,8 @@ function resetValueChanges(valueChanges: ValueChanges) {
//
export type GraphicsRenderItem = RenderItem<GraphicsRenderVariant>
export function createGraphicsRenderItem(ctx: WebGLContext, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues, materialId: number) {
return createRenderItem(ctx, drawMode, shaderCode, schema, values, materialId, GraphicsRenderVariants);
export function createGraphicsRenderItem(ctx: WebGLContext, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues, materialId: number, variants: GraphicsRenderVariant[]) {
return createRenderItem(ctx, drawMode, shaderCode, schema, values, materialId, variants);
}
export type ComputeRenderItem = RenderItem<ComputeRenderVariant>

View File

@@ -105,7 +105,12 @@ export function createResources(gl: GLRenderingContext, state: WebGLState, stats
const programCache = createReferenceCache(
(props: ProgramProps) => {
const array = [props.shaderCode.id];
Object.keys(props.defineValues).forEach(k => array.push(hashString(k), defineValueHash(props.defineValues[k].ref.value)));
const variant = (props.defineValues.dRenderVariant?.ref.value || '') as string;
Object.keys(props.defineValues).forEach(k => {
if (!props.shaderCode.ignoreDefine?.(k, variant, props.defineValues)) {
array.push(hashString(k), defineValueHash(props.defineValues[k].ref.value));
}
});
return hashFnv32a(array).toString();
},
(props: ProgramProps) => wrap('program', createProgram(gl, state, extensions, getShader, props)),

View File

@@ -1,5 +1,12 @@
/**
* Copyright (c) 2019-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>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
*/
import { parseMol } from '../mol/parser';
import { parseMol, formalChargeMapper } from '../mol/parser';
const MolString = `2244
-OEChem-04072009073D
@@ -49,6 +56,48 @@ const MolString = `2244
13 20 1 0 0 0 0
M END`;
const MolStringWithAtomBlockCharge = `
Ketcher 1 72215442D 1 1.00000 0.00000 0
4 3 0 0 0 0 999 V2000
0.0000 0.0000 0.0000 C 0 1 0 0 0 0 0 0 0 0 0 0
0.8660 0.5000 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0
-0.8660 0.5000 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0
0.0000 -1.0000 0.0000 P 0 0 0 0 0 0 0 0 0 0 0 0
1 4 2 0 0 0 0
3 1 1 0 0 0 0
2 1 1 0 0 0 0
M END`;
const MolStringWithPropertyBlockCharge = `
Ketcher 1 72215442D 1 1.00000 0.00000 0
4 3 0 0 0 0 999 V2000
0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
0.8660 0.5000 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0
-0.8660 0.5000 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0
0.0000 -1.0000 0.0000 P 0 0 0 0 0 0 0 0 0 0 0 0
1 4 2 0 0 0 0
3 1 1 0 0 0 0
2 1 1 0 0 0 0
M CHG 3 2 -1 3 1 4 1
M END`;
const MolStringWithMultipleChargeLines = `
Ketcher 1 72215442D 1 1.00000 0.00000 0
4 3 0 0 0 0 999 V2000
0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
0.8660 0.5000 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0
-0.8660 0.5000 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0
0.0000 -1.0000 0.0000 P 0 0 0 0 0 0 0 0 0 0 0 0
1 4 2 0 0 0 0
3 1 1 0 0 0 0
2 1 1 0 0 0 0
M CHG 1 2 -1
M CHG 2 3 1 4 1
M END`;
describe('mol reader', () => {
it('basic', async () => {
const parsed = await parseMol(MolString).run();
@@ -70,4 +119,63 @@ describe('mol reader', () => {
expect(bonds.atomIdxB.value(20)).toBe(20);
expect(bonds.order.value(20)).toBe(1);
});
it('property block charges', async () => {
const parsed = await parseMol(MolStringWithPropertyBlockCharge).run();
if (parsed.isError) {
throw new Error(parsed.message);
}
const { formalCharges } = parsed.result;
expect(formalCharges.atomIdx.rowCount).toBe(3);
expect(formalCharges.charge.rowCount).toBe(3);
expect(formalCharges.atomIdx.value(0)).toBe(2);
expect(formalCharges.atomIdx.value(1)).toBe(3);
expect(formalCharges.charge.value(0)).toBe(-1);
expect(formalCharges.charge.value(1)).toBe(1);
});
it('multiple charge lines', async () => {
const parsed = await parseMol(MolStringWithMultipleChargeLines).run();
if (parsed.isError) {
throw new Error(parsed.message);
}
const { formalCharges } = parsed.result;
expect(formalCharges.atomIdx.rowCount).toBe(3);
expect(formalCharges.charge.rowCount).toBe(3);
expect(formalCharges.atomIdx.value(0)).toBe(2);
expect(formalCharges.atomIdx.value(1)).toBe(3);
expect(formalCharges.charge.value(0)).toBe(-1);
expect(formalCharges.charge.value(1)).toBe(1);
});
it('atom block charge mapping', async () => {
expect(formalChargeMapper(7)).toBe(-3);
expect(formalChargeMapper(6)).toBe(-2);
expect(formalChargeMapper(5)).toBe(-1);
expect(formalChargeMapper(0)).toBe(0);
expect(formalChargeMapper(3)).toBe(1);
expect(formalChargeMapper(2)).toBe(2);
expect(formalChargeMapper(1)).toBe(3);
expect(formalChargeMapper(4)).toBe(0);
});
it('atom block charges', async () => {
const parsed = await parseMol(MolStringWithAtomBlockCharge).run();
if (parsed.isError) {
throw new Error(parsed.message);
}
const { atoms, formalCharges } = parsed.result;
/* No property block charges */
expect(formalCharges.atomIdx.rowCount).toBe(0);
expect(formalCharges.charge.rowCount).toBe(0);
expect(atoms.formal_charge.value(0)).toBe(1);
expect(atoms.formal_charge.value(1)).toBe(0);
expect(atoms.formal_charge.value(2)).toBe(0);
expect(atoms.formal_charge.value(3)).toBe(0);
});
});

View File

@@ -244,6 +244,84 @@ GASTEIGER
25 13 23 1
26 13 24 1`;
const Mol2StringCrysin = `@<TRIPOS>MOLECULE
1144204
12 11 2 0 0
SMALL
USER_CHARGES
****
Generated from the CSD
@<TRIPOS>ATOM
1 Cl1 0.0925 3.6184 1.9845 Cl 1 RES1 -1.0000
2 C1 -4.7391 0.3350 0.4215 C.ar 2 RES2 0.0000
3 C2 -3.4121 0.2604 0.9351 C.ar 2 RES2 0.0000
4 C3 -2.9169 1.2555 1.7726 C.ar 2 RES2 0.0000
5 C4 -3.7118 2.3440 2.1099 C.ar 2 RES2 0.0000
6 C5 -5.0314 2.4052 1.6209 C.ar 2 RES2 0.0000
7 C6 -5.5372 1.4057 0.7962 C.ar 2 RES2 0.0000
8 C7 -6.9925 1.4547 0.3334 C.3 2 RES2 0.0000
9 C8 -7.8537 0.5554 1.1859 C.3 2 RES2 0.0000
10 N1 -9.3089 0.7134 0.8192 N.3 2 RES2 1.0000
11 O1 -2.6613 -0.8147 0.5707 O.3 2 RES2 0.0000
12 O2 -1.6204 1.0919 2.2584 O.3 2 RES2 0.0000
@<TRIPOS>BOND
1 2 3 ar
2 3 4 ar
3 4 5 ar
4 5 6 ar
5 6 7 ar
6 7 2 ar
7 8 7 1
8 9 8 1
9 10 9 1
10 11 3 1
11 12 4 1
@<TRIPOS>SUBSTRUCTURE
1 RES1 1 GROUP 0 **** **** 0
2 RES2 2 GROUP 0 **** **** 0
@<TRIPOS>CRYSIN
10.5150 11.1300 7.9380 90.0000 90.0000 90.0000 29 5
@<TRIPOS>MOLECULE
1144204
12 11 2 0 0
SMALL
USER_CHARGES
****
Generated from the CSD
@<TRIPOS>ATOM
1 Cl1 0.0925 3.6184 1.9845 Cl 1 RES1 -1.0000
2 C1 -4.7391 0.3350 0.4215 C.ar 2 RES2 0.0000
3 C2 -3.4121 0.2604 0.9351 C.ar 2 RES2 0.0000
4 C3 -2.9169 1.2555 1.7726 C.ar 2 RES2 0.0000
5 C4 -3.7118 2.3440 2.1099 C.ar 2 RES2 0.0000
6 C5 -5.0314 2.4052 1.6209 C.ar 2 RES2 0.0000
7 C6 -5.5372 1.4057 0.7962 C.ar 2 RES2 0.0000
8 C7 -6.9925 1.4547 0.3334 C.3 2 RES2 0.0000
9 C8 -7.8537 0.5554 1.1859 C.3 2 RES2 0.0000
10 N1 -9.3089 0.7134 0.8192 N.3 2 RES2 1.0000
11 O1 -2.6613 -0.8147 0.5707 O.3 2 RES2 0.0000
12 O2 -1.6204 1.0919 2.2584 O.3 2 RES2 0.0000
@<TRIPOS>BOND
1 2 3 ar
2 3 4 ar
3 4 5 ar
4 5 6 ar
5 6 7 ar
6 7 2 ar
7 8 7 1
8 9 8 1
9 10 9 1
10 11 3 1
11 12 4 1
@<TRIPOS>SUBSTRUCTURE
1 RES1 1 GROUP 0 **** **** 0
2 RES2 2 GROUP 0 **** **** 0
@<TRIPOS>CRYSIN
10.5150 11.1300 7.9380 90.0000 90.0000 90.0000 29 5
`;
describe('mol2 reader', () => {
it('basic', async () => {
const parsed = await parseMol2(Mol2String, '').run();
@@ -397,4 +475,29 @@ describe('mol2 reader', () => {
// optional bond fields
expect(bonds.status_bits.value(0)).toBe('');
});
it('crysin', async () => {
const parsed = await parseMol2(Mol2StringCrysin, '').run();
if (parsed.isError) {
throw new Error(parsed.message);
}
const mol2File = parsed.result;
// number of structures
expect(mol2File.structures.length).toBe(2);
// crysin fields
for (const data of mol2File.structures) {
expect(data.crysin).toEqual({
a: 10.5150,
b: 11.1300,
c: 7.9380,
alpha: 90.0,
beta: 90.0,
gamma: 90.0,
spaceGroup: 29,
setting: 5
});
}
});
});

View File

@@ -1,3 +1,10 @@
/**
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
* @author David Sehnal <david.sehnal@gmail.com>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
*/
import { parseSdf } from '../sdf/parser';
@@ -458,6 +465,38 @@ describe('sdf reader', () => {
expect(compound3.dataItems.data.value(21)).toBe('2\n5\n10');
});
it('charge parsing in V2000', async () => {
const parsed = await parseSdf(SdfString).run();
if (parsed.isError) {
throw new Error(parsed.message);
}
const compound1 = parsed.result.compounds[0];
const compound2 = parsed.result.compounds[1];
const compound3 = parsed.result.compounds[2];
const formalCharges1 = {
atomIdx: compound1.molFile.formalCharges.atomIdx,
charge: compound1.molFile.formalCharges.charge
};
const formalCharges2 = {
atomIdx: compound2.molFile.formalCharges.atomIdx,
charge: compound2.molFile.formalCharges.charge
};
const formalCharges3 = {
atomIdx: compound3.molFile.formalCharges.atomIdx,
charge: compound3.molFile.formalCharges.charge
};
expect(formalCharges1.atomIdx.rowCount).toBe(3);
expect(formalCharges2.atomIdx.rowCount).toBe(3);
expect(formalCharges3.atomIdx.rowCount).toBe(0);
expect(formalCharges1.charge.rowCount === formalCharges1.atomIdx.rowCount).toBe(true);
expect(formalCharges2.charge.rowCount === formalCharges2.atomIdx.rowCount).toBe(true);
expect(formalCharges3.charge.rowCount === formalCharges3.atomIdx.rowCount).toBe(true);
});
it('v3000', async () => {
const parsed = await parseSdf(V3000SdfString).run();
if (parsed.isError) {
@@ -486,6 +525,11 @@ describe('sdf reader', () => {
expect(compound1.molFile.bonds.atomIdxB.value(10)).toBe(9);
expect(compound1.molFile.bonds.order.value(10)).toBe(2);
expect(compound1.molFile.formalCharges.atomIdx.rowCount).toBe(13);
for (let i = 0; i < compound1.molFile.atoms.count; i++) {
expect(compound1.molFile.formalCharges.charge.value(i)).toBe(0);
}
expect(compound1.dataItems.dataHeader.rowCount).toBe(2);
expect(compound1.dataItems.data.rowCount).toBe(2);

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
/**
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.353, IHM 1.17, MA 1.3.3.
* Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.355, IHM 1.17, MA 1.3.4.
*
* @author molstar/ciftools package
*/
@@ -215,6 +215,28 @@ export const mmCIF_Schema = {
* formal charge assignment normally found in chemical diagrams.
*/
pdbx_formal_charge: int,
/**
* This data item is an ordinal which identifies distinct chemical components in the atom_site category, both
* polymeric and non-polymeric.
*/
pdbx_label_index: int,
/**
* The name of additional external databases with residue level mapping.
*/
pdbx_sifts_xref_db_name: str,
/**
* The accession code related to the additional external database entry.
*/
pdbx_sifts_xref_db_acc: str,
/**
* The sequence position of the external database entry that corresponds
* to the residue mapping defined by the SIFTS process.
*/
pdbx_sifts_xref_db_num: str,
/**
* Describes the residue type of the given UniProt match
*/
pdbx_sifts_xref_db_res: str,
/**
* The model id corresponding to the atom site.
* This data item is a pointer to _ihm_model_list.model_id
@@ -682,7 +704,7 @@ export const mmCIF_Schema = {
/**
* An abbreviation that identifies the database.
*/
database_id: Aliased<'CAS' | 'CSD' | 'EMDB' | 'ICSD' | 'MDF' | 'NDB' | 'NBS' | 'PDB' | 'PDF' | 'RCSB' | 'EBI' | 'PDBE' | 'BMRB' | 'WWPDB' | 'PDB_ACC'>(str),
database_id: Aliased<'AlphaFoldDB' | 'CAS' | 'CSD' | 'EMDB' | 'ICSD' | 'ModelArchive' | 'MDF' | 'MODBASE' | 'NDB' | 'NBS' | 'PDB' | 'PDF' | 'RCSB' | 'SWISS-MODEL_REPOSITORY' | 'EBI' | 'PDBE' | 'BMRB' | 'WWPDB' | 'PDB_ACC'>(str),
/**
* The code assigned by the database identified in
* _database_2.database_id.

View File

@@ -1,7 +1,8 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
*/
import { Column } from '../../../mol-data/db';
@@ -10,6 +11,7 @@ import { TokenColumnProvider as TokenColumn } from '../common/text/column/token'
import { TokenBuilder, Tokenizer } from '../common/text/tokenizer';
import { ReaderResult as Result } from '../result';
/** Subset of the MolFile V2000 format */
export interface MolFile {
readonly title: string,
@@ -20,7 +22,8 @@ export interface MolFile {
readonly x: Column<number>,
readonly y: Column<number>,
readonly z: Column<number>,
readonly type_symbol: Column<string>
readonly type_symbol: Column<string>,
readonly formal_charge: Column<number>
},
readonly bonds: {
readonly count: number
@@ -28,6 +31,57 @@ export interface MolFile {
readonly atomIdxB: Column<number>,
readonly order: Column<number>
}
readonly formalCharges: {
readonly atomIdx: Column<number>;
readonly charge: Column<number>;
}
}
/*
The atom lines in a .mol file have the following structure:
xxxxx.xxxxyyyyy.yyyyzzzzz.zzzz aaaddcccssshhhbbbvvvHHHrrriiimmmnnneee
---------------------------------------------------------------------
Below is a breakdown of each component and its start/end indices:
xxxxx.xxxx (X COORDINATE, 1-10)
yyyyy.yyyy (Y COORDINATE, 10-20)
zzzzz.zzzz (Z COORDINATE, 20-30)
_ (30 IS EMPTY)
aaa (ATOM SYMBOL, 31-34)
dd (MASS DIFF, 34-36)
ccc (FORMAL CHARGE, 36-39)
sss (ATOM STEREO PARITY, 39-42)
hhh (HYDROGEN COUNT+1, 42-45)
bbb (STEREO CARE BOX, 45-48)
vvv (VALENCE, 48-51)
HHH (H0 DESIGNATOR, 51-54)
rrr (UNUSED, 54-57)
iii (UNUSED, 57-60)
mmm (ATOM-ATOM MAPPING NUMBER, 60-63)
nnn (INVERSION/RETENTION FLAG, 63-66)
eee (EXACT CHANGE FLAG, 66-69)
*/
/**
* @param key - The value found at the atom block.
* @returns The actual formal charge based on the mapping.
*/
export function formalChargeMapper(key: number) {
switch (key) {
case 7: return -3;
case 6: return -2;
case 5: return -1;
case 0: return 0;
case 3: return 1;
case 2: return 2;
case 1: return 3;
case 4: return 0;
default:
console.error(`Value ${key} is outside the 0-7 range, defaulting to 0.`);
return 0;
}
}
export function handleAtoms(tokenizer: Tokenizer, count: number): MolFile['atoms'] {
@@ -35,6 +89,7 @@ export function handleAtoms(tokenizer: Tokenizer, count: number): MolFile['atoms
const y = TokenBuilder.create(tokenizer.data, count * 2);
const z = TokenBuilder.create(tokenizer.data, count * 2);
const type_symbol = TokenBuilder.create(tokenizer.data, count * 2);
const formal_charge = TokenBuilder.create(tokenizer.data, count * 2);
for (let i = 0; i < count; ++i) {
Tokenizer.markLine(tokenizer);
@@ -47,6 +102,8 @@ export function handleAtoms(tokenizer: Tokenizer, count: number): MolFile['atoms
TokenBuilder.addUnchecked(z, tokenizer.tokenStart, tokenizer.tokenEnd);
Tokenizer.trim(tokenizer, s + 31, s + 34);
TokenBuilder.addUnchecked(type_symbol, tokenizer.tokenStart, tokenizer.tokenEnd);
Tokenizer.trim(tokenizer, s + 36, s + 39);
TokenBuilder.addUnchecked(formal_charge, tokenizer.tokenStart, tokenizer.tokenEnd);
tokenizer.position = position;
}
@@ -55,7 +112,8 @@ export function handleAtoms(tokenizer: Tokenizer, count: number): MolFile['atoms
x: TokenColumn(x)(Column.Schema.float),
y: TokenColumn(y)(Column.Schema.float),
z: TokenColumn(z)(Column.Schema.float),
type_symbol: TokenColumn(type_symbol)(Column.Schema.str)
type_symbol: TokenColumn(type_symbol)(Column.Schema.str),
formal_charge: TokenColumn(formal_charge)(Column.Schema.int)
};
}
@@ -84,6 +142,76 @@ export function handleBonds(tokenizer: Tokenizer, count: number): MolFile['bonds
};
}
interface FormalChargesRawData {
atomIdx: Array<number>;
charge: Array<number>;
}
export function handleFormalCharges(tokenizer: Tokenizer, lineStart: number, formalCharges: FormalChargesRawData) {
Tokenizer.trim(tokenizer, lineStart + 6, lineStart + 9);
const numOfCharges = parseInt(Tokenizer.getTokenString(tokenizer));
for (let i = 0; i < numOfCharges; ++i) {
/*
M CHG 3 1 -1 2 0 2 -1
| | | | |
| | | | |__charge2 (etc.)
| | | |
| | | |__atomIdx2
| | |
| | |__charge1
| |
| |__atomIdx1 (cursor at position 12)
|
|___numOfCharges
*/
const offset = 9 + (i * 8);
Tokenizer.trim(tokenizer, lineStart + offset, lineStart + offset + 4);
const _atomIdx = Tokenizer.getTokenString(tokenizer);
formalCharges.atomIdx.push(+_atomIdx);
Tokenizer.trim(tokenizer, lineStart + offset + 4, lineStart + offset + 8);
const _charge = Tokenizer.getTokenString(tokenizer);
formalCharges.charge.push(+_charge);
}
/* Once the line is read, move to the next one. */
Tokenizer.eatLine(tokenizer);
}
/** Call an appropriate handler based on the property type.
* (For now it only calls the formal charge handler, additional handlers can
* be added for other properties.)
*/
export function handlePropertiesBlock(tokenizer: Tokenizer): MolFile['formalCharges'] {
const _atomIdx: Array<number> = [];
const _charge: Array<number> = [];
const _formalCharges: FormalChargesRawData = { atomIdx: _atomIdx, charge: _charge };
while (tokenizer.position < tokenizer.length) {
const { position: s } = tokenizer;
Tokenizer.trim(tokenizer, s + 3, s + 6);
const propertyType = Tokenizer.getTokenString(tokenizer);
if (propertyType === 'END') break;
Tokenizer.eatLine(tokenizer);
switch (propertyType) {
case 'CHG':
handleFormalCharges(tokenizer, s, _formalCharges);
break;
default:
break;
}
}
const formalCharges: MolFile['formalCharges'] = {
atomIdx: Column.ofIntArray(_formalCharges.atomIdx),
charge: Column.ofIntArray(_formalCharges.charge)
};
return formalCharges;
}
function parseInternal(data: string): Result<MolFile> {
const tokenizer = Tokenizer(data);
@@ -98,12 +226,15 @@ function parseInternal(data: string): Result<MolFile> {
const atoms = handleAtoms(tokenizer, atomCount);
const bonds = handleBonds(tokenizer, bondCount);
const formalCharges = handlePropertiesBlock(tokenizer);
const result: MolFile = {
title,
program,
comment,
atoms,
bonds
bonds,
formalCharges,
};
return Result.success(result);
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017-2020 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 Zepei Xu <xuzepei19950617@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -259,6 +259,36 @@ async function handleBonds(state: State): Promise<Schema.Mol2Bonds> {
return ret;
}
function handleCrysin(state: State) {
const { tokenizer } = state;
while (tokenizer.position < tokenizer.data.length) {
const l = getTokenString(tokenizer);
if (l === '@<TRIPOS>MOLECULE') {
return;
} else if (l === '@<TRIPOS>CRYSIN') {
break;
} else {
markLine(tokenizer);
}
}
if (tokenizer.position >= tokenizer.data.length) return;
markLine(tokenizer);
const values = getTokenString(tokenizer).trim().split(reWhitespace);
return {
a: parseFloat(values[0]),
b: parseFloat(values[1]),
c: parseFloat(values[2]),
alpha: parseFloat(values[3]),
beta: parseFloat(values[4]),
gamma: parseFloat(values[5]),
spaceGroup: parseInt(values[6], 10),
setting: parseInt(values[7], 10),
};
}
async function parseInternal(ctx: RuntimeContext, data: string, name: string): Promise<Result<Schema.Mol2File>> {
const tokenizer = Tokenizer(data);
@@ -269,7 +299,8 @@ async function parseInternal(ctx: RuntimeContext, data: string, name: string): P
handleMolecule(state);
const atoms = await handleAtoms(state);
const bonds = await handleBonds(state);
structures.push({ molecule: state.molecule, atoms, bonds });
const crysin = handleCrysin(state);
structures.push({ molecule: state.molecule, atoms, bonds, crysin });
skipWhitespace(tokenizer);
while (getTokenString(tokenizer) !== '@<TRIPOS>MOLECULE' && tokenizer.position < tokenizer.data.length) {
markLine(tokenizer);

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