Compare commits

..

552 Commits

Author SHA1 Message Date
Alexander Rose
3667092327 tweak cube clamp param 2022-11-08 22:28:47 -08:00
Alexander Rose
842824057b add param to clamp cube values 2022-11-08 22:17:23 -08:00
Alexander Rose
6a32f85e60 Merge pull request #603 from giagitom/transparent-bg-fixes 2022-11-06 16:42:27 -08:00
giagitom
f29c62ec33 Transparent bg fix 2022-10-30 12:06:32 +01:00
Alexander Rose
d77981230b mmcif schema update 2022-10-29 12:15:29 -07:00
dsehnal
e2b92c15f0 PluginContext.initContainer options 2022-10-27 08:41:01 +02:00
dsehnal
14614f4803 3.23.0 2022-10-19 13:11:47 +02:00
dsehnal
37d3489b07 changelog 2022-10-19 13:09:02 +02:00
David Sehnal
f81225cc0d Merge pull request #592 from molstar/volume/defaults-for-em
Change EM Volume Streaming default Auto
2022-10-19 13:08:17 +02:00
dsehnal
eb47f43940 Change EM Volume Streaming default Auto 2022-10-19 13:05:16 +02:00
dsehnal
7618a5e2c9 pr template 2022-10-19 12:41:24 +02:00
David Sehnal
ab3ff842b2 Merge pull request #590 from molstar/reusable-canvas
Built-in support for mouting/unmounting PluginContext
2022-10-19 09:55:31 +02:00
dsehnal
82f0f92c15 remove unused code 2022-10-18 15:20:48 +02:00
dsehnal
545d9434d8 Add PluginContext.initContainer/canvas3dInitialized and their usage 2022-10-18 09:16:08 +02:00
dsehnal
bbc43d5113 Add PluginContext.mount/unmount 2022-10-18 08:41:29 +02:00
dsehnal
a6709acf65 3.22.0 2022-10-17 20:44:13 +02:00
dsehnal
509a027742 changelog 2022-10-17 20:41:35 +02:00
David Sehnal
7244023233 Merge pull request #589 from molstar/picking-granuality-v2
Volume.PickingGranuality custom property
2022-10-17 20:39:47 +02:00
dsehnal
c5f987d8b2 getSliceLoci tweak 2022-10-17 20:36:39 +02:00
dsehnal
793696d4c0 Volume slice granuality 2022-10-17 20:34:11 +02:00
dsehnal
305ca05f04 tweaks 2022-10-17 20:24:48 +02:00
dsehnal
f4d7d1920a typos 2022-10-17 20:12:19 +02:00
dsehnal
458aad0161 Volume.PickingGranuality custom property 2022-10-17 20:05:56 +02:00
dsehnal
9e3132461f 3.21.0 2022-10-17 17:32:57 +02:00
dsehnal
8301291215 changelog 2022-10-17 17:29:52 +02:00
David Sehnal
daed14e228 Merge pull request #588 from midlik/picking-whole-isosurfaces
New volume isosurface param pickingGranularity: voxels|surfaces
2022-10-17 17:27:34 +02:00
David Sehnal
7db82c5ba5 Merge pull request #584 from arussell123/master
Prevent component controls collapsing when option is selected
2022-10-17 16:43:01 +02:00
Alexander Rose
91d03c22c2 3.20.0 2022-10-16 21:42:39 -07:00
Alexander Rose
bc188f0d2b changelog 2022-10-16 21:37:18 -07:00
Alexander Rose
3981225824 package updates 2022-10-16 21:34:59 -07:00
Alexander Rose
1886d9d72f add structure-index color theme 2022-10-16 21:28:06 -07:00
Alexander Rose
2a7dec8892 Merge pull request #583 from jpattle/model-index-carbon-color
Model index updates & carbon color
2022-10-16 19:43:23 -07:00
Alexander Rose
35d4a5b297 Merge branch 'master' into model-index-carbon-color 2022-10-16 19:39:22 -07:00
Alexander Rose
26345bfa50 tweak 2022-10-16 17:42:43 -07:00
Alexander Rose
8c9b8676dd handle 'not enough samples' in distinctColors 2022-10-16 16:43:50 -07:00
Alexander Rose
5593c7a75f add Model.MaxIndex and use in model-index theme 2022-10-16 16:37:09 -07:00
Alexander Rose
5b70c14ffe tweak theme descriptions 2022-10-16 16:35:51 -07:00
Alexander Rose
5e4d611044 tweak changelog 2022-10-16 16:33:52 -07:00
David Sehnal
7ab9d57156 Merge pull request #545 from giagitom/lookup3d
adding nearest and distance to point methods to lookup3d
2022-10-14 21:46:33 +02:00
Adam Midlik
9ea6f51126 New volume isosurface param pickingGranularity: voxels|surfaces 2022-10-13 00:57:39 +02:00
giagitom
649fe4f4f0 Lint fix 2022-10-12 16:57:56 +02:00
giagitom
53df86c585 Avoid using unnecessary extractMinimum from heap if k=1 on nearest search, add nearest method as unreleased. 2022-10-12 16:14:51 +02:00
Alice Russell
87c708e3c1 Remove action state from being set to undefined when action selected to stop options from minimising on selection 2022-10-12 10:58:30 +01:00
Jason Pattle
ba927b0490 returned clone of theme params for model and trajectory index themes, added contributor name 2022-10-12 08:30:52 +01:00
Jason Pattle
2a09725c98 added the new model-index color theme as an option in the illustrative color-theme 2022-10-12 08:30:10 +01:00
Jason Pattle
9fa0d17933 removed carbon color adjustment option 2022-10-12 08:29:45 +01:00
Jason Pattle
8d9f8a996a Updated change log with changes to model-index and element-symbol 2022-10-12 08:29:17 +01:00
giagitom
8814b60d0b Increased performances of lookup3d nearest search. 2022-10-11 18:12:27 +02:00
Jason Pattle
541c07c53a Added a parameter to make adjusting the carbon color by the same saturation and lightness carbon colors optional 2022-10-11 16:19:17 +01:00
Jason Pattle
6cbed80815 updated the default color palette and removed the redundant model color map 2022-10-11 16:07:44 +01:00
Jason Pattle
a3c1fdc0f4 Added the model index theme provider as an option for the carbon color when selecting the element-symbol color theme 2022-10-11 15:53:05 +01:00
Jason Pattle
ddf789b01c added a new model-index color theme based off the trajectory index theme but instead using the Model.Index structure property 2022-10-11 15:50:23 +01:00
Jason Pattle
ab86cc0bf3 Renamed the model-index color theme file to trajectory-index 2022-10-11 15:37:04 +01:00
Jason Pattle
dc8fab5820 [BREAKING CHANGE] renamed the model-index color theme and its usages to trajectory-index to better reflect the functionality of the color theme 2022-10-11 15:35:29 +01:00
giagitom
813c4f845a Merge branch 'master' into lookup3d 2022-10-09 15:10:37 +02:00
Alexander Rose
6ed42e9521 add mipmap-based blur for skybox backgrounds 2022-10-08 14:54:29 -07:00
Alexander Rose
fb01ba60ec use resources object to get textures for smaa pass 2022-10-08 14:08:07 -07:00
Alexander Rose
ea4210ded5 add willReadFrequently option to sdf text 2d context 2022-10-08 14:06:25 -07:00
Alexander Rose
75e5cf54d6 remove deprecated vscode extension from recommendations 2022-10-08 14:05:12 -07:00
Jason Pattle
7cebc85a95 fixed linting warnings 2022-10-06 13:02:15 +01:00
Jason Pattle
c00faafa6d Returned a clone of the element symbol params instead of the original const, removing a todo comment 2022-10-06 11:42:53 +01:00
Jason Pattle
c9b14f0742 Updated the element symbol color theme so that the carbon color is also adjusted by saturation and brightness props 2022-10-06 09:13:33 +01:00
Alexander Rose
9624137c0d 3.19.0 2022-10-01 17:26:48 -07:00
Alexander Rose
3eb433368f changelog 2022-10-01 17:21:18 -07:00
Alexander Rose
58691f4f5f package updates 2022-10-01 17:19:59 -07:00
Alexander Rose
5e9295abd5 changelog 2022-10-01 15:45:17 -07:00
Alexander Rose
6ed0ae55b2 fix black artifacts
- on specular highlights with transparent background
2022-10-01 15:34:54 -07:00
Alexander Rose
84448d0aa1 Merge branch 'master' of https://github.com/molstar/molstar 2022-10-01 13:51:58 -07:00
Alexander Rose
31ced24966 Merge pull request #573 from molstar/optimize-binary-packing
Optimize BinaryCIF integer packing
2022-10-01 13:51:21 -07:00
Alexander Rose
24681840af debug tweaks 2022-10-01 13:34:50 -07:00
Alexander Rose
5d28aa4f2e add cameraClipping.minNear param 2022-10-01 13:32:09 -07:00
Alexander Rose
7dabdf3085 dpoit fixes
- when post-processing is off
- when rendering direct-volumes
2022-10-01 13:26:10 -07:00
giagitom
d7cbd5570c Implement lookup & grid nearest search using fibonacci heap 2022-09-30 15:44:10 +02:00
dsehnal
80011d4aea optimize BinaryCIF integer packing 2022-09-29 17:30:26 +02:00
David Sehnal
c6fe440a01 Merge pull request #569 from russellp17/fix-empty-texture-error-on-empty-canvas
Fix "empty textures" error on empty canvas
2022-09-27 16:52:28 +02:00
Russell Parker
ba8d6dc3fa Fix "empty textures" error on empty canvas 2022-09-27 10:24:55 -04:00
giagitom
378f4f8304 Merge branch 'master' into lookup3d 2022-09-19 10:22:43 +02:00
Alexander Rose
aa414485a5 3.18.0 2022-09-17 11:55:04 -07:00
Alexander Rose
3a35a5d66a changelog 2022-09-17 11:49:08 -07:00
Alexander Rose
43b0a72b09 package updates 2022-09-17 11:49:01 -07:00
Alexander Rose
521ac2d13f stereo camera improvements
- fix param updates not applied
- better param ranges and description
- add timer.mark for left/right camera
2022-09-17 11:43:18 -07:00
Alexander Rose
30520c50c2 fix changelog 2022-09-17 11:39:40 -07:00
Alexander Rose
819f07eba3 Merge pull request #533 from giagitom/dpoit
Integration of Dual depth peeling - OIT method
2022-09-17 11:36:56 -07:00
Alexander Rose
d8d6aa7136 wboit, tweak timer.mark 2022-09-17 11:34:48 -07:00
giagitom
0bdcfea276 Merge branch 'master' into lookup3d 2022-09-16 17:45:20 +02:00
giagitom
718f76313f Adding nearest method to lookup3d at unit and structure level. 2022-09-16 17:32:07 +02:00
Alexander Rose
ed75a365d8 dpoit, cleanup 2022-09-12 22:38:49 -07:00
Alexander Rose
f5ff13ffe4 dpoit, fix transparent background 2022-09-12 22:38:26 -07:00
Alexander Rose
44c69b1716 dpoit, fix depthMask not off 2022-09-12 22:27:03 -07:00
Alexander Rose
559ca7ffb8 Merge branch 'master' of https://github.com/molstar/molstar into pr/giagitom/533 2022-09-12 22:22:55 -07:00
Alexander Rose
524f34e8c1 3.17.0 2022-09-11 14:38:51 -07:00
Alexander Rose
d749be11f0 type fixes 2022-09-11 14:34:21 -07:00
Alexander Rose
13dc9ff3cb package updates 2022-09-11 14:12:57 -07:00
Alexander Rose
24b4fce326 improve RG texture format handling 2022-09-10 17:21:20 -07:00
Alexander Rose
f506210bf8 dpoit, fix webgl1 support
- in webgl1 drawbuffers must be in the same format for some reason
2022-09-10 17:12:59 -07:00
Alexander Rose
cb0cbd06ce Merge branch 'master' of https://github.com/molstar/molstar into pr/giagitom/533 2022-09-10 16:02:43 -07:00
Alexander Rose
3356239089 fix click event triggered after move 2022-09-10 15:57:07 -07:00
Alexander Rose
9a5b2edc08 cleanup unused variable 2022-09-10 15:39:07 -07:00
Alexander Rose
2d41b4bd83 dpoit, use half float for color textures when available 2022-09-10 15:36:54 -07:00
Alexander Rose
58f7758ee1 add note to MAX_DPOIT_DEPTH constant 2022-09-10 15:30:28 -07:00
Alexander Rose
9dbb642883 more blend back handling cleanup 2022-09-10 15:26:49 -07:00
Alexander Rose
c5222e4d1d Merge branch 'master' of https://github.com/molstar/molstar into pr/giagitom/533 2022-09-10 14:54:57 -07:00
Alexander Rose
a5a695a17c Merge pull request #514 from yakomaxa/molql_integration_PR
Integration of pymol/vmd/jmol transpilers from MolQL project (WIP rasmol transpiler)
2022-09-10 14:51:53 -07:00
Alexander Rose
7d1dc86cfb Merge branch 'master' into molql_integration_PR 2022-09-10 14:50:27 -07:00
Alexander Rose
03224f914a transpiler helper cleanup 2022-09-10 14:49:03 -07:00
Alexander Rose
1cf1f07232 Merge pull request #552 from molstar/perf-caveat
add support for failIfMajorPerformanceCaveat
2022-09-10 13:43:37 -07:00
Alexander Rose
838d36a74e Merge branch 'master' into perf-caveat 2022-09-10 13:43:24 -07:00
Alexander Rose
6c9300d01b fix useBehavior handling 2022-09-10 13:42:52 -07:00
Alexander Rose
3059f7efef Merge pull request #553 from molstar/pdb-ter
fix handling of PDB TER records
2022-09-10 12:51:08 -07:00
Alexander Rose
fbce7d9afa Merge branch 'master' into pdb-ter 2022-09-10 12:50:59 -07:00
Alexander Rose
1c9f3ed9fa simplify ter record check 2022-09-10 12:50:22 -07:00
Alexander Rose
8c47d2d400 Merge pull request #554 from molstar/repr-getAllLoci
add repr.getAllLoci
2022-09-10 12:46:13 -07:00
Alexander Rose
8a18f25b5d Merge branch 'master' into repr-getAllLoci 2022-09-10 12:46:05 -07:00
Alexander Rose
e7ae0058ed Merge pull request #555 from molstar/bond-key-prop
add key property to intra- and inter-bonds
2022-09-10 12:45:39 -07:00
Alexander Rose
98bf3a3e33 tweak per-group transparency cutoff 2022-09-09 23:45:02 -07:00
Alexander Rose
379fcd4494 dpoit, render volumes with standard blending 2022-09-09 23:13:15 -07:00
Alexander Rose
8589777bac Merge branch 'master' of https://github.com/molstar/molstar into pr/giagitom/533 2022-09-09 22:27:45 -07:00
Alexander Rose
c10a21ecbd add key property to intra- and inter-bonds 2022-09-09 22:19:57 -07:00
Alexander Rose
eddc616b14 add repr.getAllLoci 2022-09-09 19:23:46 -07:00
Alexander Rose
70fc1a9579 support residue ranges in jmol atom expressions 2022-09-09 15:59:14 -07:00
Alexander Rose
f27ec4d6a4 formatting 2022-09-09 15:36:08 -07:00
Alexander Rose
1e6e956e2d improve pymol keywords
- rename polymer.protein and polymer.nucleic
- fix guide "C4'"
2022-09-09 15:35:46 -07:00
Alexander Rose
0a2a3530d1 fix handling of PDB TER records 2022-09-09 13:20:15 -07:00
Alexander Rose
9e4c820e26 add support for failIfMajorPerformanceCaveat 2022-09-09 12:31:40 -07:00
Alexander Rose
05290bfe9e update renderer spec resource counts 2022-09-08 18:40:43 -07:00
Alexander Rose
607f4ce353 dpoit: fix drawbuffer setup and blend to target 2022-09-08 18:10:31 -07:00
Alexander Rose
4b819ead1d formating 2022-09-08 18:08:53 -07:00
Alexander Rose
d07d9d3f31 add missing dpoitIterations param 2022-09-08 18:08:16 -07:00
Alexander Rose
d08776bf19 Merge branch 'master' of https://github.com/molstar/molstar into pr/yakomaxa/514 2022-09-07 18:37:37 -07:00
Alexander Rose
7a61fd44fd remove rasmol transpiler
- functionallity integrated into jmol transpiler
2022-09-07 18:32:43 -07:00
Alexander Rose
151da1487c formatting 2022-09-07 18:28:45 -07:00
Alexander Rose
e3f6dfad5b improve jmol transpiler
- add basic within function
- add basic backbone and protein keyword
- allow withspace in parans
2022-09-07 18:06:15 -07:00
Alexander Rose
32080ce918 improve jmol transpiler
- add resno ranges
- add bracketed resnames
- allow comma as OR
2022-09-07 15:16:28 -07:00
Alexander Rose
aedb2138c8 Merge pull request #548 from molstar/interactions-parent-display
add parentDisplay param to interactions repr
2022-09-06 12:19:25 -07:00
Alexander Rose
90e6938f1c Merge branch 'master' into interactions-parent-display 2022-09-06 11:57:02 -07:00
David Sehnal
eadff35250 Merge pull request #547 from molstar/sifts-alignment/residue-test
alignAndSuperposeWithSIFTSMapping includeResidueTest option
2022-09-05 15:11:53 +02:00
Alexander Rose
487450ec64 show only 'between' interaction in docking-viewer 2022-09-04 22:41:35 -07:00
Alexander Rose
f2f730bab5 fix parentDisplay 'between' logic 2022-09-04 22:41:06 -07:00
Alexander Rose
ceecee37a7 add parentDisplay param to interactions repr 2022-09-02 22:19:45 -07:00
dsehnal
394377bea9 alignAndSuperposeWithSIFTSMapping update 2022-09-02 15:50:58 +02:00
dsehnal
2b47818deb alignAndSuperposeWithSIFTSMapping includeResidueTest option 2022-09-02 15:38:06 +02:00
giagitom
9f72465052 remove unnecessary 2022-09-02 10:21:56 +02:00
giagitom
ac33b4a322 Adding distanceToVec in sphere3d namespace 2022-09-02 10:20:51 +02:00
giagitom
911c844e54 Remove logs and unused variables 2022-09-01 18:09:04 +02:00
giagitom
12b9655565 adding nearest and distance to point methods to lookup3d 2022-09-01 17:41:09 +02:00
dsehnal
2935717a06 Canvas3DParams fix 2022-08-29 16:49:30 +02:00
dsehnal
2c8d2cfa21 3.16.0 2022-08-25 17:55:54 +02:00
dsehnal
d67c0eb757 ViewportHelpContent fix 2022-08-25 17:53:10 +02:00
giagitom
7bcbcd5a7f Exposed dpoitIterations parameter 2022-08-25 16:07:36 +02:00
giagitom
1c06e7f36e Merge branch 'master' into dpoit 2022-08-24 18:44:30 +02:00
giagitom
407297adc0 added dpoit-enable query string 2022-08-24 18:21:34 +02:00
giagitom
b082b31562 Remove logs, changelog, set wboit as default option 2022-08-24 18:19:59 +02:00
giagitom
fcbeb0f82f Add defines for direct-volume 2022-08-24 17:42:08 +02:00
dsehnal
7094f8f265 Improve Viewer theming & label customization 2022-08-24 15:26:24 +02:00
giagitom
48aaa13420 Fix missing texture format 2022-08-24 15:24:30 +02:00
giagitom
d2434cf91f Merge branch 'master' into dpoit 2022-08-24 15:09:13 +02:00
giagitom
8dbe0d2793 Lint-fix 2022-08-24 14:37:33 +02:00
giagitom
7b308cf984 Keep wboit as default OIT method 2022-08-24 14:34:44 +02:00
giagitom
520af504aa Throw an error if wboit and dpoit are both enabled 2022-08-24 14:29:40 +02:00
giagitom
4bee130599 Added credits and contributions + lint-fix 2022-08-24 14:12:12 +02:00
dsehnal
19a9ed3e19 3.15.0 2022-08-23 19:00:36 +02:00
dsehnal
0dac1b93ae changelog 2022-08-23 18:57:45 +02:00
dsehnal
e824863de1 lint 2022-08-23 18:56:07 +02:00
David Sehnal
9ff8becd62 Merge pull request #530 from midlik/volume-streaming-camera-target
Volume streaming camera target (without caching)
2022-08-23 18:53:11 +02:00
Adam Midlik
fcaa1bcfa8 Merge branch 'master' into volume-streaming-camera-target 2022-08-23 18:39:37 +02:00
giagitom
39f51bcc4f Integration of Dual depth peeling - OIT method 2022-08-23 17:09:53 +02:00
Adam Midlik
1b904ee2c9 Addressed PR comments 2022-08-23 17:03:32 +02:00
yakomaxa
e2baafc426 lint 2022-08-23 18:13:14 +09:00
yakomaxa
6dabe73002 Update pymol keywords and properties 2022-08-23 17:16:10 +09:00
David Sehnal
d9b2b99c86 Merge pull request #531 from molstar/safari-wboit
add missing depth renderbuffer to wboit pass
2022-08-23 09:40:27 +02:00
Alexander Rose
bdf23a7c4e add missing depth renderbuffer to wboit pass
- fix wboit in Safari >=15
2022-08-22 23:39:50 -07:00
yakomaxa
c1723e0806 lint 2022-08-21 21:59:10 +09:00
yakomaxa
93590bd482 Remove comment line in vmd keyword 2022-08-21 21:54:38 +09:00
yakomaxa
fbaa9d9e58 Finally enabled selection by negative-valued residue index in RasMol 2022-08-21 21:48:47 +09:00
yakomaxa
ba78a8558c Finally enabled selection by negative-valued residue index 2022-08-21 21:39:20 +09:00
yakomaxa
513be04352 lint PyMOL 2022-08-21 21:38:43 +09:00
yakomaxa
15317aa11b give pymol the ability to deal with negatively indexed residue numbering 2022-08-21 19:37:20 +09:00
yakomaxa
93e107f333 give pymol the ability to deal with negatively indexed residue numbering 2022-08-21 19:32:45 +09:00
yakomaxa
655b334b0a Updated rasmol parser and spec 2022-08-21 17:14:29 +09:00
yakomaxa
95cc1c58a6 remove comment and unused character and functions 2022-08-21 16:56:20 +09:00
yakomaxa
0511d3e599 Unparenthesized residue range enabled in RasMol and parenthesized was un-activated 2022-08-21 16:51:38 +09:00
yakomaxa
92a41b5c46 Unparenthesized residue range enabled in RasMol, which should be ported to Jmol 2022-08-21 16:48:41 +09:00
yakomaxa
be16837c8c Remove unused lines 2022-08-21 15:51:41 +09:00
yakomaxa
bf5f26cb12 Remove comment-out lines. Moved un-bracketed residue name to the supported feature 2022-08-21 15:44:09 +09:00
yakomaxa
ccbcef7eff lint 2022-08-21 15:33:49 +09:00
yakomaxa
d02a97b7f0 un-bracketed residue-name and un-parenthesized residue number is enabeled in RasMol 2022-08-21 15:31:48 +09:00
yakomaxa
e0ca413c54 Remove needless substitutions to a temporary variable x 2022-08-21 10:42:32 +09:00
yakomaxa
a272fc1c05 Remove comment-out lines 2022-08-21 10:39:44 +09:00
yakomaxa
2c3f74d4ea Remove a comment-out line 2022-08-21 10:38:40 +09:00
yakomaxa
c65b2fc0fd Remove a comment-out line 2022-08-21 10:37:25 +09:00
yakomaxa
fd725adf27 Added TODO comment for comment-out functions 2022-08-21 10:36:47 +09:00
yakomaxa
ceba6da91f Remove comment-out lines 2022-08-21 10:27:57 +09:00
yakomaxa
064e7d42ee Remove a comment-out import 2022-08-21 10:26:56 +09:00
yakomaxa
cfdbf0c614 Remove comment-out lines 2022-08-21 10:25:33 +09:00
yakomaxa
436777fe34 Remove a comment-out import and a comment-out piece of code 2022-08-21 10:22:45 +09:00
yakomaxa
f08aa46222 Remove two comment-out import 2022-08-21 10:20:58 +09:00
yakomaxa
e099ac514a Remove three comment-out console.log 2022-08-21 10:19:56 +09:00
yakomaxa
873755f619 Remove comment-out unused import 2022-08-21 10:18:49 +09:00
yakomaxa
2094b7cf83 Update changelog mentioning this feature 2022-08-21 10:16:49 +09:00
yakomaxa
6ffdd81bb1 remove commented console.log 2022-08-21 10:14:32 +09:00
yakomaxa
a69cb17602 remove default paramerter and added whitespace before } 2022-08-21 10:12:56 +09:00
yakomaxa
7c82a9fd6e update transpile.ts 2022-08-21 10:11:59 +09:00
yakomaxa
10d9a6c83d removed console.log in try and replaced it by console.error in catch 2022-08-21 10:01:56 +09:00
Alexander Rose
e474e9b090 3.14.0 2022-08-20 16:43:16 -07:00
Alexander Rose
837f9a6c74 changelog 2022-08-20 16:37:46 -07:00
Alexander Rose
c357aed7bb schema updates 2022-08-20 16:36:48 -07:00
Alexander Rose
59ffddfd8d update packages 2022-08-20 16:33:17 -07:00
Alexander Rose
fb3accaa36 Merge pull request #528 from molstar/safari-surf-fix
wrap gl_VertexID in int()
2022-08-20 15:45:14 -07:00
Alexander Rose
b3e79544ad Merge branch 'master' into safari-surf-fix 2022-08-20 15:44:30 -07:00
Alexander Rose
2ee0f3bf97 Merge pull request #515 from molstar/background-pass
Background pass
2022-08-20 15:41:32 -07:00
Alexander Rose
a56b5edc4e cleanup 2022-08-20 15:32:01 -07:00
Alexander Rose
f2d71b6551 Merge branch 'master' into background-pass 2022-08-20 15:25:20 -07:00
Alexander Rose
ef560ddc03 Merge pull request #529 from molstar/webgl-state
Webgl state
2022-08-20 15:22:04 -07:00
Alexander Rose
2e30ffe1bc Merge branch 'master' into webgl-state 2022-08-20 15:21:54 -07:00
Alexander Rose
325b5e9297 Merge pull request #527 from molstar/custom-prop-fix
fix CustomElementProperty coloring
2022-08-20 15:21:01 -07:00
Alexander Rose
ae9e04b8d4 reduce number of webgl state changes
- add viewport and scissor to state object
- add hasOpaque to scene object
2022-08-20 12:04:51 -07:00
Alexander Rose
ab0010122b handle renderable rendering edge cases
- fix text background rendering for opaque text
- fix helper scenes not shown when rendering directly to draw target
2022-08-20 12:04:04 -07:00
Alexander Rose
08d736ecdc image loading error handling and other tweaks 2022-08-20 11:54:51 -07:00
Alexander Rose
9c362c8ffd Merge branch 'master' of https://github.com/molstar/molstar into background-pass 2022-08-20 11:07:57 -07:00
Alexander Rose
62c8778560 Merge pull request #513 from molstar/inter-bonds-props
expose inter-bonds props & improve performance
2022-08-20 11:06:04 -07:00
Alexander Rose
2fe0665e12 simplify box3d functions 2022-08-20 10:57:40 -07:00
Alexander Rose
14a957f517 Merge branch 'master' of https://github.com/molstar/molstar into inter-bonds-props 2022-08-20 10:56:14 -07:00
Alexander Rose
087010d0a1 Merge pull request #525 from molstar/pairBonds-maxDistance
set some IndexPairBonds maxDistance to Infinity
2022-08-20 10:54:12 -07:00
Alexander Rose
f92657310a Merge branch 'master' into pairBonds-maxDistance 2022-08-20 10:54:03 -07:00
Alexander Rose
19e91400b5 fix CustomElementProperty coloring
- can't check data availabilty in isApplicable because it is obtained on demand
2022-08-20 10:14:21 -07:00
Alexander Rose
7885fb7b4f wrap gl_VertexID in int()
-fix GPU surfaces rendering in Safari with WebGL2
2022-08-20 10:12:51 -07:00
Alexander Rose
331bec11ee cleanup comment 2022-08-20 10:08:47 -07:00
Adam Midlik
f6ed650ef6 Refactoring, cleaning 2022-08-19 15:17:50 +02:00
Adam Midlik
df9ce6add9 Volume Streaming Around Camera - parameter Radius is relative to FOV 2022-08-19 12:42:10 +02:00
Adam Midlik
28ee47d501 Volume Streaming Around Camera - avoid chain-reaction updates 2022-08-19 11:29:52 +02:00
Adam Midlik
df2da479ad Volume Streaming - corrected help text for Detail Level (range is 1-7) 2022-08-19 10:23:00 +02:00
Adam Midlik
46eb9d8baf Volume Streaming - correct init value of Selection Detail and Dynamic Detail when View type changes 2022-08-19 10:21:08 +02:00
Adam Midlik
b6be871a21 Volume Streaming all types use MonoQueue 2022-08-18 23:28:11 +02:00
Adam Midlik
ce2367fc0a Volume streaming Around Focus uses MonoQueue (i.e. never queuing more than 1 update) 2022-08-18 19:27:53 +02:00
dsehnal
f219cd6c8b prefer webgl1 in Safari 16 2022-08-18 17:39:18 +02:00
yakomaxa
7789e8cfea deleted package-lock.json 2022-08-18 19:58:30 +09:00
Alexander Rose
e697624064 prefer WebGL1 for more Safari versions
-avoid broken GPU surfaces rendering
2022-08-17 22:08:17 -07:00
Alexander Rose
92ffdeb5bf don't include glycam names in default saccharides 2022-08-17 21:57:55 -07:00
Alexander Rose
ddefe7e542 package updates 2022-08-17 21:50:54 -07:00
Alexander Rose
fb4019c041 changelog 2022-08-17 21:39:14 -07:00
Alexander Rose
46026e047e set some IndexPairBonds maxDistance to Infinity
- for MOL/SDF and MOL2 (without symmetry) models
- avoid filtering by element-based rules
2022-08-17 21:38:06 -07:00
yakomaxa
51a9effcaa Added a sentence on transpilers in CHANGELOG.md 2022-08-17 12:07:12 +09:00
yakomaxa
fc3b953a8e Added whitespace remover to prefixRemoveKet in helper.ts and update rasmol.spec.ts 2022-08-17 11:55:45 +09:00
yakomaxa
a2ded3cecc Small update for RasMol parser 2022-08-17 10:28:39 +09:00
yakomaxa
ffb1390b51 added conditional evalation to inComplement 2022-08-17 10:15:18 +09:00
yakomaxa
3b92591c05 added conditional evaluation to disjunct and invert 2022-08-17 10:08:15 +09:00
yakomaxa
f173ddcf00 removed comment-out lines from rasmol parser.ts 2022-08-16 22:50:35 +09:00
yakomaxa
f78306f624 removed unused abbr. from rasmol operators.ts 2022-08-16 22:41:18 +09:00
yakomaxa
9852c9301e Changed list name in respective examples.ts and now examples.spec.ts runs without error 2022-08-16 20:07:44 +09:00
yakomaxa
2e4f3de604 debugging example.spec.ts 2022-08-16 19:47:11 +09:00
yakomaxa
300dd97353 Added spec, debugged Jmol parser, removed unused definition from RasMol properties/operators 2022-08-16 19:34:13 +09:00
yakomaxa
8e29ade83a minor change of language name: Rasmol -> RasMol 2022-08-16 16:52:34 +09:00
Alexander Rose
971c770f6a add experimental warning for transpiled scripts 2022-08-15 22:03:24 -07:00
Alexander Rose
0dfad5a757 imporve labels of skybox params 2022-08-15 20:55:45 -07:00
Alexander Rose
a0495f8aae fix SSAO renderable initialization 2022-08-15 20:55:27 -07:00
yakomaxa
7d31a06ae4 Updated atom-set.ts according to suggestion by dsehnal 2022-08-16 12:50:02 +09:00
KoyaS
c5319ad7b1 Update package.json
Co-authored-by: David Sehnal <dsehnal@users.noreply.github.com>
2022-08-16 12:38:31 +09:00
KoyaS
f8bdb28ea6 Update package.json
Co-authored-by: David Sehnal <dsehnal@users.noreply.github.com>
2022-08-16 12:38:18 +09:00
KoyaS
2f8806d7c2 Update src/mol-script/runtime/query/table.ts
Co-authored-by: David Sehnal <dsehnal@users.noreply.github.com>
2022-08-16 12:37:55 +09:00
KoyaS
7d0a181c12 Update src/mol-model/structure/query/queries/atom-set.ts
Co-authored-by: David Sehnal <dsehnal@users.noreply.github.com>
2022-08-16 12:37:46 +09:00
KoyaS
27cb7e53ed Update src/mol-script/runtime/query/table.ts
Co-authored-by: David Sehnal <dsehnal@users.noreply.github.com>
2022-08-16 12:37:36 +09:00
KoyaS
ee5154b510 Update src/mol-script/runtime/query/table.ts
Co-authored-by: David Sehnal <dsehnal@users.noreply.github.com>
2022-08-16 12:37:20 +09:00
KoyaS
080837201a Update src/mol-script/runtime/query/table.ts
Co-authored-by: David Sehnal <dsehnal@users.noreply.github.com>
2022-08-16 12:36:44 +09:00
KoyaS
656e6c0d94 Update src/mol-script/runtime/query/table.ts
Co-authored-by: David Sehnal <dsehnal@users.noreply.github.com>
2022-08-16 12:36:17 +09:00
KoyaS
b018f61bab Update src/mol-script/runtime/query/table.ts
conditional evaluation

Co-authored-by: David Sehnal <dsehnal@users.noreply.github.com>
2022-08-16 12:35:37 +09:00
yakomaxa
b03b306848 Enabling some numerical properties and cleaning by lint 2022-08-16 06:17:58 +09:00
yakomaxa
19bf5c2b3e [SER]3:A.CA type of selection enabled 2022-08-16 05:27:30 +09:00
yakomaxa
c22a716cf9 Update helper.ts for RasMol 2022-08-16 04:35:26 +09:00
yakomaxa
220c65da92 Parentheses re-enabled in RasMol 2022-08-16 04:27:17 +09:00
yakomaxa
675f4b86f8 Minor update of RasMol mode 2022-08-16 04:18:00 +09:00
yakomaxa
d31b3522b2 Added myself as author in package.json. Thanks. 2022-08-15 21:12:22 +09:00
yakomaxa
4ed2bab1d7 cleaning by lint 2022-08-15 20:56:20 +09:00
yakomaxa
a572872806 Updated author information and removed notes 2022-08-15 20:53:47 +09:00
yakomaxa
e3ca23db0b Remove needless setting of class members and added revised author information 2022-08-15 20:24:23 +09:00
KoyaS
67eb16a53f Merge pull request #18 from yakomaxa/rasmol
Merge from RasMol branch and begin answering the reviewers
2022-08-15 20:12:23 +09:00
yakomaxa
d7421cd1a3 Removed unwanted file 2022-08-15 20:00:48 +09:00
yakomaxa
c2e68ced66 cleaning by lint 2022-08-15 19:39:54 +09:00
yakomaxa
10cf0db050 Within operator was implemented in RasMol 2022-08-15 19:38:14 +09:00
yakomaxa
08f1a1dcfe Added experimental within to rasmol 2022-08-15 18:03:16 +09:00
yakomaxa
11bf352295 Added experimental within to rasmol 2022-08-15 15:39:57 +09:00
yakomaxa
5fd560b30a Added experimental within to rasmol 2022-08-15 15:38:38 +09:00
KoyaS
e6d01ca246 Merge pull request #17 from yakomaxa/molql_integration_PR
Molql integration pr
2022-08-15 13:03:55 +09:00
Alexander Rose
1610f05b83 Merge branch 'master' of https://github.com/molstar/molstar into inter-bonds-props 2022-08-14 16:28:21 -07:00
Alexander Rose
8202b75cda Merge branch 'master' of https://github.com/molstar/molstar into background-pass 2022-08-14 16:26:47 -07:00
Alexander Rose
4904bae5a6 background pass improvements
- add PluginConfig.Background.Styles
- file support, asset management
- opacity, saturation, lightness controls for skybox/image
- coverage controls for image/gradient
- add backgrounds extension with examples
- image handling for build/watch (webpack, cpx)
2022-08-14 16:24:28 -07:00
Alexander Rose
04c06db02c Merge pull request #519 from MadCatX/gzip_files
Allow download of Gzipped files
2022-08-14 14:52:10 -07:00
Alexander Rose
0ddf2fa00d typing tweaks 2022-08-14 14:37:30 -07:00
Alexander Rose
8776143ec2 revert to mol-script in StructureSelectionFromScript 2022-08-14 14:07:39 -07:00
Alexander Rose
080c8e7af3 add basic scipt language selector 2022-08-14 14:06:12 -07:00
Michal Malý
a96f94b676 Allow download of Gzipped files 2022-08-14 17:11:35 +02:00
yakomaxa
64998e762b cleaning by lint and adding comments 2022-08-14 11:15:50 +09:00
yakomaxa
b508da5ccc cleaning by lint 2022-08-14 11:06:24 +09:00
KoyaS
84a492655a Merge pull request #16 from yakomaxa/molql_integration_PR
Molql integration pr
2022-08-14 10:57:07 +09:00
yakomaxa
9b9cfe4138 rasmol -> pymol 2022-08-14 10:55:11 +09:00
yakomaxa
f362a7086a rasmol update 2022-08-14 10:54:31 +09:00
KoyaS
9e9ec57a5f Merge pull request #14 from yakomaxa/rasmol
merge from rasmol branch
2022-08-14 04:09:15 +09:00
yakomaxa
da6a153985 Construction of rasmol selection command has nearly finished 2022-08-14 04:03:48 +09:00
yakomaxa
b4bde3f510 Rasmol parser WIP 5 2022-08-14 03:31:08 +09:00
yakomaxa
8a5cebd635 Rasmol parser WIP 4 2022-08-14 03:23:54 +09:00
Alexander Rose
ebdfc694c2 Merge pull request #520 from MadCatX/clean_up_pyramids
Change the lookup logic of NtC steps from residues
2022-08-13 11:19:30 -07:00
yakomaxa
ddf2733d3c Rasmol parser WIP 3 2022-08-14 00:36:37 +09:00
yakomaxa
cf65bfbcd0 Rasmol parser WIP 2 2022-08-13 23:03:26 +09:00
yakomaxa
03cce830bc Rasmol parser WIP 2022-08-13 21:43:05 +09:00
yakomaxa
913cf4c3e9 developing rasmol residue-range selector WIP 2022-08-13 12:44:14 +09:00
yakomaxa
2ccce0beaf developing rasmol residue-range selector WIP 2022-08-13 12:43:07 +09:00
yakomaxa
52239f71cd refined rasmol keywords 2022-08-13 11:20:41 +09:00
KoyaS
d9265af2e8 Merge pull request #13 from yakomaxa/molql_integration_PR
Added transpilers/_spec and checked that they pass the test
2022-08-13 08:57:48 +09:00
yakomaxa
5877f6a627 Added transpilers/_spec and checked that they pass the test 2022-08-13 02:43:54 +09:00
Michal Malý
7f29340797 Change the lookup logic of NtC steps from residues 2022-08-12 15:29:26 +02:00
KoyaS
e3e982c051 Merge pull request #11 from yakomaxa/molql_integration_PR
merge from molql_integration_PR
2022-08-12 18:07:23 +09:00
yakomaxa
17f09ff3de Cleaning and lint 2022-08-12 18:00:45 +09:00
yakomaxa
7a73416c03 Cleaning and lint 2022-08-12 17:44:29 +09:00
yakomaxa
0f799d44ad Found a bug in withSameAtomProperties in filter.ts, where propSet and currentProps are placed inversely in isSuperSet() 2022-08-12 16:08:42 +09:00
yakomaxa
24ebd44f87 remove namedAtomProperty from vmd/parser.ts, removed as any from definition of withSameAtomProperties, un-commented out the continue line in filter.ts 2022-08-12 10:06:44 +09:00
KoyaS
572b10e655 Merge pull request #9 from yakomaxa/molql_integration_PR
merge from molql_integration_pr branch
2022-08-12 07:11:15 +09:00
yakomaxa
60361c176b Updated pick function in filter.ts. This enebles VMD keywords like backbone and many more. 2022-08-12 06:29:13 +09:00
yakomaxa
b232a2c58f Updated atom-set.ts and filter.ts, which leads to correct behavior of inorganic in PyMOL. The bound_to operator was found to behave differently from original PyMOL even in MolQL implementation: it's just mistake in logic. 2022-08-12 06:19:20 +09:00
yakomaxa
2a44ac56fb cleaning by lint 2022-08-12 01:18:56 +09:00
yakomaxa
d0340a3257 The function called by filter.withSameAtomProperties is debugged and almost everything works fine now. 2022-08-11 23:54:57 +09:00
yakomaxa
e708a53ddb atom-set.ts table.ts updates 2022-08-11 22:11:15 +09:00
yakomaxa
23cdd70198 updated atomCount and many problem solved 2022-08-11 21:20:55 +09:00
yakomaxa
ba4bc30a78 remove trash file 2022-08-11 14:28:31 +09:00
yakomaxa
e516ea146d Update for atomCount and countQuery in table.ts 2022-08-11 14:26:10 +09:00
yakomaxa
61af638fe4 Update atom-set 2022-08-11 13:59:56 +09:00
yakomaxa
7a0af4142f Now PyMOL within operator works. The key was to replace xs['max-radius'] -> xs['max-radius'](ctx) in table.ts 2022-08-11 10:52:56 +09:00
yakomaxa
1aa8d596a3 Updates for queryInSelection in src/mol-script/runtime/query/table.ts was reverted (made bug) 2022-08-11 10:30:09 +09:00
yakomaxa
a457810623 queryInSelection in src/mol-script/runtime/query/table.ts was updated 2022-08-11 10:18:16 +09:00
yakomaxa
4bccb7ab84 VMD jmol rasmol parser debugged 2022-08-11 09:46:15 +09:00
yakomaxa
fcd5b2ce0a VMD parser debugged 2022-08-11 09:36:32 +09:00
yakomaxa
57a1184a16 VMD parser updated, now math function to value comparison enabled 2022-08-11 09:22:37 +09:00
Adam Midlik
ef176efed8 Volume streaming 'Around Camera' - prototype 2022-08-11 01:39:22 +02:00
yakomaxa
9943e0317d VMD jmol and rasmol parser updated 2022-08-11 08:39:21 +09:00
yakomaxa
ae11c1c904 VMD keyword update 2022-08-11 03:33:28 +09:00
yakomaxa
f937e069ca Clearning by Lint 2022-08-11 02:41:43 +09:00
yakomaxa
c9326da47b Implemented subset of atomSet: atomCount, queryCount and propertySet, which enables some of PyMOL operators. Implementation should be reviewed as byring is not working, and inorganic working incorrectly 2022-08-11 02:32:45 +09:00
yakomaxa
4698c05f9c re-implementation of VMD's exwithin without using wihtin 2022-08-10 21:41:03 +09:00
yakomaxa
15fd2cd5a0 let transpile.ts show what the query is 2022-08-10 21:27:25 +09:00
yakomaxa
193eb11095 Set some operators that lacks corresponding implementation to isUnsupported : true 2022-08-10 16:34:35 +09:00
yakomaxa
59cc0096cd fixing typo in example of pymol operator: resname CA -> name CA 2022-08-10 16:28:51 +09:00
yakomaxa
6bd7390eb8 Debbuged asAtoms in helper.ts, which enables operation using this function such as byresidue and neighbor. In addition, min-spelling in neighbor operation fixed: neighbour -> neighbor 2022-08-10 16:21:06 +09:00
yakomaxa
eabeb18f34 pymol 2022-08-10 16:11:54 +09:00
yakomaxa
4b6f539ba3 debugging wrapValue in helper.ts : property.head -> property.head.name : This enables secondary structure selection for VMD via valuesTest in helper.ts 2022-08-09 22:22:08 +09:00
yakomaxa
ea55fc5f41 debugging VMD mode 2022-08-09 22:05:04 +09:00
yakomaxa
94787229a4 testLevel in helper.ts was debuged and now comes back in vmd/parser.ts 2022-08-09 19:24:34 +09:00
Alexander Rose
113d0b5141 add background pass
- skybox, image, horizontal/radial gradient
2022-08-07 13:27:06 -07:00
Alexander Rose
163285b0a9 cleanup 2022-08-07 13:20:52 -07:00
Alexander Rose
9f1cf5377a add sceneRadiusFactor param 2022-08-07 13:17:52 -07:00
Alexander Rose
c37636215b expose fov camera param 2022-08-07 13:14:57 -07:00
KoyaS
0cb162022c Merge pull request #6 from yakomaxa/enable_not_and
Enabled NOT operation
2022-08-07 17:45:45 +09:00
yakomaxa
5ff2ca311e structure-query.generator.query-in-selection was implemented: this enables NOT operation. In addition, tranpiler/helper.ts was fixed to use B.struct.generator.all() instead of B.struct.generator.atomGroups(). B.struct.generator.atomGroups() is not working for some reason, which was also met in the keyword 'all' in all of vmd/pymol/jmol parser.ts 2022-08-07 17:42:15 +09:00
KoyaS
44b8d452a8 Merge pull request #5 from yakomaxa/enable_not_and
structure-query.modifier.intersectby was implemented
2022-08-07 15:55:08 +09:00
yakomaxa
82ccb1ab23 structure-query.modifier.intersectBy was implemented: this enables AND operation 2022-08-07 15:51:06 +09:00
yakomaxa
feb8b94e30 modified _spec: I don't understand what these _spec things do 2022-08-07 10:19:03 +09:00
yakomaxa
5adc73e277 removed unsused modules from package.json 2022-08-07 10:18:41 +09:00
KoyaS
c4ac983fc7 Merge pull request #4 from molstar/master
merge from head repository
2022-08-07 08:40:03 +09:00
yakomaxa
5ba7ba3aac Cleaning before PR. Note that many tests fail. 2022-08-07 08:34:22 +09:00
yakomaxa
e879479b3d removed mock-extension for language selector and reverted src/mol-plugin-state/transforms/representation.ts to the original 2022-08-07 07:36:39 +09:00
yakomaxa
7b49463297 NamedAtomProperties was added to vmd parser 2022-08-07 06:16:29 +09:00
Alexander Rose
1f77b19ced changelog 2022-08-06 13:45:29 -07:00
Alexander Rose
9853ebf02f Merge pull request #507 from MadCatX/add_pyramid_labels
Add labels for Confal pyramids
2022-08-06 13:42:02 -07:00
Alexander Rose
6e13aa0bc9 expose inter-bonds props & improve performance 2022-08-06 13:31:10 -07:00
yakomaxa
66600c3373 deleted unused comment lines in monadic-parser.ts 2022-08-07 02:39:22 +09:00
yakomaxa
19401c4bc6 Activated RangeListProperty in vmd/parser.ts with hardcoded 'atom-test' line 2022-08-07 02:38:21 +09:00
yakomaxa
bfc8660c5e replaced structure. -> structure-query. in transpiler directory 2022-08-07 02:21:52 +09:00
yakomaxa
6a83dc56ba reverted vmd parser and properties to original 2022-08-07 02:12:21 +09:00
KoyaS
82df9d8cad Merge pull request #3 from yakomaxa/rasmol
Rasmol transpiler added
2022-08-05 23:47:28 +09:00
yakomaxa
dd30fef078 Added many properties and keywords for PyMOL and rasmol 2022-08-05 23:38:41 +09:00
yakomaxa
79feb5a1cc rasmol branch launched 2022-08-05 20:48:16 +09:00
yakomaxa
0665524b11 refined VMD: RangeListProperty in vmd/parser.ts is not working for unknown reasons, so it was substituted by NamedAtomProperties: This limits the selection like 'residue 10 to 50' and 'residue 10' 2022-08-05 20:36:32 +09:00
yakomaxa
d45367e840 Refining VMD parser and properties 2022-08-05 13:32:46 +09:00
Michal Malý
1b7f0e0f1e Add example mmCIF to allow testing of Confal pyramids 2022-08-04 10:09:50 +02:00
Michal Malý
18cb3360b5 Update changelog 2022-08-04 09:56:28 +02:00
yakomaxa
cb0d988efc Updated language-select/ui, refactored transpile.ts and monadic-parser.ts 2022-08-03 22:52:07 +09:00
yakomaxa
fc0c556967 updated default value for Script and added mock-up for language-selector 2022-08-03 21:31:53 +09:00
yakomaxa
00970164db Debugged all in jmol 2022-08-03 03:24:35 +09:00
yakomaxa
7c3d76e9fe Merge branch 'master' of github:yakomaxa/molstar_my 2022-08-03 02:50:43 +09:00
yakomaxa
190c1f9620 Added pymol, jmol and vmd alongside mol-script: pymol is now enabled 2022-08-03 02:49:52 +09:00
KoyaS
f532325147 Merge pull request #2 from yakomaxa/PyMOL
added all and polymer.protein to keywords of pymol
2022-08-02 13:25:09 +09:00
yakomaxa
278dcb8808 added all and polymer.protein to keywords of pymol 2022-08-02 13:10:42 +09:00
Michal Malý
6fec598b96 Add labels for Confal pyramids 2022-08-01 14:46:35 +02:00
KoyaS
309c25e10b Merge pull request #1 from yakomaxa/jmol
Added Jmol transpiler (not enabled)
2022-07-31 23:05:06 +09:00
yakomaxa
6df728ea3e added jmol transplier (not enabled) 2022-07-31 23:01:11 +09:00
yakomaxa
dcf4ef6d74 added jmol transpiler 2022-07-31 22:49:20 +09:00
yakomaxa
4de1369a5a added jmol transpiler 2022-07-31 22:46:58 +09:00
yakomaxa
2ccfdb1280 added _spec (not tested whether it works) 2022-07-31 21:58:43 +09:00
yakomaxa
9fbf800639 added _spec (not tested whether it works) 2022-07-31 21:58:09 +09:00
yakomaxa
577daf64df PyMOL mode was successfully enabled (though mol-script is disabled...) 2022-07-31 12:27:15 +09:00
yakomaxa
0b1943e9b3 Now it worksgit add src/mol-script/transpile.ts 2022-07-31 12:17:25 +09:00
yakomaxa
30bd2dd876 Compiles but not boot 2022-07-31 12:04:38 +09:00
yakomaxa
cecd4d4179 Compiles but not working again 2022-07-31 11:15:58 +09:00
yakomaxa
364baab18d Added transpile.ts and all.ts 2022-07-31 10:30:37 +09:00
yakomaxa
bb3d4d2171 Added default export 2022-07-31 10:00:36 +09:00
yakomaxa
2355faf899 compiles but not working 2022-07-31 01:54:27 +09:00
yakomaxa
858e0b24ff now with few errors 2022-07-31 00:03:22 +09:00
yakomaxa
f7d0ed3988 Added additional modules 2022-07-30 23:17:21 +09:00
David Sehnal
40096ecdfb Merge pull request #502 from giagitom/master 2022-07-27 10:05:35 +02:00
giagitom
43061b80b8 Deliver defaultAttribs to Passes constructor 2022-07-26 19:24:31 +02:00
Alexander Rose
aa3d657d42 3.13.0 2022-07-24 17:11:08 -07:00
Alexander Rose
b0ef385769 changelog 2022-07-24 17:05:47 -07:00
Alexander Rose
dcf24e6292 Merge pull request #496 from JonStargaryen/master
Download CCD from Configurable URL
2022-07-24 17:04:28 -07:00
Alexander Rose
2fdd77737c Merge pull request #499 from molstar/immediate-isolevel
enable immediateUpdate for iso level
2022-07-24 17:02:55 -07:00
Alexander Rose
31c98ef1ba package updates 2022-07-23 13:40:23 -07:00
Alexander Rose
ceeec2c13a enable immediateUpdate for iso level 2022-07-23 13:36:15 -07:00
Alexander Rose
cc82e0cff8 Merge pull request #498 from molstar/varying-group
Varying group
2022-07-23 13:19:10 -07:00
Alexander Rose
29fc6c59e9 support constant group in gpu mc 2022-07-23 13:18:16 -07:00
Alexander Rose
aa931fab7b add dVaryingGroup to avoid flat qualifier more 2022-07-23 13:06:35 -07:00
Alexander Rose
8e2585a5c0 add material annotation support for textures 2022-07-23 11:26:34 -07:00
Alexander Rose
c115047f74 handle principal axes of points in a plane 2022-07-23 11:06:01 -07:00
Alexander Rose
0ac58cb137 changelog 2022-07-23 11:02:01 -07:00
Alexander Rose
492e0977c3 Merge pull request #494 from giagitom/master
only update camera state if manualReset is off
2022-07-23 10:57:36 -07:00
JonStargaryen
e8a09e81f3 fix short arg names 2022-07-21 14:02:57 -07:00
JonStargaryen
4fcc2c6208 download CCD from configurable URL 2022-07-21 09:50:01 -07:00
giagitom
e3523dc5fe only update camera state if manualReset is off 2022-07-20 18:04:03 +02:00
dsehnal
acf6c31a36 3.12.1 2022-07-20 15:43:33 +02:00
dsehnal
339b2e696c PluginBehavior dispose logic 2022-07-20 15:40:30 +02:00
Alexander Rose
6417fd49d6 3.12.0 2022-07-17 16:28:26 -07:00
Alexander Rose
374fd4db65 changelog 2022-07-17 16:23:08 -07:00
Alexander Rose
0b70dd9e38 Merge pull request #487 from molstar/fix/struct_conn-parsing
struct_conn parsing fix
2022-07-17 16:20:45 -07:00
dsehnal
55b19a7922 changelog 2022-07-17 18:01:28 +02:00
dsehnal
beb1b2655e scan all entities when looking for struct_conn etries
- solves PDB loading issue
2022-07-17 17:58:57 +02:00
Alexander Rose
6a81e48c3a package updates 2022-07-16 13:20:19 -07:00
Alexander Rose
f9841dd3df improve CellPack's adjustStyle option
- disable colorMarker
- set component options
- enable marking w/o ghost
2022-07-16 13:02:04 -07:00
Alexander Rose
b563c773c1 avoid using flat qualifier in shaders
- causing slowdown
2022-07-16 13:01:33 -07:00
Alexander Rose
dcda649d9d add colorMarker option to Renderer
- disables the highlight and select marker at a shader level
- faster rendering of large scenes in some cases.
2022-07-16 12:58:49 -07:00
Alexander Rose
d6cfd23ae5 fix missing material annotation for some uniforms
- causing unnecessary uniform updates
2022-07-16 12:31:38 -07:00
Alexander Rose
b69f62c9a4 remove use of isnan in impostor shaders
- not needed and causing slowdown
2022-07-16 12:28:40 -07:00
Alexander Rose
582ee7d623 bind shared textures only once per pass 2022-07-16 12:27:16 -07:00
dsehnal
7c4202186d 3.11.0 2022-07-04 16:30:54 +02:00
dsehnal
7c56e4c09d fix unused import 2022-07-04 16:28:24 +02:00
dsehnal
b10b466c61 changelog 2022-07-04 16:25:23 +02:00
David Sehnal
80d1986c61 Merge pull request #474 from molstar/composable-superposition
coordinate system support for superposition
2022-07-04 16:24:45 +02:00
dsehnal
7f9e413604 coordinate system support for superposition 2022-07-04 16:23:51 +02:00
Alexander Rose
4dfbc3830f Merge pull request #466 from molstar/cellpack-tweaks
Cellpack tweaks
2022-07-03 14:18:26 -07:00
Alexander Rose
46cdefa9ee add adjustStyle option to LoadCellPackModel 2022-07-02 12:48:05 -07:00
Alexander Rose
f857ea6095 fix missing rename
- forceInstanceTheme -> instanceGranularity
2022-07-01 06:34:27 -07:00
Alexander Rose
994920f99f fix shader compilation
- support instance texture params for overpaint, substance, transparency
2022-06-29 22:28:10 -07:00
Alexander Rose
130d4096d5 Merge branch 'master' of https://github.com/molstar/molstar into cellpack-tweaks 2022-06-26 17:50:14 -07:00
Alexander Rose
d5659529d7 3.10.2 2022-06-26 17:34:46 -07:00
Alexander Rose
f6e06ab16e changelog 2022-06-26 17:29:48 -07:00
Alexander Rose
4245eaf1b2 improve use of gl_VertexID when possible 2022-06-26 17:29:21 -07:00
Alexander Rose
c41bd702e2 remove superfluous shader varying 2022-06-26 17:25:14 -07:00
Alexander Rose
3911145f87 Merge branch 'master' of https://github.com/molstar/molstar into cellpack-tweaks 2022-06-26 14:54:15 -07:00
Alexander Rose
350f5c4266 3.10.1 2022-06-26 14:17:31 -07:00
Alexander Rose
ed4056bc8b changelog 2022-06-26 14:04:20 -07:00
Alexander Rose
0d96fa21b7 schema updates 2022-06-26 14:03:09 -07:00
Alexander Rose
0ee8d33d36 package updates 2022-06-26 13:30:44 -07:00
Alexander Rose
64cedec12b fix groupCount updating TextureMesh-based visuals 2022-06-26 12:42:26 -07:00
Alexander Rose
a16eaca42e finalize instanceGranularity 2022-06-26 12:27:02 -07:00
dsehnal
366b3b1b75 3.10.0 2022-06-24 15:59:45 +02:00
dsehnal
33963c085a ShowTrajectoryControls option 2022-06-24 15:56:39 +02:00
Alexander Rose
f3b18ef518 fix & simplify lociApply for instanceGranularity 2022-06-20 22:33:24 -07:00
Alexander Rose
bca1b45fd4 tweak name
- useInstanceGranularity -> instanceGranularity
2022-06-20 22:00:21 -07:00
Alexander Rose
3448d5ef03 Merge branch 'master' of https://github.com/molstar/molstar into cellpack-tweaks 2022-06-20 21:23:32 -07:00
David Sehnal
0f5a6194ff Merge pull request #455 from molstar/support-glycam-names
Support glycam names
2022-06-20 13:37:16 +02:00
David Sehnal
9266faec59 Merge branch 'master' into support-glycam-names 2022-06-20 13:37:10 +02:00
Alexander Rose
94347c6dde cleanup app.ts (#450) 2022-06-19 19:38:43 -07:00
Alexander Rose
7a07701be0 3.9.1 2022-06-19 19:21:25 -07:00
Alexander Rose
42519a4f75 changelog 2022-06-19 19:16:15 -07:00
Alexander Rose
c898c16430 package updates 2022-06-19 19:15:06 -07:00
Alexander Rose
318863bd18 fix missing aromatic bond display
- simplify code to always show when aromatic
2022-06-19 19:05:58 -07:00
Alexander Rose
ce94760d02 fix missing uGroupCount update for visuals 2022-06-19 18:51:16 -07:00
Alexander Rose
99759b5282 add useInstanceGranularity option
- for marker, transparency, clipping, overpaint, substance data
- saves memory
2022-06-19 18:45:31 -07:00
dsehnal
d9d8562ed9 changelog 2022-06-13 19:52:44 +02:00
dsehnal
dee55ea874 support glycam names 2022-06-13 19:52:14 +02:00
dsehnal
da413cc9e6 added missing super.componentWillUnmount 2022-06-10 14:53:26 +02:00
David Sehnal
c72e93a13d Merge pull request #452 from simeonborko/sborko/screenshot-unmount
DownloadScreenshotControls: componentWillUnmount
2022-06-10 14:46:19 +02:00
Simeon Borko
21f910a3ca DownloadScreenshotControls: componentWillUnmount
This should solve the error:

Warning: Can't perform a React state update on an unmounted component.
This is a no-op, but it indicates a memory leak in your application.
To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
2022-06-10 10:04:31 +02:00
Alexander Rose
2c51edb4c2 changelog 2022-05-30 19:20:41 -07:00
Alexander Rose
da2c893721 Merge branch 'master' of https://github.com/molstar/molstar into cellpack-tweaks 2022-05-30 19:18:49 -07:00
Alexander Rose
e7ce693e50 3.9.0 2022-05-30 11:46:42 -07:00
Alexander Rose
29e8fe7904 fix types 2022-05-30 11:41:40 -07:00
Alexander Rose
baf3a6077e package updates 2022-05-30 11:32:05 -07:00
Alexander Rose
e030e7a32d changelog 2022-05-30 11:29:23 -07:00
Alexander Rose
125566ed75 only call renderBlendedTransparent when needed 2022-05-30 11:27:58 -07:00
Alexander Rose
c51cb67519 Merge pull request #447 from molstar/pick-drawbuffers
use drawbuffers for picking
2022-05-29 23:35:53 -07:00
Alexander Rose
57f086b530 Merge branch 'master' into pick-drawbuffers 2022-05-29 23:32:57 -07:00
Alexander Rose
d1e17785b8 Merge pull request #446 from molstar/webgl-timer
Webgl timing support
2022-05-29 23:32:29 -07:00
Alexander Rose
774328a1d8 Merge branch 'master' into webgl-timer 2022-05-29 23:31:13 -07:00
Alexander Rose
175a0f48fa lint fix 2022-05-29 23:30:36 -07:00
Alexander Rose
60b91ff032 Merge branch 'master' into pick-drawbuffers 2022-05-29 11:11:29 -07:00
David Sehnal
2b003bc5b0 Merge pull request #445 from aliaksei-chareshneu/fix_bad_axis_order
Add check that a data block contains volume data before parsing
2022-05-29 16:00:10 +02:00
David Sehnal
029a2fcab1 Merge branch 'master' into fix_bad_axis_order 2022-05-29 15:59:52 +02:00
Alexander Rose
aa47f7fe4a use instances to create dna/rna curves
- much less memory use (but can't show as single cartoon)
2022-05-28 19:49:56 -07:00
Alexander Rose
a828113d9b use drawbuffers for picking 2022-05-28 13:17:35 -07:00
Alexander Rose
ab4d509eda fix rendering volumes
- when wboit is switched off and postprocessing is enabled
2022-05-28 13:13:35 -07:00
Alexander Rose
1296f32fa8 tweak alpha-orbitals example 2022-05-28 11:54:33 -07:00
Alexander Rose
5aa1ec9876 add timing mode 2022-05-28 11:43:13 -07:00
Alexander Rose
f2cf1ab226 add support for GPU timer queries 2022-05-28 11:20:22 -07:00
Alexander Rose
2d34c2a40b fix Scene.clear not clearing primitives/volumes 2022-05-28 11:07:03 -07:00
Aliaksei
a7181e865c Skipping server data block
Co-authored-by: David Sehnal <dsehnal@users.noreply.github.com>
2022-05-28 08:16:58 +02:00
Aliaksei
0a71b788b3 Apply suggestions from code review
fix bugs and optimizing the code related to iteration over data blocks

Co-authored-by: David Sehnal <dsehnal@users.noreply.github.com>
2022-05-27 18:22:00 +02:00
aliaksei-chareshneu
1ed3d84043 fix to skip block with header SERVER 2022-05-27 14:26:42 +02:00
aliaksei-chareshneu
f472b75d0d iterate over all blocks as even 0th can contain data 2022-05-27 13:53:08 +02:00
aliaksei-chareshneu
072a9d1ccd add name and email to header; add changelog entry 2022-05-27 12:19:51 +02:00
aliaksei-chareshneu
8e26d1be68 fix bad axis ordering bug 2022-05-27 10:34:48 +02:00
Alexander Rose
c5871e9025 3.8.2 2022-05-22 08:36:50 -07:00
Alexander Rose
f26911b358 changelog 2022-05-22 08:31:57 -07:00
Alexander Rose
3a595b80b5 package updates 2022-05-22 08:30:15 -07:00
Alexander Rose
45760ddd41 fix Scene.opacityAverage not handling xray shaded 2022-05-22 08:22:39 -07:00
Alexander Rose
447d068bf1 3.8.1 2022-05-14 11:59:03 -07:00
Alexander Rose
7e1642a4a3 changelog 2022-05-14 11:53:55 -07:00
Alexander Rose
7781267e78 package updates 2022-05-14 11:53:18 -07:00
Alexander Rose
f824fdcfed improve scene marker/opacity average calculation 2022-05-14 11:43:24 -07:00
Alexander Rose
79dd441967 Merge pull request #436 from JonStargaryen/master
Fix Polymer Chain Instance Coloring
2022-05-09 22:41:33 -07:00
Sebastian Bittrich
9dcf9c0785 CHANGELOG 2022-05-09 15:58:22 -07:00
Sebastian Bittrich
83569462c6 fix instance coloring 2022-05-09 15:53:26 -07:00
Alexander Rose
947e169c3a Merge pull request #434 from JonStargaryen/master
proposed fix for headless-gl issues
2022-05-06 22:29:40 -07:00
Sebastian Bittrich
e6b36c52d1 CHANGELOG 2022-05-06 09:11:40 -07:00
Sebastian Bittrich
7fed3b84fa Merge remote-tracking branch 'upstream/master' 2022-05-06 09:08:53 -07:00
Sebastian Bittrich
cbc941f193 simplify array uniform check 2022-05-06 09:08:22 -07:00
Alexander Rose
a7ef0fb85f add interesting pdb entry 2022-05-05 22:41:16 -07:00
Alexander Rose
d10c36eaf5 fix issues with marking camera/handle helper 2022-05-05 22:40:52 -07:00
Sebastian Bittrich
e4c3a66753 proposed fix for headless-gl issues 2022-05-04 11:50:42 -07:00
Alexander Rose
f58f2cdc90 3.8.0 2022-04-30 16:11:07 -07:00
Alexander Rose
8f2676e91e schema updates 2022-04-30 16:05:30 -07:00
Alexander Rose
89d397898e changelog 2022-04-30 15:24:26 -07:00
Alexander Rose
2dc32be9ee Merge pull request #427 from molstar/transparent-object-outline
Transparent object outline
2022-04-30 15:08:46 -07:00
Alexander Rose
769220bd82 Merge pull request #421 from molstar/color-theme-typing
better color theme typing
2022-04-30 15:06:06 -07:00
Alexander Rose
c75aa5dd52 improve transparency & outlines
- proper per-group transparency with wboit off
- fixed outlines with transparent backgound
2022-04-30 12:53:14 -07:00
Alexander Rose
88f1cfd8c4 fix ignoreLight for direct-volume with webgl1 2022-04-30 09:24:39 -07:00
Alexander Rose
108279c1aa add clearDepth & blendColor to WebGLState 2022-04-30 08:42:01 -07:00
Alexander Rose
8150490aac Merge branch 'master' of https://github.com/molstar/molstar into transparent-object-outline 2022-04-30 08:39:16 -07:00
Alexander Rose
9497aa6362 package updates 2022-04-30 08:38:07 -07:00
Alexander Rose
3769da48a1 handle building GridLookup3D with zero cell size 2022-04-29 20:18:40 -07:00
Alexander Rose
3c21fcd53a fix FormatRegistry.isApplicable
- was returning true for unregistered formats
2022-04-29 20:16:09 -07:00
Alexander Rose
102ef2795d handle outlines for per-group transparency
- fix allowTransparentBackfaces for per-group transparency
2022-04-23 23:02:11 -07:00
Alexander Rose
0befa253c2 add WebGLState.depthFunc 2022-04-23 20:07:41 -07:00
Alexander Rose
87189cee58 support outlines for transparent objects
- cleanup of renderer and draw pass
2022-04-23 15:19:01 -07:00
Alexander Rose
3284f13fc6 fix spec of emptyDepthTexture in Renderer 2022-04-23 15:17:20 -07:00
Alexander Rose
70d219b120 improve Renderer.renderPick typing 2022-04-23 15:13:38 -07:00
Alexander Rose
a5ed3a08ea add Scene.getOpacityAverage 2022-04-23 15:12:16 -07:00
Alexander Rose
3665e7e999 expose RenderTarget.depthRenderbuffer 2022-04-23 15:10:40 -07:00
Alexander Rose
9b583b23ae add Renderbuffer.detachFramebuffer 2022-04-23 15:10:16 -07:00
Alexander Rose
d602415e98 check if WEBGL_depth_texture is supported 2022-04-23 15:09:44 -07:00
Alexander Rose
2c49a423e2 type assertion 2022-04-21 19:47:13 -07:00
Alexander Rose
8a266e70c8 fix handling of struct_conf mmCIF field (#425) 2022-04-21 19:46:50 -07:00
Alexander Rose
0df3bcd65d document supported file formats 2022-04-19 17:43:33 -07:00
dsehnal
f5ecf5648e 3.7.0 2022-04-13 20:04:01 +02:00
dsehnal
821f82fc3f changelog 2022-04-13 20:01:04 +02:00
David Sehnal
92305fe628 Merge pull request #420 from molstar/transparentBackfaces
improve transparentBackfaces handling
2022-04-13 19:57:29 +02:00
David Sehnal
17fe57b8a5 Merge pull request #423 from jpattle/option-to-disable-drag-overlay
Added an option to disable the drag overlay
2022-04-13 16:31:21 +02:00
Jason Pattle
47433a51d3 Added an option to the components spec to allow disabling of the drag-and-drop overlap for Molstar that loads files into the viewer. By default the drag overlay is enabled, just as it was before 2022-04-13 15:00:03 +01:00
Alexander Rose
e090827ced Merge branch 'master' of https://github.com/molstar/molstar into transparentBackfaces 2022-04-10 13:20:22 -07:00
Alexander Rose
856e6a8b74 use ColorTypeLocation as default
- for backwards compatibility
- most used
2022-04-09 19:27:06 -07:00
Alexander Rose
a813b4d40e Merge branch 'master' of https://github.com/molstar/molstar into color-theme-typing 2022-04-09 16:53:17 -07:00
dsehnal
98afc27442 fix react typing 2022-04-09 21:24:14 +02:00
Alexander Rose
9d4f28a395 fix type deps 2022-04-09 11:36:17 -07:00
Alexander Rose
50266d9a56 package updates 2022-04-09 11:20:28 -07:00
Alexander Rose
602a532cf2 better color theme typing 2022-04-09 10:38:03 -07:00
Alexander Rose
b23d610c94 improve transparentBackfaces handling
- off: don't show (default)
- on: show with transparency
- opaque: show fully opaque
2022-04-09 10:17:41 -07:00
Alexander Rose
119c43d527 fix xrayShaded for texture-mesh geometries 2022-04-09 10:04:06 -07:00
Alexander Rose
124feeb790 color theme tweaks
- add optional grid property
- handle dColorType_direct for non direct-volume shaders
2022-04-09 09:57:31 -07:00
322 changed files with 34345 additions and 24513 deletions

10
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,10 @@
<!-- Thank you for contributing to Mol* -->
# Description
## Actions
- [ ] Added description of changes to the `[Unreleased]` section of `CHANGELOG.md`
- [ ] Updated headers of modified files
- [ ] Added my name to `package.json`'s `contributors`

View File

@@ -6,7 +6,6 @@
"recommendations": [
"dbaeumer.vscode-eslint",
"firsttris.vscode-jest-runner",
"msjsdiag.debugger-for-chrome",
"slevesque.shader",
"stpn.vscode-graphql",
"wayou.vscode-todo-highlight"

View File

@@ -6,6 +6,202 @@ Note that since we don't clearly distinguish between a public and private interf
## [Unreleased]
- Make `PluginContext.initContainer` checkered canvas background optional
## [v3.23.0] - 2022-10-19
- Add `PluginContext.initContainer/mount/unmount` methods; these should make it easier to reuse a plugin context with both custom and built-in UI
- Add `PluginContext.canvas3dInitialized`
- `createPluginUI` now resolves after the 3d canvas has been initialized
- Change EM Volume Streaming default from `Whote Structure` to `Auto`
## [v3.22.0] - 2022-10-17
- Replace `VolumeIsosurfaceParams.pickingGranularity` param with `Volume.PickingGranuality`
## [v3.21.0] - 2022-10-17
- Add `VolumeIsosurfaceParams.pickingGranularity` param
- Prevent component controls collapsing when option is selected
## [v3.20.0] - 2022-10-16
- [Breaking] Rename the ``model-index`` color theme to ``trajectory-index``
- Add a new ``model-index`` color theme that uniquely colors each loaded model
- Add the new ``model-index`` and ``structure-index`` color themes as an option for the carbon color in the ``element-symbol`` and ``ilustrative`` color themes
- Add ``structure-index`` color theme that uniquely colors each root structure
- Add ``nearest`` method to ``Lookup3D``
- Add mipmap-based blur for skybox backgrounds
## [v3.19.0] - 2022-10-01
- Fix "empty textures" error on empty canvas
- Optimize BinaryCIF integer packing encoder
- Fix dual depth peeling when post-processing is off or when rendering direct-volumes
- Add ``cameraClipping.minNear`` parameter
- Fix black artifacts on specular highlights with transparent background
## [v3.18.0] - 2022-09-17
- Integration of Dual depth peeling - OIT method
- Stereo camera improvements
- Fix param updates not applied
- Better param ranges and description
- Add timer.mark for left/right camera
## [v3.17.0] - 2022-09-11
- [Fix] Clone ``Canvas3DParams`` when creating a ``Canvas3D`` instance to prevent shared state between multiple instances
- Add ``includeResidueTest`` option to ``alignAndSuperposeWithSIFTSMapping``
- Add ``parentDisplay`` param for interactions representation.
- [Experimental] Add support for PyMOL, VMD, and Jmol atom expressions in selection scripts
- Support for ``failIfMajorPerformanceCaveat`` webgl attribute. Add ``PluginConfig.General.AllowMajorPerformanceCaveat`` and ``allow-major-performance-caveat`` Viewer GET param.
- Fix handling of PDB TER records (#549)
- Add support for getting multiple loci from a representation (``.getAllLoci()``)
- Add ``key`` property to intra- and inter-bonds for referencing source data
- Fix click event triggered after move
## [v3.16.0] - 2022-08-25
- Support ``globalColorParams`` and ``globalSymmetryParams`` in common representation params
- Support ``label`` parameter in ``Viewer.loadStructureFromUrl``
- Fix ``ViewportHelpContent`` Mouse Controls section
## [v3.15.0] - 2022-08-23
- Fix wboit in Safari >=15 (add missing depth renderbuffer to wboit pass)
- Add 'Around Camera' option to Volume streaming
- Avoid queuing more than one update in Volume streaming
## [v3.14.0] - 2022-08-20
- Expose inter-bonds compute params in structure
- Improve performance of inter/intra-bonds compute
- Fix defaultAttribs handling in Canvas3DContext.fromCanvas
- Confal pyramids extension improvements
- Add custom labels to Confal pyramids
- Improve naming of some internal types in Confal pyramids extension coordinate
- Add example mmCIF file with categories necessary to display Confal pyramids
- Change the lookup logic of NtC steps from residues
- Add support for download of gzipped files
- Don't filter IndexPairBonds by element-based rules in MOL/SDF and MOL2 (without symmetry) models
- Fix Glycam Saccharide Names used by default
- Fix GPU surfaces rendering in Safari with WebGL2
- Add ``fov`` (Field of View) Canvas3D parameter
- Add ``sceneRadiusFactor`` Canvas3D parameter
- Add background pass (skybox, image, horizontal/radial gradient)
- Set simple-settings presets via ``PluginConfig.Background.Styles``
- Example presets in new backgrounds extension
- Load skybox/image from URL or File (saved in session)
- Opacity, saturation, lightness controls for skybox/image
- Coverage (viewport or canvas) controls for image/gradient
- [Breaking] ``AssetManager`` needs to be passed to various graphics related classes
- Fix SSAO renderable initialization
- Reduce number of webgl state changes
- Add ``viewport`` and ``scissor`` to state object
- Add ``hasOpaque`` to scene object
- Handle edge cases where some renderables would not get (correctly) rendered
- Fix text background rendering for opaque text
- Fix helper scenes not shown when rendering directly to draw target
- Fix ``CustomElementProperty`` coloring not working
## [v3.13.0] - 2022-07-24
- Fix: only update camera state if manualReset is off (#494)
- Improve handling principal axes of points in a plane
- Add 'material' annotation support for textures
- More effort to avoid using ``flat`` qualifier in shaders: add ``dVaryingGroup``
- Enable ``immediateUpdate`` for iso level in isosurface and volume streaming controls
- Add support to download CCD from configurable URL
## [v3.12.1] - 2022-07-20
- Fix plugin behavior dispose logic to correctly unsubscribe observables.
## [v3.12.0] - 2022-07-17
- Add ``colorMarker`` option to Renderer. This disables the highlight and select marker at a shader level for faster rendering of large scenes in some cases.
- Bind shared textures only once per pass, not for each render item
- Fix missing 'material' annotation for some uniforms, causing unnecessary uniform updates
- Remove use of ``isnan`` in impostor shaders, not needed and causing slowdown
- Avoid using ``flat`` qualifier in shaders, causing slowdown
- Improve CellPack's ``adjustStyle`` option (disable ``colorMarker``, set component options, enable marking w/o ghost)
- Scan all entities when looking for ``struct_conn`` entries (fixes issue when the same ``label_asym_id`` is used in more than one entity)
## [v3.11.0] - 2022-07-04
- Add ``instanceGranularity`` option for marker, transparency, clipping, overpaint, substance data to save memory
- CellPack extension tweaks
- Use instancing to create DNA/RNA curves to save memory
- Enable ``instanceGranularity`` by default
- Add ``adjustStyle`` option to LoadCellPackModel action (stylized, no multi-sample, no far clipping, chain picking)
- Structure Superposition now respects pivot's coordinate system
## [v3.10.2] - 2022-06-26
- Fix superfluous shader varying
- Improve use of gl_VertexID when possible
## [v3.10.1] - 2022-06-26
- Fix groupCount when updating TextureMesh-based visuals
## [v3.10.0] - 2022-06-24
- Add support for Glycam saccharide names
- Add ``PluginConfig.Viewport.ShowTrajectoryControls`` config option
## [v3.9.1] - 2022-06-19
- Fix missing ``super.componentWillUnmount()`` calls (@simeonborko)
- Fix missing ``uGroupCount`` update for visuals
- Fix missing aromatic bond display
## [v3.9.0] - 2022-05-30
- Improve picking by using drawbuffers (when available) to reduce number of drawcalls
- GPU timing support
- Add ``timing-mode`` Viewer GET param
- Add support for webgl timer queries
- Add timer marks around GPU render & compute operations
- Volume Server CIF: Add check that a data block contains volume data before parsing
- Fix ``Scene.clear`` not clearing primitives & volumes arrays (@JonStargaryen)
- Fix rendering volumes when wboit is switched off and postprocessing is enabled
## [v3.8.2] - 2022-05-22
- Fix ``Scene.opacityAverage`` not taking xray shaded into account
## [v3.8.1] - 2022-05-14
- Fix issues with marking camera/handle helper (#433)
- Fix issues with array uniforms when running with headless-gl
- Fix Polymer Chain Instance coloring
- Improve performance of scene marker/opacity average calculation
## [v3.8.0] - 2022-04-30
- Add support for outlines around transparent objects
- Improve per-group transparency when wboit is switched off
- Improve ``ColorTheme`` typing with ``ColorType`` generic.
- Defaults to ``ColorTypeLocation``
- Set when using ``ColorTypeDirect`` or ``ColorTypeGrid``
- Fix case handling of ``struct_conf`` mmCIF enumeration field (#425)
- Fix ``allowTransparentBackfaces`` for per-group transparency
- Fix ``FormatRegistry.isApplicable`` returning true for unregistered formats
- Fix: handle building of ``GridLookup3D`` with zero cell size
- Fix ``ignoreLight`` for direct-volume rendering with webgl1
- Fix (non-black) outlines when using transparent background
## [v3.7.0] - 2022-04-13
- Fix ``xrayShaded`` for texture-mesh geometries
- [Breaking] Change ``allowTransparentBackfaces`` to ``transparentBackfaces`` with options ``off``, ``on``, ``opaque``. This was only added in 3.6.0, so allowing a breaking change here.
- ``off``: don't show (default)
- ``on``: show with transparency
- ``opaque``: show fully opaque
- Add option to disable file drop overlay.
## [v3.6.2] - 2022-04-05
- ModelServer ligand queries: fixes for alternate locations, additional atoms & UNL ligand
@@ -27,8 +223,8 @@ Note that since we don't clearly distinguish between a public and private interf
- Fix handling of case insensitive mmCIF enumeration fields (including entity.type)
- Fix ``disable-wboit`` Viewer GET param
- Add support for React 18.
- Used by importing ``createPluginUI`` from ``mol-plugin-ui/react18``;
- In Mol* 4.0, React 18 will become the default option.
- Used by importing ``createPluginUI`` from ``mol-plugin-ui/react18``;
- In Mol* 4.0, React 18 will become the default option.
## [v3.5.0] - 2022-03-25

View File

@@ -126,7 +126,7 @@ and navigate to `build/viewer`
**GraphQL schemas**
node node_modules//@graphql-codegen/cli/bin -c src/extensions/rcsb/graphql/codegen.yml
node node_modules/@graphql-codegen/cli/cjs/bin -c src/extensions/rcsb/graphql/codegen.yml
### Other scripts
**Create chem comp bond table**
@@ -152,7 +152,7 @@ Or
node lib/commonjs/cli/cif2bcif
E.g.
node lib/commonjs/cli/cif2bcif src.cif out.bcif.gz
node lib/commonjs/cli/cif2bcif src.bcif.gz out.cif

44
docs/file-formats.md Normal file
View File

@@ -0,0 +1,44 @@
Support file formats and their extensions.
## Structure
- MMCIF and CIFCORE (mmCIF and coreCIF schemas): cif, bcif, mmcif, mcif
- GRO: gro
- MOL: mol
- MOL2: mol2
- PDB/PDBQT: pdb, ent, pdbqt
- SDF: sdf, sd
- XYZ: xyz
## Topology
Need to be loaded together with Coordinates.
- PRMTOP: prmtop, parm7
- PSF: psf
- TOP: top
## Coordinates
Need to be loaded together with a Structure or Topology.
- DCD: dcd
- NCTRAJ: nc, nctraj
- TRR: trr
- XTC: xtc
## Volume
- CCP4/MRC/MAP: ccp4, mrc, map
- CUBE (may include a Structure): cub, cube
- DSN6/BRIX: dsn6, brix
- DX and DXBIN: dx, dxbin
- DSCIF (DensityServer CIF schema): cif, bcif
## Shape
- PLY

View File

@@ -13,6 +13,7 @@
* DZ has C1 instead of N1 (e.g. 6I4N)
* DP has N5 instead of C5 and C7 instead of N7 (e.g. 6I4N)
* Beta & Gamma peptides (e.g. 1GAC, 6PQF)
* Helices of D-amino acids (e.g. 7QDI)
* Mixed (heterogeneous) all-atom/trace-only RNA model (1JGQ)
* Polymers with residues with missing trace atoms (e.g. 2QFJ)
* Modified RNA bases (1y26, 5L4O)

File diff suppressed because it is too large Load Diff

42856
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "3.6.2",
"version": "3.23.0",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -20,7 +20,7 @@
"rebuild": "npm run clean && npm run build",
"build-viewer": "npm run build-tsc && npm run build-extra && npm run build-webpack-viewer",
"build-tsc": "concurrently \"tsc --incremental\" \"tsc --build tsconfig.commonjs.json --incremental\"",
"build-extra": "cpx \"src/**/*.{scss,html,ico}\" lib/",
"build-extra": "cpx \"src/**/*.{scss,html,ico,jpg}\" lib/",
"build-webpack": "webpack --mode production --config ./webpack.config.production.js",
"build-webpack-viewer": "webpack --mode production --config ./webpack.config.viewer.js",
"watch": "concurrently -c \"green,green,gray,gray\" --names \"tsc,srv,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-servers\" \"npm:watch-extra\" \"npm:watch-webpack\"",
@@ -28,7 +28,7 @@
"watch-viewer-debug": "concurrently -c \"green,gray,gray\" --names \"tsc,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack-viewer-debug\"",
"watch-tsc": "tsc --watch --incremental",
"watch-servers": "tsc --build tsconfig.commonjs.json --watch --incremental",
"watch-extra": "cpx \"src/**/*.{scss,html,ico}\" lib/ --watch",
"watch-extra": "cpx \"src/**/*.{scss,html,ico,jpg}\" lib/ --watch",
"watch-webpack": "webpack -w --mode development --stats minimal",
"watch-webpack-viewer": "webpack -w --mode development --stats minimal --config ./webpack.config.viewer.js",
"watch-webpack-viewer-debug": "webpack -w --mode development --stats minimal --config ./webpack.config.viewer.debug.js",
@@ -75,7 +75,9 @@
"node_modules",
"lib"
],
"testURL": "http://localhost/",
"testEnvironmentOptions": {
"url": "http://localhost/"
},
"testRegex": "\\.spec\\.ts$"
},
"author": "Mol* Contributors",
@@ -87,76 +89,79 @@
"Ludovic Autin <autin@scripps.edu>",
"Michal Malý <michal.maly@ibt.cas.cz>",
"Jiří Černý <jiri.cerny@ibt.cas.cz>",
"Panagiotis Tourlas <panagiot_tourlov@hotmail.com>"
"Panagiotis Tourlas <panagiot_tourlov@hotmail.com>",
"Adam Midlik <midlik@gmail.com>",
"Koya Sakuma <koya.sakuma.work@gmail.com>",
"Gianluca Tomasello <giagitom@gmail.com>"
],
"license": "MIT",
"devDependencies": {
"@graphql-codegen/add": "^3.1.1",
"@graphql-codegen/cli": "^2.6.2",
"@graphql-codegen/time": "^3.1.1",
"@graphql-codegen/typescript": "^2.4.7",
"@graphql-codegen/typescript-graphql-files-modules": "^2.1.1",
"@graphql-codegen/typescript-graphql-request": "^4.4.2",
"@graphql-codegen/typescript-operations": "^2.3.4",
"@graphql-codegen/add": "^3.2.1",
"@graphql-codegen/cli": "^2.13.7",
"@graphql-codegen/time": "^3.2.1",
"@graphql-codegen/typescript": "^2.7.4",
"@graphql-codegen/typescript-graphql-files-modules": "^2.2.1",
"@graphql-codegen/typescript-graphql-request": "^4.5.6",
"@graphql-codegen/typescript-operations": "^2.5.4",
"@types/cors": "^2.8.12",
"@types/gl": "^4.1.0",
"@types/jest": "^27.4.1",
"@types/react": "^17.0.43",
"@types/react-dom": "^17.0.14",
"@typescript-eslint/eslint-plugin": "^5.14.0",
"@typescript-eslint/parser": "^5.14.0",
"@types/gl": "^4.1.1",
"@types/jest": "^29.1.2",
"@types/react": "^18.0.21",
"@types/react-dom": "^18.0.6",
"@typescript-eslint/eslint-plugin": "^5.40.0",
"@typescript-eslint/parser": "^5.40.0",
"benchmark": "^2.1.4",
"concurrently": "^7.0.0",
"concurrently": "^7.4.0",
"cpx2": "^4.2.0",
"crypto-browserify": "^3.12.0",
"css-loader": "^6.7.1",
"eslint": "^8.11.0",
"eslint": "^8.25.0",
"extra-watch-webpack-plugin": "^1.0.3",
"file-loader": "^6.2.0",
"fs-extra": "^10.0.1",
"graphql": "^16.3.0",
"http-server": "^14.1.0",
"jest": "^27.5.1",
"mini-css-extract-plugin": "^2.6.0",
"fs-extra": "^10.1.0",
"graphql": "^16.6.0",
"http-server": "^14.1.1",
"jest": "^29.2.0",
"mini-css-extract-plugin": "^2.6.1",
"path-browserify": "^1.0.1",
"raw-loader": "^4.0.2",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"sass": "^1.49.9",
"sass-loader": "^12.6.0",
"simple-git": "^3.3.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"sass": "^1.55.0",
"sass-loader": "^13.1.0",
"simple-git": "^3.14.1",
"stream-browserify": "^3.0.0",
"style-loader": "^3.3.1",
"ts-jest": "^27.1.3",
"typescript": "^4.6.3",
"webpack": "^5.70.0",
"webpack-cli": "^4.9.2"
"ts-jest": "^29.0.3",
"typescript": "^4.8.4",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0"
},
"dependencies": {
"@types/argparse": "^2.0.10",
"@types/benchmark": "^2.1.1",
"@types/benchmark": "^2.1.2",
"@types/compression": "1.7.2",
"@types/express": "^4.17.13",
"@types/node": "^16.11.26",
"@types/node-fetch": "^2.6.1",
"@types/express": "^4.17.14",
"@types/node": "^16.11.66",
"@types/node-fetch": "^2.6.2",
"@types/swagger-ui-dist": "3.30.1",
"argparse": "^2.0.1",
"body-parser": "^1.19.2",
"body-parser": "^1.20.1",
"compression": "^1.7.4",
"cors": "^2.8.5",
"express": "^4.17.3",
"express": "^4.18.2",
"h264-mp4-encoder": "^1.0.12",
"immer": "^9.0.12",
"immutable": "^4.0.0",
"immer": "^9.0.15",
"immutable": "^4.1.0",
"node-fetch": "^2.6.7",
"rxjs": "^7.5.5",
"swagger-ui-dist": "^4.6.2",
"tslib": "^2.3.1",
"rxjs": "^7.5.7",
"swagger-ui-dist": "^4.14.3",
"tslib": "^2.4.0",
"util.promisify": "^1.1.1",
"xhr2": "^0.2.1"
},
"peerDependencies": {
"react": "^18.0.0 || ^17.0.2 || ^16.14.0",
"react-dom": "^18.0.0 || ^17.0.2 || ^16.14.0"
"react": "^18.1.0 || ^17.0.2 || ^16.14.0",
"react-dom": "^18.1.0 || ^17.0.2 || ^16.14.0"
}
}

View File

@@ -58,20 +58,22 @@ class Viewer {
}
static async create(elementOrId: string | HTMLElement, colors = [Color(0x992211), Color(0xDDDDDD)], showButtons = true) {
const o = { ...DefaultViewerOptions, ...{
layoutIsExpanded: false,
layoutShowControls: false,
layoutShowRemoteState: false,
layoutShowSequence: true,
layoutShowLog: false,
layoutShowLeftPanel: true,
const o = {
...DefaultViewerOptions, ...{
layoutIsExpanded: false,
layoutShowControls: false,
layoutShowRemoteState: false,
layoutShowSequence: true,
layoutShowLog: false,
layoutShowLeftPanel: true,
viewportShowExpand: true,
viewportShowControls: false,
viewportShowSettings: false,
viewportShowSelectionMode: false,
viewportShowAnimation: false,
} };
viewportShowExpand: true,
viewportShowControls: false,
viewportShowSettings: false,
viewportShowSelectionMode: false,
viewportShowAnimation: false,
}
};
const defaultSpec = DefaultPluginUISpec();
const spec: PluginUISpec = {
@@ -135,18 +137,16 @@ class Viewer {
}
};
plugin.behaviors.canvas3d.initialized.subscribe(v => {
if (v) {
PluginCommands.Canvas3D.SetSettings(plugin, { settings: {
renderer: {
...plugin.canvas3d!.props.renderer,
backgroundColor: ColorNames.white,
},
camera: {
...plugin.canvas3d!.props.camera,
helper: { axes: { name: 'off', params: {} } }
}
} });
PluginCommands.Canvas3D.SetSettings(plugin, {
settings: {
renderer: {
...plugin.canvas3d!.props.renderer,
backgroundColor: ColorNames.white,
},
camera: {
...plugin.canvas3d!.props.camera,
helper: { axes: { name: 'off', params: {} } }
}
}
});
@@ -166,7 +166,7 @@ class Viewer {
structures.push({ ref: structureProperties?.ref || structure.ref });
}
// remove current structuresfrom hierarchy as they will be merged
// remove current structures from hierarchy as they will be merged
// TODO only works with using loadStructuresFromUrlsAndMerge once
// need some more API metho to work with the hierarchy
this.plugin.managers.structure.hierarchy.updateCurrent(this.plugin.managers.structure.hierarchy.current.structures, 'remove');

View File

@@ -1,5 +1,5 @@
/**
* 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -202,14 +202,14 @@ const InteractionsPreset = StructureRepresentationPresetProvider({
const components = {
ligand: await presetStaticComponent(plugin, structureCell, 'ligand'),
surroundings: await plugin.builders.structure.tryCreateComponentFromSelection(structureCell, ligandSurroundings, `surroundings`),
interactions: await plugin.builders.structure.tryCreateComponentFromSelection(structureCell, ligandPlusSurroundings, `interactions`)
interactions: await presetStaticComponent(plugin, structureCell, 'ligand'),
};
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
const representations = {
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, sizeFactor: 0.3 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
ballAndStick: builder.buildRepresentation(update, components.surroundings, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, sizeFactor: 0.1, sizeAspectRatio: 1 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ball-and-stick' }),
interactions: builder.buildRepresentation(update, components.interactions, { type: InteractionsRepresentationProvider, typeParams: { ...typeParams, material: CustomMaterial }, color: InteractionTypeColorThemeProvider }, { tag: 'interactions' }),
interactions: builder.buildRepresentation(update, components.interactions, { type: InteractionsRepresentationProvider, typeParams: { ...typeParams, material: CustomMaterial, includeParent: true, parentDisplay: 'between' }, color: InteractionTypeColorThemeProvider }, { tag: 'interactions' }),
label: builder.buildRepresentation(update, components.surroundings, { type: 'label', typeParams: { ...typeParams, material: CustomMaterial, background: false, borderWidth: 0.1 }, color: 'uniform', colorParams: { value: Color(0x000000) } }, { tag: 'label' }),
};

View File

@@ -45,15 +45,18 @@ import { Asset } from '../../mol-util/assets';
import { Color } from '../../mol-util/color';
import '../../mol-util/polyfill';
import { ObjectKeys } from '../../mol-util/type-helpers';
import { SaccharideCompIdMapType } from '../../mol-model/structure/structure/carbohydrates/constants';
import { Backgrounds } from '../../extensions/backgrounds';
export { PLUGIN_VERSION as version } from '../../mol-plugin/version';
export { setDebugMode, setProductionMode } from '../../mol-util/debug';
export { setDebugMode, setProductionMode, setTimingMode } from '../../mol-util/debug';
const CustomFormats = [
['g3d', G3dProvider] as const
];
const Extensions = {
'backgrounds': PluginSpec.Behavior(Backgrounds),
'cellpack': PluginSpec.Behavior(CellPack),
'dnatco-confal-pyramids': PluginSpec.Behavior(DnatcoConfalPyramids),
'pdbe-structure-quality-report': PluginSpec.Behavior(PDBeStructureQualityReport),
@@ -85,18 +88,22 @@ const DefaultViewerOptions = {
pickScale: PluginConfig.General.PickScale.defaultValue,
pickPadding: PluginConfig.General.PickPadding.defaultValue,
enableWboit: PluginConfig.General.EnableWboit.defaultValue,
enableDpoit: PluginConfig.General.EnableDpoit.defaultValue,
preferWebgl1: PluginConfig.General.PreferWebGl1.defaultValue,
allowMajorPerformanceCaveat: PluginConfig.General.AllowMajorPerformanceCaveat.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,
viewportShowTrajectoryControls: PluginConfig.Viewport.ShowTrajectoryControls.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,
saccharideCompIdMapType: 'default' as SaccharideCompIdMapType,
};
type ViewerOptions = typeof DefaultViewerOptions;
@@ -153,12 +160,15 @@ export class Viewer {
[PluginConfig.General.PickScale, o.pickScale],
[PluginConfig.General.PickPadding, o.pickPadding],
[PluginConfig.General.EnableWboit, o.enableWboit],
[PluginConfig.General.EnableDpoit, o.enableDpoit],
[PluginConfig.General.PreferWebGl1, o.preferWebgl1],
[PluginConfig.General.AllowMajorPerformanceCaveat, o.allowMajorPerformanceCaveat],
[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.Viewport.ShowTrajectoryControls, o.viewportShowTrajectoryControls],
[PluginConfig.State.DefaultServer, o.pluginStateServer],
[PluginConfig.State.CurrentServer, o.pluginStateServer],
[PluginConfig.VolumeStreaming.DefaultServer, o.volumeStreamingServer],
@@ -166,6 +176,7 @@ export class Viewer {
[PluginConfig.Download.DefaultPdbProvider, o.pdbProvider],
[PluginConfig.Download.DefaultEmdbProvider, o.emdbProvider],
[PluginConfig.Structure.DefaultRepresentationPreset, ViewerAutoPreset.id],
[PluginConfig.Structure.SaccharideCompIdMapType, o.saccharideCompIdMapType],
]
};
@@ -192,7 +203,7 @@ export class Viewer {
return PluginCommands.State.Snapshots.OpenUrl(this.plugin, { url, type });
}
loadStructureFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false, options?: LoadStructureOptions) {
loadStructureFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false, options?: LoadStructureOptions & { label?: string }) {
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
source: {
@@ -201,6 +212,7 @@ export class Viewer {
url: Asset.Url(url),
format: format as any,
isBinary,
label: options?.label,
options: { ...params.source.params.options, representationParams: options?.representationParams as any },
}
}
@@ -397,7 +409,7 @@ export class Viewer {
async loadTrajectory(params: LoadTrajectoryParams) {
const plugin = this.plugin;
let model: StateObjectSelector, coords: StateObjectSelector;
let model: StateObjectSelector;
if (params.model.kind === 'model-data' || params.model.kind === 'model-url') {
const data = params.model.kind === 'model-data'
@@ -415,14 +427,12 @@ export class Viewer {
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 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 provider = plugin.dataFormats.get(params.coordinates.format);
const coords = await provider!.parse(plugin, data);
const trajectory = await plugin.build().toRoot()
.apply(TrajectoryFromModelAndCoordinates, {
@@ -491,4 +501,4 @@ export const ViewerAutoPreset = StructureRepresentationPresetProvider({
return await PresetStructureRepresentations.auto.apply(ref, params, plugin);
}
}
});
});

View File

@@ -38,6 +38,15 @@
viewer.loadPdb('7bv2');
viewer.loadEmdb('EMD-30210', { detail: 6 });
// viewer.loadAllModelsOrAssemblyFromUrl('https://cs.litemol.org/5ire/full', 'mmcif', false, { representationParams: { theme: { globalName: 'operator-name' } } })
// viewer.loadStructureFromUrl('my url', 'pdb', false, {
// representationParams: {
// theme: {
// globalName: 'uniform',
// globalColorParams: { value: 0xff0000 }
// }
// },
// label: 'my structure'
// });
});
</script>
</body>

View File

@@ -46,7 +46,10 @@
}
var debugMode = getParam('debug-mode', '[^&]+').trim() === '1';
if (debugMode) molstar.setDebugMode(debugMode, debugMode);
if (debugMode) molstar.setDebugMode(debugMode);
var timingMode = getParam('timing-mode', '[^&]+').trim() === '1';
if (timingMode) molstar.setTimingMode(timingMode);
var hideControls = getParam('hide-controls', '[^&]+').trim() === '1';
var collapseLeftPanel = getParam('collapse-left-panel', '[^&]+').trim() === '1';
@@ -57,7 +60,9 @@
var pickScale = getParam('pick-scale', '[^&]+').trim();
var pickPadding = getParam('pick-padding', '[^&]+').trim();
var disableWboit = getParam('disable-wboit', '[^&]+').trim() === '1';
var enableDpoit = getParam('enable-dpoit', '[^&]+').trim() === '1';
var preferWebgl1 = getParam('prefer-webgl1', '[^&]+').trim() === '1' || void 0;
var allowMajorPerformanceCaveat = getParam('allow-major-performance-caveat', '[^&]+').trim() === '1';
molstar.Viewer.create('app', {
layoutShowControls: !hideControls,
@@ -71,8 +76,10 @@
pixelScale: parseFloat(pixelScale) || 1,
pickScale: parseFloat(pickScale) || 0.25,
pickPadding: isNaN(parseFloat(pickPadding)) ? 1 : parseFloat(pickPadding),
enableWboit: disableWboit ? false : void 0, // use default value if disable-wboit is not set
enableWboit: (disableWboit || enableDpoit) ? false : void 0, // use default value if disable-wboit is not set
enableDpoit: enableDpoit ? true : void 0,
preferWebgl1: preferWebgl1,
allowMajorPerformanceCaveat: allowMajorPerformanceCaveat,
}).then(viewer => {
var snapshotId = getParam('snapshot-id', '[^&]+').trim();
if (snapshotId) viewer.setRemoteSnapshot(snapshotId);

View File

@@ -1,8 +1,9 @@
#!/usr/bin/env node
/**
* 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 Josh McMenemy <josh.mcmenemy@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as argparse from 'argparse';
@@ -14,7 +15,7 @@ const writeFile = util.promisify(fs.writeFile);
import { DatabaseCollection } from '../../mol-data/db';
import { CCD_Schema } from '../../mol-io/reader/cif/schema/ccd';
import { ensureDataAvailable, readCCD } from './util';
import { DefaultDataOptions, ensureDataAvailable, readCCD } from './util';
function extractIonNames(ccd: DatabaseCollection<CCD_Schema>) {
const ionNames: string[] = [];
@@ -31,11 +32,11 @@ function extractIonNames(ccd: DatabaseCollection<CCD_Schema>) {
function writeIonNamesFile(filePath: string, ionNames: string[]) {
const output = `/**
* 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.
*
* Code-generated ion names params file. Names extracted from CCD components.
*
* @author molstar/chem-comp-dict/create-table cli
* @author molstar/chem-comp-dict/create-ions cli
*/
export const IonNames = new Set(${JSON.stringify(ionNames).replace(/"/g, "'").replace(/,/g, ', ')});
@@ -43,8 +44,8 @@ export const IonNames = new Set(${JSON.stringify(ionNames).replace(/"/g, "'").re
writeFile(filePath, output);
}
async function run(out: string, forceDownload = false) {
await ensureDataAvailable(forceDownload);
async function run(out: string, options = DefaultDataOptions) {
await ensureDataAvailable(options);
const ccd = await readCCD();
const ionNames = extractIonNames(ccd);
if (!fs.existsSync(path.dirname(out))) {
@@ -64,10 +65,15 @@ parser.add_argument('--forceDownload', '-f', {
action: 'store_true',
help: 'Force download of CCD and PVCD.'
});
parser.add_argument('--ccdUrl', '-c', {
help: 'Fetch the CCD from a custom URL. This forces download of the CCD.',
required: false
});
interface Args {
out: string,
forceDownload?: boolean,
ccdUrl?: string
}
const args: Args = parser.parse_args();
run(args.out, args.forceDownload);
run(args.out, { forceDownload: args.forceDownload, ccdUrl: args.ccdUrl });

View File

@@ -14,7 +14,7 @@ const writeFile = util.promisify(fs.writeFile);
import { DatabaseCollection } from '../../mol-data/db';
import { CCD_Schema } from '../../mol-io/reader/cif/schema/ccd';
import { ensureDataAvailable, readCCD } from './util';
import { DefaultDataOptions, ensureDataAvailable, readCCD } from './util';
function extractSaccharideNames(ccd: DatabaseCollection<CCD_Schema>) {
const saccharideNames: string[] = [];
@@ -47,8 +47,8 @@ export const SaccharideNames = new Set(${JSON.stringify(ionNames).replace(/"/g,
writeFile(filePath, output);
}
async function run(out: string, forceDownload = false) {
await ensureDataAvailable(forceDownload);
async function run(out: string, options = DefaultDataOptions) {
await ensureDataAvailable(options);
const ccd = await readCCD();
const saccharideNames = extractSaccharideNames(ccd);
if (!fs.existsSync(path.dirname(out))) {
@@ -68,10 +68,15 @@ parser.add_argument('--forceDownload', '-f', {
action: 'store_true',
help: 'Force download of CCD and PVCD.'
});
parser.add_argument('--ccdUrl', '-c', {
help: 'Fetch the CCD from a custom URL. This forces download of the CCD.',
required: false
});
interface Args {
out: string,
forceDownload?: boolean,
ccdUrl?: string
}
const args: Args = parser.parse_args();
run(args.out, args.forceDownload);
run(args.out, { forceDownload: args.forceDownload, ccdUrl: args.ccdUrl });

View File

@@ -18,7 +18,7 @@ import { SetUtils } from '../../mol-util/set';
import { DefaultMap } from '../../mol-util/map';
import { mmCIF_chemCompBond_schema } from '../../mol-io/reader/cif/schema/mmcif-extras';
import { ccd_chemCompAtom_schema } from '../../mol-io/reader/cif/schema/ccd-extras';
import { ensureDataAvailable, getEncodedCif, readCCD, readPVCD } from './util';
import { DefaultDataOptions, ensureDataAvailable, getEncodedCif, readCCD, readPVCD } from './util';
type CCB = Table<CCD_Schema['chem_comp_bond']>
type CCA = Table<CCD_Schema['chem_comp_atom']>
@@ -239,8 +239,8 @@ function createAtoms(ccd: DatabaseCollection<CCD_Schema>, pvcd: DatabaseCollecti
);
}
async function run(out: string, binary = false, forceDownload = false, ccaOut?: string) {
await ensureDataAvailable(forceDownload);
async function run(out: string, binary = false, options = DefaultDataOptions, ccaOut?: string) {
await ensureDataAvailable(options);
const ccd = await readCCD();
const pvcd = await readPVCD();
@@ -283,12 +283,22 @@ parser.add_argument('--ccaOut', '-a', {
help: 'Optional generated file output path for chem_comp_atom data.',
required: false
});
parser.add_argument('--ccdUrl', '-c', {
help: 'Fetch the CCD from a custom URL. This forces download of the CCD.',
required: false
});
parser.add_argument('--pvcdUrl', '-p', {
help: 'Fetch the PVCD from a custom URL. This forces download of the PVCD.',
required: false
});
interface Args {
out: string,
forceDownload?: boolean,
binary?: boolean,
ccaOut?: string
ccaOut?: string,
ccdUrl?: string,
pvcdUrl?: string
}
const args: Args = parser.parse_args();
run(args.out, args.binary, args.forceDownload, args.ccaOut);
run(args.out, args.binary, { forceDownload: args.forceDownload, ccdUrl: args.ccdUrl, pvcdUrl: args.pvcdUrl }, args.ccaOut);

View File

@@ -35,9 +35,9 @@ export async function ensureAvailable(path: string, url: string, forceDownload =
}
}
export async function ensureDataAvailable(forceDownload = false) {
await ensureAvailable(CCD_PATH, CCD_URL, forceDownload);
await ensureAvailable(PVCD_PATH, PVCD_URL, forceDownload);
export async function ensureDataAvailable(options: DataOptions) {
await ensureAvailable(CCD_PATH, options.ccdUrl || CCD_URL, !!options.ccdUrl || options.forceDownload);
await ensureAvailable(PVCD_PATH, options.pvcdUrl || PVCD_URL, !!options.pvcdUrl || options.forceDownload);
}
export async function readFileAsCollection<S extends Database.Schema>(path: string, schema: S) {
@@ -68,6 +68,16 @@ export function getEncodedCif(name: string, database: Database<Database.Schema>,
return encoder.getData();
}
export type DataOptions = {
ccdUrl?: string,
pvcdUrl?: string,
forceDownload?: boolean
}
export const DefaultDataOptions: DataOptions = {
forceDownload: false
};
const DATA_DIR = path.join(__dirname, '..', '..', '..', '..', 'build/data');
const CCD_PATH = path.join(DATA_DIR, 'components.cif');
const PVCD_PATH = path.join(DATA_DIR, 'aa-variants-v1.cif');

View File

@@ -161,7 +161,7 @@ const MMCIF_DIC_URL = 'http://mmcif.wwpdb.org/dictionaries/ascii/mmcif_pdbx_v50.
const IHM_DIC_PATH = `${DIC_DIR}/ihm-extension.dic`;
const IHM_DIC_URL = 'https://raw.githubusercontent.com/ihmwg/IHM-dictionary/master/ihm-extension.dic';
const MA_DIC_PATH = `${DIC_DIR}/ma-extension.dic`;
const MA_DIC_URL = 'https://raw.githubusercontent.com/ihmwg/MA-dictionary/master/mmcif_ma.dic';
const MA_DIC_URL = 'https://raw.githubusercontent.com/ihmwg/ModelCIF/master/dist/mmcif_ma.dic';
const CIF_CORE_DIC_PATH = `${DIC_DIR}/cif_core.dic`;
const CIF_CORE_DIC_URL = 'https://raw.githubusercontent.com/COMCIFS/cif_core/master/cif_core.dic';

View File

@@ -51,6 +51,7 @@ export function getFieldType(type: string, description: string, values?: string[
case 'operation_expression':
case 'point_symmetry':
case '4x3_matrix':
case '3x4_matrix':
case '3x4_matrices':
case 'point_group':
case 'point_group_helical':
@@ -70,6 +71,7 @@ export function getFieldType(type: string, description: string, values?: string[
case 'ec-type':
case 'ucode-alphanum-csv':
case 'id_list':
case 'entity_id_list':
return ListCol('str', ',', description);
case 'id_list_spc':
return ListCol('str', ' ', description);

View File

@@ -4,16 +4,16 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import * as ReactDOM from 'react-dom';
import { createRoot } from 'react-dom/client';
import { AlphaOrbitalsExample } from '.';
import { ParameterControls } from '../../mol-plugin-ui/controls/parameters';
import { useBehavior } from '../../mol-plugin-ui/hooks/use-behavior';
import { PluginContextContainer } from '../../mol-plugin-ui/plugin';
export function mountControls(orbitals: AlphaOrbitalsExample, parent: Element) {
ReactDOM.render(<PluginContextContainer plugin={orbitals.plugin}>
createRoot(parent).render(<PluginContextContainer plugin={orbitals.plugin}>
<Controls orbitals={orbitals} />
</PluginContextContainer>, parent);
</PluginContextContainer>);
}
function Controls({ orbitals }: { orbitals: AlphaOrbitalsExample }) {

View File

@@ -55,6 +55,17 @@
</a>
</div>
<script>
function getParam(name, regex) {
var r = new RegExp(name + '=' + '(' + regex + ')[&]?', 'i');
return decodeURIComponent(((window.location.search || '').match(r) || [])[1] || '');
}
var debugMode = getParam('debug-mode', '[^&]+').trim() === '1';
if (debugMode) AlphaOrbitalsExample.setDebugMode(debugMode);
var timingMode = getParam('timing-mode', '[^&]+').trim() === '1';
if (timingMode) AlphaOrbitalsExample.setTimingMode(timingMode);
AlphaOrbitalsExample.init('app')
</script>
<!-- __MOLSTAR_ANALYTICS__ -->

View File

@@ -1,5 +1,5 @@
/**
* 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>
*/
@@ -25,6 +25,8 @@ import { DemoMoleculeSDF, DemoOrbitals } from './example-data';
import './index.html';
require('mol-plugin-ui/skin/light.scss');
import { setDebugMode, setTimingMode } from '../../mol-util/debug';
interface DemoInput {
moleculeSdf: string,
basis: Basis,
@@ -80,24 +82,20 @@ export class AlphaOrbitalsExample {
this.plugin.managers.interactivity.setProps({ granularity: 'element' });
this.plugin.behaviors.canvas3d.initialized.subscribe(init => {
if (!init) return;
if (!canComputeGrid3dOnGPU(this.plugin.canvas3d?.webgl)) {
PluginCommands.Toast.Show(this.plugin, {
title: 'Error',
message: `Browser/device does not support required WebGL extension (OES_texture_float).`
});
return;
}
this.load({
moleculeSdf: DemoMoleculeSDF,
...DemoOrbitals
if (!canComputeGrid3dOnGPU(this.plugin.canvas3d?.webgl)) {
PluginCommands.Toast.Show(this.plugin, {
title: 'Error',
message: `Browser/device does not support required WebGL extension (OES_texture_float).`
});
return;
}
mountControls(this, document.getElementById('controls')!);
this.load({
moleculeSdf: DemoMoleculeSDF,
...DemoOrbitals
});
mountControls(this, document.getElementById('controls')!);
}
readonly params = new BehaviorSubject<ParamDefinition.For<Params>>({} as any);
@@ -222,4 +220,6 @@ export class AlphaOrbitalsExample {
}
}
(window as any).AlphaOrbitalsExample = new AlphaOrbitalsExample();
(window as any).AlphaOrbitalsExample = new AlphaOrbitalsExample();
(window as any).AlphaOrbitalsExample.setDebugMode = setDebugMode;
(window as any).AlphaOrbitalsExample.setTimingMode = setTimingMode;

View File

@@ -1,5 +1,5 @@
/**
* 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>
*/
@@ -8,6 +8,7 @@ import { sortArray } from '../../mol-data/util';
import { canComputeGrid3dOnGPU } from '../../mol-gl/compute/grid3d';
import { WebGLContext } from '../../mol-gl/webgl/context';
import { Task } from '../../mol-task';
import { isTimingMode } from '../../mol-util/debug';
import { AlphaOrbital, createGrid, CubeGrid, CubeGridComputationParams, initCubeGrid } from './data-model';
import { gpuComputeAlphaOrbitalsDensityGridValues } from './gpu/compute';
@@ -19,9 +20,9 @@ export function createSphericalCollocationDensityGrid(
let matrix: Float32Array;
if (canComputeGrid3dOnGPU(webgl)) {
// console.time('gpu');
matrix = await gpuComputeAlphaOrbitalsDensityGridValues(ctx, webgl!, cubeGrid, orbitals);
// console.timeEnd('gpu');
if (isTimingMode) webgl.timer.mark('createSphericalCollocationDensityGrid');
matrix = await gpuComputeAlphaOrbitalsDensityGridValues(ctx, webgl, cubeGrid, orbitals);
if (isTimingMode) webgl.timer.markEnd('createSphericalCollocationDensityGrid');
} else {
throw new Error('Missing OES_texture_float WebGL extension.');
}

View File

@@ -1,5 +1,5 @@
/**
* 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.
*
* Inspired by https://github.com/dgasmith/gau2grid.
*
@@ -10,12 +10,11 @@ import { sortArray } from '../../mol-data/util';
import { canComputeGrid3dOnGPU } from '../../mol-gl/compute/grid3d';
import { WebGLContext } from '../../mol-gl/webgl/context';
import { Task } from '../../mol-task';
import { isTimingMode } from '../../mol-util/debug';
import { sphericalCollocation } from './collocation';
import { AlphaOrbital, createGrid, CubeGrid, CubeGridComputationParams, initCubeGrid } from './data-model';
import { gpuComputeAlphaOrbitalsGridValues } from './gpu/compute';
// setDebugMode(true);
export function createSphericalCollocationGrid(
params: CubeGridComputationParams, orbital: AlphaOrbital, webgl?: WebGLContext
): Task<CubeGrid> {
@@ -24,9 +23,9 @@ export function createSphericalCollocationGrid(
let matrix: Float32Array;
if (canComputeGrid3dOnGPU(webgl)) {
// console.time('gpu');
matrix = await gpuComputeAlphaOrbitalsGridValues(ctx, webgl!, cubeGrid, orbital);
// console.timeEnd('gpu');
if (isTimingMode) webgl.timer.mark('createSphericalCollocationGrid');
matrix = await gpuComputeAlphaOrbitalsGridValues(ctx, webgl, cubeGrid, orbital);
if (isTimingMode) webgl.timer.markEnd('createSphericalCollocationGrid');
} else {
// console.time('cpu');
matrix = await sphericalCollocation(cubeGrid, orbital, ctx);

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

View File

@@ -0,0 +1,91 @@
/**
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { PluginBehavior } from '../../mol-plugin/behavior/behavior';
import { PluginConfig } from '../../mol-plugin/config';
import { Color } from '../../mol-util/color/color';
// from https://visualsonline.cancer.gov/details.cfm?imageid=2304, public domain
import image_cells from './images/cells.jpg';
// created with http://alexcpeterson.com/spacescape/
import face_nebula_nx from './skyboxes/nebula/nebula_left2.jpg';
import face_nebula_ny from './skyboxes/nebula/nebula_bottom4.jpg';
import face_nebula_nz from './skyboxes/nebula/nebula_back6.jpg';
import face_nebula_px from './skyboxes/nebula/nebula_right1.jpg';
import face_nebula_py from './skyboxes/nebula/nebula_top3.jpg';
import face_nebula_pz from './skyboxes/nebula/nebula_front5.jpg';
export const Backgrounds = PluginBehavior.create<{ }>({
name: 'extension-backgrounds',
category: 'misc',
display: {
name: 'Backgrounds'
},
ctor: class extends PluginBehavior.Handler<{ }> {
register(): void {
this.ctx.config.set(PluginConfig.Background.Styles, [
[{
variant: {
name: 'radialGradient',
params: {
centerColor: Color(0xFFFFFF),
edgeColor: Color(0x808080),
ratio: 0.2,
coverage: 'viewport',
}
}
}, 'Light Radial Gradient'],
[{
variant: {
name: 'image',
params: {
source: {
name: 'url',
params: image_cells
},
lightness: 0,
saturation: 0,
opacity: 1,
coverage: 'viewport',
}
}
}, 'Normal Cells Image'],
[{
variant: {
name: 'skybox',
params: {
faces: {
name: 'urls',
params: {
nx: face_nebula_nx,
ny: face_nebula_ny,
nz: face_nebula_nz,
px: face_nebula_px,
py: face_nebula_py,
pz: face_nebula_pz,
}
},
lightness: 0,
saturation: 0,
opacity: 1,
blur: 0.3,
}
}
}, 'Purple Nebula Skybox'],
]);
}
update() {
return false;
}
unregister() {
this.ctx.config.set(PluginConfig.Background.Styles, []);
}
},
params: () => ({ })
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

10
src/extensions/backgrounds/typings.d.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
/**
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
declare module '*.jpg' {
const value: string;
export = value;
}

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 Ludovic Autin <ludovic.autin@gmail.com>
@@ -12,7 +12,7 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { Ingredient, CellPacking, CompartmentPrimitives } from './data';
import { getFromPdb, getFromCellPackDB, IngredientFiles, parseCif, parsePDBfile, getStructureMean, getFromOPM } from './util';
import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext, Unit, Trajectory } from '../../mol-model/structure';
import { trajectoryFromMmCIF, MmcifFormat } from '../../mol-model-formats/structure/mmcif';
import { trajectoryFromMmCIF } from '../../mol-model-formats/structure/mmcif';
import { trajectoryFromPDB } from '../../mol-model-formats/structure/pdb';
import { Mat4, Vec3, Quat } from '../../mol-math/linear-algebra';
import { SymmetryOperator } from '../../mol-math/geometry';
@@ -22,17 +22,12 @@ import { ParseCellPack, StructureFromCellpack, DefaultCellPackBaseUrl, Structure
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { getMatFromResamplePoints } from './curve';
import { compile } from '../../mol-script/runtime/query/compiler';
import { CifCategory, CifField } from '../../mol-io/reader/cif';
import { mmCIF_Schema } from '../../mol-io/reader/cif/schema/mmcif';
import { Column } from '../../mol-data/db';
import { createModels } from '../../mol-model-formats/structure/basic/parser';
import { CellpackPackingPreset, CellpackMembranePreset } from './preset';
import { Asset } from '../../mol-util/assets';
import { Color } from '../../mol-util/color';
import { objectForEach } from '../../mol-util/object';
import { readFromFile } from '../../mol-util/data-source';
import { ColorNames } from '../../mol-util/color/names';
import { createBasic } from '../../mol-model-formats/structure/basic/schema';
function getCellPackModelUrl(fileName: string, baseUrl: string) {
return `${baseUrl}/results/${fileName}`;
@@ -142,7 +137,7 @@ async function getStructure(plugin: PluginContext, model: Model, source: Ingredi
function getTransformLegacy(trans: Vec3, rot: Quat) {
const q: Quat = Quat.create(-rot[3], rot[0], rot[1], rot[2]);
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q);
const m: Mat4 = Mat4.fromQuat(Mat4(), q);
Mat4.transpose(m, m);
Mat4.scale(m, m, Vec3.create(-1.0, 1.0, -1.0));
Mat4.setTranslation(m, trans);
@@ -151,7 +146,7 @@ function getTransformLegacy(trans: Vec3, rot: Quat) {
function getTransform(trans: Vec3, rot: Quat) {
const q: Quat = Quat.create(-rot[0], rot[1], rot[2], -rot[3]);
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q);
const m: Mat4 = Mat4.fromQuat(Mat4(), q);
const p: Vec3 = Vec3.create(-trans[0], trans[1], trans[2]);
Mat4.setTranslation(m, p);
return m;
@@ -214,111 +209,10 @@ function getAssembly(name: string, transforms: Mat4[], structure: Structure) {
return builder.getStructure();
}
function getCifCurve(name: string, transforms: Mat4[], model: Model) {
if (!MmcifFormat.is(model.sourceData)) throw new Error('mmcif source data needed');
const { db } = model.sourceData.data;
const d = db.atom_site;
const n = d._rowCount;
const rowCount = n * transforms.length;
const { offsets, count } = model.atomicHierarchy.chainAtomSegments;
const x = d.Cartn_x.toArray();
const y = d.Cartn_y.toArray();
const z = d.Cartn_z.toArray();
const Cartn_x = new Float32Array(rowCount);
const Cartn_y = new Float32Array(rowCount);
const Cartn_z = new Float32Array(rowCount);
const map = new Uint32Array(rowCount);
const seq = new Int32Array(rowCount);
let offset = 0;
for (let c = 0; c < count; ++c) {
const cStart = offsets[c];
const cEnd = offsets[c + 1];
const cLength = cEnd - cStart;
for (let t = 0, tl = transforms.length; t < tl; ++t) {
const m = transforms[t];
for (let j = cStart; j < cEnd; ++j) {
const i = offset + j - cStart;
const xj = x[j], yj = y[j], zj = z[j];
Cartn_x[i] = m[0] * xj + m[4] * yj + m[8] * zj + m[12];
Cartn_y[i] = m[1] * xj + m[5] * yj + m[9] * zj + m[13];
Cartn_z[i] = m[2] * xj + m[6] * yj + m[10] * zj + m[14];
map[i] = j;
seq[i] = t + 1;
}
offset += cLength;
}
}
function multColumn<T>(column: Column<T>) {
const array = column.toArray();
return Column.ofLambda({
value: row => array[map[row]],
areValuesEqual: (rowA, rowB) => map[rowA] === map[rowB] || array[map[rowA]] === array[map[rowB]],
rowCount, schema: column.schema
});
}
const _atom_site: CifCategory.SomeFields<mmCIF_Schema['atom_site']> = {
auth_asym_id: CifField.ofColumn(multColumn(d.auth_asym_id)),
auth_atom_id: CifField.ofColumn(multColumn(d.auth_atom_id)),
auth_comp_id: CifField.ofColumn(multColumn(d.auth_comp_id)),
auth_seq_id: CifField.ofNumbers(seq),
B_iso_or_equiv: CifField.ofColumn(Column.ofConst(0, rowCount, Column.Schema.float)),
Cartn_x: CifField.ofNumbers(Cartn_x),
Cartn_y: CifField.ofNumbers(Cartn_y),
Cartn_z: CifField.ofNumbers(Cartn_z),
group_PDB: CifField.ofColumn(Column.ofConst('ATOM', rowCount, Column.Schema.str)),
id: CifField.ofColumn(Column.ofLambda({
value: row => row,
areValuesEqual: (rowA, rowB) => rowA === rowB,
rowCount, schema: d.id.schema,
})),
label_alt_id: CifField.ofColumn(multColumn(d.label_alt_id)),
label_asym_id: CifField.ofColumn(multColumn(d.label_asym_id)),
label_atom_id: CifField.ofColumn(multColumn(d.label_atom_id)),
label_comp_id: CifField.ofColumn(multColumn(d.label_comp_id)),
label_seq_id: CifField.ofNumbers(seq),
label_entity_id: CifField.ofColumn(Column.ofConst('1', rowCount, Column.Schema.str)),
occupancy: CifField.ofColumn(Column.ofConst(1, rowCount, Column.Schema.float)),
type_symbol: CifField.ofColumn(multColumn(d.type_symbol)),
pdbx_PDB_ins_code: CifField.ofColumn(Column.ofConst('', rowCount, Column.Schema.str)),
pdbx_PDB_model_num: CifField.ofColumn(Column.ofConst(1, rowCount, Column.Schema.int)),
};
const categories = {
entity: CifCategory.ofTable('entity', db.entity),
chem_comp: CifCategory.ofTable('chem_comp', db.chem_comp),
atom_site: CifCategory.ofFields('atom_site', _atom_site)
};
return {
header: name,
categoryNames: Object.keys(categories),
categories
};
}
async function getCurve(plugin: PluginContext, name: string, ingredient: Ingredient, transforms: Mat4[], model: Model) {
const cif = getCifCurve(name, transforms, model);
const curveModelTask = Task.create('Curve Model', async ctx => {
const format = MmcifFormat.fromFrame(cif);
const basic = createBasic(format.data.db, true);
const models = await createModels(basic, format, ctx);
return models.representative;
});
const curveModel = await plugin.runTask(curveModelTask);
// ingredient.source.selection = undefined;
return getStructure(plugin, curveModel, ingredient);
async function getCurve(name: string, transforms: Mat4[], model: Model) {
const structure = Structure.ofModel(model);
const assembly = getAssembly(name, transforms, structure);
return assembly;
}
async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredient, baseUrl: string, ingredientFiles: IngredientFiles, trajCache: TrajectoryCache, location: 'surface' | 'interior' | 'cytoplasme') {
@@ -339,7 +233,7 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
if (!model) return;
let structure: Structure;
if (nbCurve) {
structure = await getCurve(plugin, name, ingredient, getCurveTransforms(ingredient), model);
structure = await getCurve(name, getCurveTransforms(ingredient), model);
} else {
if ((!results || results.length === 0)) return;
let bu: string|undefined = source.bu ? source.bu : undefined;
@@ -363,7 +257,7 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
structure = Structure.transform(structure, m1);
if (ingredient.offset) {
const o: Vec3 = Vec3.create(ingredient.offset[0], ingredient.offset[1], ingredient.offset[2]);
if (!Vec3.exactEquals(o, Vec3.zero())) { // -1, 1, 4e-16 ??
if (!Vec3.exactEquals(o, Vec3())) { // -1, 1, 4e-16 ??
if (location !== 'surface') {
Vec3.negate(o, o);
}
@@ -377,7 +271,7 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
if (!Vec3.exactEquals(p, Vec3.unitZ)) {
const q: Quat = Quat.identity();
Quat.rotationTo(q, p, Vec3.unitZ);
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q);
const m: Mat4 = Mat4.fromQuat(Mat4(), q);
structure = Structure.transform(structure, m);
}
}
@@ -521,6 +415,7 @@ async function loadMembrane(plugin: PluginContext, name: string, state: State, p
.apply(StructureFromAssemblies, undefined, { state: { isGhost: true } })
.commit({ revertOnError: true });
const membraneParams = {
ignoreLight: params.preset.adjustStyle,
representation: params.preset.representation,
};
await CellpackMembranePreset.apply(membrane, membraneParams, plugin);
@@ -537,6 +432,7 @@ async function loadMembrane(plugin: PluginContext, name: string, state: State, p
.apply(StateTransforms.Model.StructureFromModel, props, { state: { isGhost: true } })
.commit({ revertOnError: true });
const membraneParams = {
ignoreLight: params.preset.adjustStyle,
representation: params.preset.representation,
};
await CellpackMembranePreset.apply(membrane, membraneParams, plugin);
@@ -620,6 +516,7 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
const packingParams = {
traceOnly: params.preset.traceOnly,
ignoreLight: params.preset.adjustStyle,
representation: params.preset.representation,
};
await CellpackPackingPreset.apply(packing, packingParams, plugin);
@@ -671,7 +568,8 @@ const LoadCellPackModelParams = {
ingredients: PD.FileList({ accept: '.cif,.bcif,.pdb', label: 'Ingredient files' }),
preset: PD.Group({
traceOnly: PD.Boolean(false),
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['spacefill', 'gaussian-surface', 'point', 'orientation']))
adjustStyle: PD.Boolean(true),
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['spacefill', 'gaussian-surface', 'point', 'orientation'] as const))
}, { isExpanded: true })
};
type LoadCellPackModelParams = PD.Values<typeof LoadCellPackModelParams>
@@ -681,5 +579,43 @@ export const LoadCellPackModel = StateAction.build({
params: LoadCellPackModelParams,
from: PSO.Root
})(({ state, params }, ctx: PluginContext) => Task.create('CellPack Loader', async taskCtx => {
if (params.preset.adjustStyle) {
ctx.managers.interactivity.setProps({ granularity: 'chain' });
ctx.managers.structure.component.setOptions({
... ctx.managers.structure.component.state.options,
visualQuality: 'custom',
ignoreLight: true,
showHydrogens: false,
});
ctx.canvas3d?.setProps({
multiSample: { mode: 'off' },
cameraClipping: { far: false },
renderer: { colorMarker: false },
marking: {
enabled: true,
ghostEdgeStrength: 1,
},
postprocessing: {
occlusion: {
name: 'on',
params: {
samples: 32,
radius: 8,
bias: 1,
blurKernelSize: 15,
resolutionScale: 1,
}
},
outline: {
name: 'on',
params: {
scale: 1,
threshold: 0.33,
color: ColorNames.black,
}
}
}
});
}
await loadPackings(ctx, taskCtx, state, params);
}));

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 Ludovic Autin <ludovic.autin@gmail.com>
@@ -13,7 +13,8 @@ import { CellPackGenerateColorThemeProvider } from './color/generate';
export const CellpackPackingPresetParams = {
traceOnly: PD.Boolean(true),
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['gaussian-surface', 'spacefill', 'point', 'orientation'])),
ignoreLight: PD.Boolean(false),
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['gaussian-surface', 'spacefill', 'point', 'orientation'] as const)),
};
export type CellpackPackingPresetParams = PD.ValuesFor<typeof CellpackPackingPresetParams>
@@ -27,7 +28,9 @@ export const CellpackPackingPreset = StructureRepresentationPresetProvider({
const reprProps = {
ignoreHydrogens: true,
traceOnly: params.traceOnly
traceOnly: params.traceOnly,
instanceGranularity: true,
ignoreLight: params.ignoreLight,
};
const components = {
polymer: await presetStaticComponent(plugin, structureCell, 'polymer')
@@ -37,8 +40,8 @@ export const CellpackPackingPreset = StructureRepresentationPresetProvider({
Object.assign(reprProps, {
quality: 'custom', resolution: 10, radiusOffset: 2, doubleSided: false
});
} else if (params.representation === 'spacefill' && params.traceOnly) {
Object.assign(reprProps, { sizeFactor: 2 });
} else if (params.representation === 'spacefill') {
Object.assign(reprProps, { sizeFactor: params.traceOnly ? 2 : 1 });
}
// default is generated
@@ -57,7 +60,8 @@ export const CellpackPackingPreset = StructureRepresentationPresetProvider({
//
export const CellpackMembranePresetParams = {
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['gaussian-surface', 'spacefill', 'point', 'orientation'])),
ignoreLight: PD.Boolean(false),
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['gaussian-surface', 'spacefill', 'point', 'orientation'] as const)),
};
export type CellpackMembranePresetParams = PD.ValuesFor<typeof CellpackMembranePresetParams>
@@ -71,6 +75,8 @@ export const CellpackMembranePreset = StructureRepresentationPresetProvider({
const reprProps = {
ignoreHydrogens: true,
instanceGranularity: true,
ignoreLight: params.ignoreLight,
};
const components = {
membrane: await presetStaticComponent(plugin, structureCell, 'all', { label: 'Membrane' })
@@ -84,7 +90,7 @@ export const CellpackMembranePreset = StructureRepresentationPresetProvider({
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, {});
const representations = {
membrane: builder.buildRepresentation(update, components.membrane, { type: 'gaussian-surface', typeParams: { ...typeParams, ...reprProps }, color: 'uniform', colorParams: { value: ColorNames.lightgrey } }, { tag: 'all' })
membrane: builder.buildRepresentation(update, components.membrane, { type: params.representation, typeParams: { ...typeParams, ...reprProps }, color: 'uniform', colorParams: { value: ColorNames.lightgrey } }, { tag: 'all' })
};
await update.commit({ revertOnError: true });

View File

@@ -8,7 +8,7 @@
import { ConfalPyramidsColorThemeProvider } from './color';
import { ConfalPyramids, ConfalPyramidsProvider } from './property';
import { ConfalPyramidsRepresentationProvider } from './representation';
import { Loci } from '../../../mol-model/loci';
import { ConfalPyramidsTypes } from './types';
import { PluginBehavior } from '../../../mol-plugin/behavior/behavior';
import { StructureRepresentationPresetProvider, PresetStructureRepresentations } from '../../../mol-plugin-state/builder/structure/representation-preset';
import { StateObjectRef } from '../../../mol-state';
@@ -56,21 +56,10 @@ export const DnatcoConfalPyramids = PluginBehavior.create<{ autoAttach: boolean,
description: 'Schematic depiction of conformer class and confal value.',
},
ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean, showToolTip: boolean }> {
private provider = ConfalPyramidsProvider;
private labelConfalPyramids = {
label: (loci: Loci): string | undefined => {
if (!this.params.showToolTip) return void 0;
/* TODO: Implement this */
return void 0;
}
};
register(): void {
this.ctx.customModelProperties.register(this.provider, this.params.autoAttach);
this.ctx.managers.lociLabels.addProvider(this.labelConfalPyramids);
this.ctx.representation.structure.themes.colorThemeRegistry.add(ConfalPyramidsColorThemeProvider);
this.ctx.representation.structure.registry.add(ConfalPyramidsRepresentationProvider);
@@ -88,7 +77,6 @@ export const DnatcoConfalPyramids = PluginBehavior.create<{ autoAttach: boolean,
unregister() {
this.ctx.customModelProperties.unregister(ConfalPyramidsProvider.descriptor.name);
this.ctx.managers.lociLabels.removeProvider(this.labelConfalPyramids);
this.ctx.representation.structure.registry.remove(ConfalPyramidsRepresentationProvider);
this.ctx.representation.structure.themes.colorThemeRegistry.remove(ConfalPyramidsColorThemeProvider);
@@ -101,3 +89,13 @@ export const DnatcoConfalPyramids = PluginBehavior.create<{ autoAttach: boolean,
showToolTip: PD.Boolean(true)
})
});
export function confalPyramidLabel(halfPyramid: ConfalPyramidsTypes.HalfPyramid) {
const { step } = halfPyramid;
return `
<b>${step.auth_asym_id_1}</b> |
<b>${step.label_comp_id_1} ${step.auth_seq_id_1}${step.PDB_ins_code_1}${step.label_alt_id_1.length > 0 ? ` (alt ${step.label_alt_id_1})` : ''}
${step.label_comp_id_2} ${step.auth_seq_id_2}${step.PDB_ins_code_2}${step.label_alt_id_2.length > 0 ? ` (alt ${step.label_alt_id_2})` : ''} </b><br />
<i>NtC:</i> ${step.NtC} | <i>Confal score:</i> ${step.confal_score} | <i>RMSD:</i> ${step.rmsd.toFixed(2)}
`;
}

View File

@@ -247,8 +247,8 @@ export function ConfalPyramidsColorTheme(ctx: ThemeDataContext, props: PD.Values
function color(location: Location, isSecondary: boolean): Color {
if (CPT.isLocation(location)) {
const { pyramid, isLower } = location.data;
const key = pyramid.NtC + `_${isLower ? 'Lwr' : 'Upr'}` as keyof PyramidsColors;
const { step, isLower } = location.data;
const key = step.NtC + `_${isLower ? 'Lwr' : 'Upr'}` as keyof PyramidsColors;
return colorMap[key] ?? ErrorColor;
}

View File

@@ -16,7 +16,7 @@ import { PropertyWrapper } from '../../../mol-model-props/common/wrapper';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { MmcifFormat } from '../../../mol-model-formats/structure/mmcif';
export type ConfalPyramids = PropertyWrapper<CPT.PyramidsData | undefined >;
export type ConfalPyramids = PropertyWrapper<CPT.Steps | undefined>;
export namespace ConfalPyramids {
export const Schema = {
@@ -105,34 +105,42 @@ export const ConfalPyramidsProvider: CustomModelProperty.Provider<ConfalPyramids
type StepsSummaryTable = Table<typeof ConfalPyramids.Schema.ndb_struct_ntc_step_summary>;
function createPyramidsFromCif(model: Model,
steps: Table<typeof ConfalPyramids.Schema.ndb_struct_ntc_step>,
stepsSummary: StepsSummaryTable): CPT.PyramidsData {
const pyramids = new Array<CPT.Pyramid>();
const names = new Map<string, number>();
const locations = new Array<CPT.Location>();
let hasMultipleModels = false;
function createPyramidsFromCif(
model: Model,
cifSteps: Table<typeof ConfalPyramids.Schema.ndb_struct_ntc_step>,
stepsSummary: StepsSummaryTable
): CPT.Steps {
const steps = new Array<CPT.Step>();
const mapping = new Array<CPT.MappedChains>();
const {
id, PDB_model_number, name,
auth_asym_id_1, auth_seq_id_1, label_comp_id_1, label_alt_id_1, PDB_ins_code_1,
auth_asym_id_2, auth_seq_id_2, label_comp_id_2, label_alt_id_2, PDB_ins_code_2,
_rowCount } = steps;
_rowCount
} = cifSteps;
if (_rowCount !== stepsSummary._rowCount) throw new Error('Inconsistent mmCIF data');
for (let i = 0; i < _rowCount; i++) {
const model_num = PDB_model_number.value(i);
if (model_num !== model.modelNum)
hasMultipleModels = true;
const {
NtC,
confal_score,
rmsd
} = getSummaryData(id.value(i), i, stepsSummary);
const modelNum = PDB_model_number.value(i);
const chainId = auth_asym_id_1.value(i);
const seqId = auth_seq_id_1.value(i);
const modelIdx = modelNum - 1;
const { _NtC, _confal_score } = getNtCAndConfalScore(id.value(i), i, stepsSummary);
if (mapping.length <= modelIdx || !mapping[modelIdx])
mapping[modelIdx] = new Map<string, CPT.MappedResidues>();
const pyramid = {
PDB_model_number: model_num,
const step = {
PDB_model_number: modelNum,
name: name.value(i),
auth_asym_id_1: auth_asym_id_1.value(i),
auth_seq_id_1: auth_seq_id_1.value(i),
auth_asym_id_1: chainId,
auth_seq_id_1: seqId,
label_comp_id_1: label_comp_id_1.value(i),
label_alt_id_1: label_alt_id_1.value(i),
PDB_ins_code_1: PDB_ins_code_1.value(i),
@@ -141,30 +149,41 @@ function createPyramidsFromCif(model: Model,
label_comp_id_2: label_comp_id_2.value(i),
label_alt_id_2: label_alt_id_2.value(i),
PDB_ins_code_2: PDB_ins_code_2.value(i),
confal_score: _confal_score,
NtC: _NtC
confal_score,
NtC,
rmsd,
};
pyramids.push(pyramid);
names.set(pyramid.name, pyramids.length - 1);
steps.push(step);
locations.push(CPT.Location(pyramid, false));
locations.push(CPT.Location(pyramid, true));
const mappedChains = mapping[modelIdx];
const residuesOnChain = mappedChains.get(chainId) ?? new Map<number, number[]>();
const stepsForResidue = residuesOnChain.get(seqId) ?? [];
stepsForResidue.push(steps.length - 1);
residuesOnChain.set(seqId, stepsForResidue);
mappedChains.set(chainId, residuesOnChain);
mapping[modelIdx] = mappedChains;
}
return { pyramids, names, locations, hasMultipleModels };
return { steps, mapping };
}
function getNtCAndConfalScore(id: number, i: number, stepsSummary: StepsSummaryTable) {
const { step_id, confal_score, assigned_NtC } = stepsSummary;
function getSummaryData(id: number, i: number, stepsSummary: StepsSummaryTable) {
const {
step_id,
confal_score,
assigned_NtC,
cartesian_rmsd_closest_NtC_representative,
} = stepsSummary;
// Assume that step_ids in ntc_step_summary are in the same order as steps in ntc_step
for (let j = i; j < stepsSummary._rowCount; j++) {
if (id === step_id.value(j)) return { _NtC: assigned_NtC.value(j), _confal_score: confal_score.value(j) };
if (id === step_id.value(j)) return { NtC: assigned_NtC.value(j), confal_score: confal_score.value(j), rmsd: cartesian_rmsd_closest_NtC_representative.value(j) };
}
// Safety net for cases where the previous assumption is not met
for (let j = 0; j < i; j++) {
if (id === step_id.value(j)) return { _NtC: assigned_NtC.value(j), _confal_score: confal_score.value(j) };
if (id === step_id.value(j)) return { NtC: assigned_NtC.value(j), confal_score: confal_score.value(j), rmsd: cartesian_rmsd_closest_NtC_representative.value(j) };
}
throw new Error('Inconsistent mmCIF data');
}

View File

@@ -6,7 +6,7 @@
*/
import { ConfalPyramids, ConfalPyramidsProvider } from './property';
import { ConfalPyramidsUtil } from './util';
import { ConfalPyramidsIterator } from './util';
import { ConfalPyramidsTypes as CPT } from './types';
import { Interval } from '../../../mol-data/int';
import { Mesh } from '../../../mol-geo/geometry/mesh/mesh';
@@ -16,14 +16,14 @@ import { PrimitiveBuilder } from '../../../mol-geo/primitive/primitive';
import { LocationIterator } from '../../../mol-geo/util/location-iterator';
import { Mat4, Vec3 } from '../../../mol-math/linear-algebra';
import { EmptyLoci, Loci } from '../../../mol-model/loci';
import { Structure, StructureProperties, Unit } from '../../../mol-model/structure';
import { Structure, Unit } from '../../../mol-model/structure';
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
import { Representation, RepresentationContext, RepresentationParamsGetter } from '../../../mol-repr/representation';
import { StructureRepresentation, StructureRepresentationProvider, StructureRepresentationStateBuilder, UnitsRepresentation } from '../../../mol-repr/structure/representation';
import { UnitsMeshParams, UnitsMeshVisual, UnitsVisual } from '../../../mol-repr/structure/units-visual';
import { VisualUpdateState } from '../../../mol-repr/util';
import { VisualContext } from '../../../mol-repr/visual';
import { getAltResidueLociFromId, StructureGroup } from '../../../mol-repr/structure/visual/util/common';
import { StructureGroup } from '../../../mol-repr/structure/visual/util/common';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { Theme, ThemeRegistryContext } from '../../../mol-theme/theme';
import { NullLocation } from '../../../mol-model/location';
@@ -32,6 +32,12 @@ const t = Mat4.identity();
const w = Vec3.zero();
const mp = Vec3.zero();
const posO3 = Vec3();
const posP = Vec3();
const posOP1 = Vec3();
const posOP2 = Vec3();
const posO5 = Vec3();
function calcMidpoint(mp: Vec3, v: Vec3, w: Vec3) {
Vec3.sub(mp, v, w);
Vec3.scale(mp, mp, 0.5);
@@ -53,64 +59,76 @@ function createConfalPyramidsIterator(structureGroup: StructureGroup): LocationI
const { structure, group } = structureGroup;
const instanceCount = group.units.length;
const prop = ConfalPyramidsProvider.get(structure.model).value;
if (prop === undefined || prop.data === undefined) {
return LocationIterator(0, 1, 1, () => NullLocation);
}
const data = ConfalPyramidsProvider.get(structure.model)?.value?.data;
if (!data) return LocationIterator(0, 1, 1, () => NullLocation);
const { locations } = prop.data;
const halfPyramidsCount = data.steps.length * 2;
const getLocation = (groupIndex: number, instanceIndex: number) => {
if (locations.length <= groupIndex) return NullLocation;
return locations[groupIndex];
if (halfPyramidsCount <= groupIndex) return NullLocation;
const idx = Math.floor(groupIndex / 2); // Map groupIndex to a step, see createConfalPyramidsMesh() for full explanation
return CPT.Location(data.steps[idx], groupIndex % 2 === 1);
};
return LocationIterator(locations.length, instanceCount, 1, getLocation);
return LocationIterator(halfPyramidsCount, instanceCount, 1, getLocation);
}
function createConfalPyramidsMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<ConfalPyramidsMeshParams>, mesh?: Mesh) {
if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh);
const prop = ConfalPyramidsProvider.get(structure.model).value;
if (prop === undefined || prop.data === undefined) return Mesh.createEmpty(mesh);
const data = ConfalPyramidsProvider.get(structure.model)?.value?.data;
if (!data) return Mesh.createEmpty(mesh);
const { pyramids } = prop.data;
if (pyramids.length === 0) return Mesh.createEmpty(mesh);
const { steps, mapping } = data;
if (steps.length === 0) return Mesh.createEmpty(mesh);
const vertexCount = (6 * steps.length) / mapping.length;
const mb = MeshBuilder.createState(512, 512, mesh);
const mb = MeshBuilder.createState(vertexCount, vertexCount / 10, mesh);
const handler = (pyramid: CPT.Pyramid, first: ConfalPyramidsUtil.FirstResidueAtoms, second: ConfalPyramidsUtil.SecondResidueAtoms, firsLocIndex: number, secondLocIndex: number) => {
if (firsLocIndex === -1 || secondLocIndex === -1)
throw new Error('Invalid location index');
const it = new ConfalPyramidsIterator(structure, unit);
while (it.hasNext) {
const allPoints = it.move();
const scale = (pyramid.confal_score - 20.0) / 100.0;
const O3 = first.O3.pos;
const OP1 = second.OP1.pos; const OP2 = second.OP2.pos; const O5 = second.O5.pos; const P = second.P.pos;
for (const points of allPoints) {
const { O3, P, OP1, OP2, O5, confalScore } = points;
const scale = (confalScore - 20.0) / 100.0;
// Steps can be drawn in a different order than they are stored.
// To make sure that we can get from the drawn pyramid back to the step in represents,
// we need to use an appropriate groupId. The stepIdx passed from the iterator
// is an index into the array of all steps in the structure.
// Since a step is drawn as two "half-pyramids" we need two ids to map to a single step.
// To do that, we just multiply the index by 2. idx*2 marks the "upper" half-pyramid,
// (idx*2)+1 the "lower" half-pyramid.
const groupIdx = points.stepIdx * 2;
shiftVertex(O3, P, scale);
shiftVertex(OP1, P, scale);
shiftVertex(OP2, P, scale);
shiftVertex(O5, P, scale);
calcMidpoint(mp, O3, O5);
unit.conformation.invariantPosition(O3, posO3);
unit.conformation.invariantPosition(P, posP);
unit.conformation.invariantPosition(OP1, posOP1);
unit.conformation.invariantPosition(OP2, posOP2);
unit.conformation.invariantPosition(O5, posO5);
mb.currentGroup = firsLocIndex;
let pb = PrimitiveBuilder(3);
/* Upper part (for first residue in step) */
pb.add(O3, OP1, OP2);
pb.add(O3, mp, OP1);
pb.add(O3, OP2, mp);
MeshBuilder.addPrimitive(mb, t, pb.getPrimitive());
shiftVertex(posO3, posP, scale);
shiftVertex(posOP1, posP, scale);
shiftVertex(posOP2, posP, scale);
shiftVertex(posO5, posP, scale);
calcMidpoint(mp, posO3, posO5);
/* Lower part (for second residue in step */
mb.currentGroup = secondLocIndex;
pb = PrimitiveBuilder(3);
pb.add(mp, O5, OP1);
pb.add(mp, OP2, O5);
pb.add(O5, OP2, OP1);
MeshBuilder.addPrimitive(mb, t, pb.getPrimitive());
};
mb.currentGroup = groupIdx;
let pb = PrimitiveBuilder(3);
/* Upper part (for first residue in step) */
pb.add(posO3, posOP1, posOP2);
pb.add(posO3, mp, posOP1);
pb.add(posO3, posOP2, mp);
MeshBuilder.addPrimitive(mb, t, pb.getPrimitive());
const walker = new ConfalPyramidsUtil.UnitWalker(structure, unit, handler);
walker.walk();
/* Lower part (for second residue in step) */
mb.currentGroup = groupIdx + 1;
pb = PrimitiveBuilder(3);
pb.add(mp, posO5, posOP1);
pb.add(mp, posOP2, posO5);
pb.add(posO5, posOP2, posOP1);
MeshBuilder.addPrimitive(mb, t, pb.getPrimitive());
}
}
return MeshBuilder.getMesh(mb);
}
@@ -124,16 +142,17 @@ function getConfalPyramidLoci(pickingId: PickingId, structureGroup: StructureGro
const unit = structureGroup.group.units[instanceId];
if (!Unit.isAtomic(unit)) return EmptyLoci;
const prop = ConfalPyramidsProvider.get(structure.model).value;
if (prop === undefined || prop.data === undefined) return EmptyLoci;
const data = ConfalPyramidsProvider.get(structure.model)?.value?.data;
if (!data) return EmptyLoci;
const { locations } = prop.data;
const halfPyramidsCount = data.steps.length * 2;
if (locations.length <= groupId) return EmptyLoci;
const altId = StructureProperties.atom.label_alt_id(CPT.toElementLocation(locations[groupId]));
const rI = unit.residueIndex[locations[groupId].element.element];
if (halfPyramidsCount <= groupId) return EmptyLoci;
return getAltResidueLociFromId(structure, unit, rI, altId);
const idx = Math.floor(groupId / 2); // Map groupIndex to a step, see createConfalPyramidsMesh() for full explanation
const step = data.steps[idx];
return CPT.Loci({ step, isLower: groupId % 2 === 1 }, [{}]);
}
function eachConfalPyramid(loci: Loci, structureGroup: StructureGroup, apply: (interval: Interval) => boolean) {

View File

@@ -6,10 +6,13 @@
*/
import { DataLocation } from '../../../mol-model/location';
import { ElementIndex, Structure, StructureElement, Unit } from '../../../mol-model/structure';
import { DataLoci } from '../../../mol-model/loci';
import { confalPyramidLabel } from './behavior';
export namespace ConfalPyramidsTypes {
export type Pyramid = {
export const DataTag = 'dnatco-confal-half-pyramid';
export type Step = {
PDB_model_number: number,
name: string,
auth_asym_id_1: string,
@@ -23,38 +26,40 @@ export namespace ConfalPyramidsTypes {
label_alt_id_2: string,
PDB_ins_code_2: string,
confal_score: number,
NtC: string
NtC: string,
rmsd: number,
}
export interface PyramidsData {
pyramids: Array<Pyramid>,
names: Map<string, number>,
locations: Array<Location>,
hasMultipleModels: boolean
export type MappedChains = Map<string, MappedResidues>;
export type MappedResidues = Map<number, number[]>;
export interface Steps {
steps: Array<Step>,
mapping: MappedChains[],
}
export interface LocationData {
readonly pyramid: Pyramid
readonly isLower: boolean;
export interface HalfPyramid {
step: Step,
isLower: boolean,
}
export interface Element {
structure: Structure;
unit: Unit.Atomic;
element: ElementIndex;
}
export interface Location extends DataLocation<HalfPyramid, {}> {}
export interface Location extends DataLocation<LocationData, Element> {}
export function Location(pyramid: Pyramid, isLower: boolean, structure?: Structure, unit?: Unit.Atomic, element?: ElementIndex) {
return DataLocation('pyramid', { pyramid, isLower }, { structure: structure as any, unit: unit as any, element: element as any });
export function Location(step: Step, isLower: boolean) {
return DataLocation(DataTag, { step, isLower }, {});
}
export function isLocation(x: any): x is Location {
return !!x && x.kind === 'data-location' && x.tag === 'pyramid';
return !!x && x.kind === 'data-location' && x.tag === DataTag;
}
export function toElementLocation(location: Location) {
return StructureElement.Location.create(location.element.structure, location.element.unit, location.element.element);
export interface Loci extends DataLoci<HalfPyramid, {}> {}
export function Loci(data: HalfPyramid, elements: ReadonlyArray<{}>): Loci {
return DataLoci(DataTag, data, elements, undefined, () => confalPyramidLabel(data));
}
export function isLoci(x: any): x is Loci {
return !!x && x.kind === 'data-loci' && x.tag === DataTag;
}
}

View File

@@ -8,288 +8,120 @@
import { ConfalPyramidsProvider } from './property';
import { ConfalPyramidsTypes as CPT } from './types';
import { Segmentation } from '../../../mol-data/int';
import { Vec3 } from '../../../mol-math/linear-algebra';
import { ChainIndex, ElementIndex, ResidueIndex, Structure, StructureElement, StructureProperties, Unit } from '../../../mol-model/structure';
export namespace ConfalPyramidsUtil {
type Residue = Segmentation.Segment<ResidueIndex>;
type Residue = Segmentation.Segment<ResidueIndex>;
export type AtomInfo = {
pos: Vec3,
index: ElementIndex,
fakeAltId: string,
};
export type Pyramid = {
O3: ElementIndex,
P: ElementIndex,
OP1: ElementIndex,
OP2: ElementIndex,
O5: ElementIndex,
confalScore: number,
stepIdx: number,
};
export type FirstResidueAtoms = {
O3: AtomInfo,
};
const EmptyStepIndices = new Array<number>();
export type SecondResidueAtoms = {
OP1: AtomInfo,
OP2: AtomInfo,
O5: AtomInfo,
P: AtomInfo,
};
function copyResidue(r?: Residue) {
return r ? { index: r.index, start: r.start, end: r.end } : void 0;
}
type ResidueInfo = {
PDB_model_num: number,
asym_id: string,
auth_asym_id: string,
seq_id: number,
auth_seq_id: number,
comp_id: string,
alt_id: string,
ins_code: string,
};
function getAtomIndex(loc: StructureElement.Location, residue: Residue, names: string[], altId: string): ElementIndex {
for (let eI = residue.start; eI < residue.end; eI++) {
loc.element = loc.unit.elements[eI];
const elName = StructureProperties.atom.label_atom_id(loc);
const elAltId = StructureProperties.atom.label_alt_id(loc);
export type Handler = (pyramid: CPT.Pyramid, first: FirstResidueAtoms, second: SecondResidueAtoms, firstLocIndex: number, secondLocIndex: number) => void;
function residueInfoFromLocation(loc: StructureElement.Location): ResidueInfo {
return {
PDB_model_num: StructureProperties.unit.model_num(loc),
asym_id: StructureProperties.chain.label_asym_id(loc),
auth_asym_id: StructureProperties.chain.auth_asym_id(loc),
seq_id: StructureProperties.residue.label_seq_id(loc),
auth_seq_id: StructureProperties.residue.auth_seq_id(loc),
comp_id: StructureProperties.atom.label_comp_id(loc),
alt_id: StructureProperties.atom.label_alt_id(loc),
ins_code: StructureProperties.residue.pdbx_PDB_ins_code(loc)
};
if (names.includes(elName) && (elAltId === altId || elAltId.length === 0))
return loc.element;
}
export function hasMultipleModels(unit: Unit.Atomic): boolean {
return -1 as ElementIndex;
}
function getPyramid(loc: StructureElement.Location, one: Residue, two: Residue, altIdOne: string, altIdTwo: string, confalScore: number, stepIdx: number): Pyramid {
const O3 = getAtomIndex(loc, one, ['O3\'', 'O3*'], altIdOne);
const P = getAtomIndex(loc, two, ['P'], altIdTwo);
const OP1 = getAtomIndex(loc, two, ['OP1'], altIdTwo);
const OP2 = getAtomIndex(loc, two, ['OP2'], altIdTwo);
const O5 = getAtomIndex(loc, two, ['O5\'', 'O5*'], altIdTwo);
return { O3, P, OP1, OP2, O5, confalScore, stepIdx };
}
export class ConfalPyramidsIterator {
private chainIt: Segmentation.SegmentIterator<ChainIndex>;
private residueIt: Segmentation.SegmentIterator<ResidueIndex>;
private residueOne?: Residue;
private residueTwo: Residue;
private data?: CPT.Steps;
private loc: StructureElement.Location;
private getStepIndices(r: Residue) {
this.loc.element = this.loc.unit.elements[r.start];
const modelIdx = StructureProperties.unit.model_num(this.loc) - 1;
const chainId = StructureProperties.chain.auth_asym_id(this.loc);
const seqId = StructureProperties.residue.auth_seq_id(this.loc);
const chains = this.data!.mapping[modelIdx];
if (!chains) return EmptyStepIndices;
const residues = chains.get(chainId);
if (!residues) return EmptyStepIndices;
return residues.get(seqId) ?? EmptyStepIndices;
}
private moveStep() {
this.residueOne = copyResidue(this.residueTwo);
this.residueTwo = copyResidue(this.residueIt.move())!;
return this.toPyramids(this.residueOne!, this.residueTwo);
}
private toPyramids(one: Residue, two: Residue) {
const indices = this.getStepIndices(one);
const points = [];
for (const idx of indices) {
const step = this.data!.steps[idx];
points.push(getPyramid(this.loc, one, two, step.label_alt_id_1, step.label_alt_id_2, step.confal_score, idx));
}
return points;
}
constructor(structure: Structure, unit: Unit) {
this.chainIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, unit.elements);
this.residueIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements);
const prop = ConfalPyramidsProvider.get(unit.model).value;
if (prop === undefined || prop.data === undefined) throw new Error('No custom properties data');
return prop.data.hasMultipleModels;
this.data = prop?.data;
if (this.chainIt.hasNext) {
this.residueIt.setSegment(this.chainIt.move());
if (this.residueIt.hasNext)
this.residueTwo = this.residueIt.move();
}
this.loc = StructureElement.Location.create(structure, unit, -1 as ElementIndex);
}
function getPossibleAltIds(residue: Residue, structure: Structure, unit: Unit.Atomic): string[] {
const possibleAltIds: string[] = [];
const loc = StructureElement.Location.create(structure, unit, -1 as ElementIndex);
for (let rI = residue.start; rI <= residue.end - 1; rI++) {
loc.element = unit.elements[rI];
const altId = StructureProperties.atom.label_alt_id(loc);
if (altId !== '' && !possibleAltIds.includes(altId)) possibleAltIds.push(altId);
}
return possibleAltIds;
get hasNext() {
if (!this.data)
return false;
return this.residueIt.hasNext
? true
: this.chainIt.hasNext;
}
class Utility {
protected getPyramidByName(name: string): { pyramid: CPT.Pyramid | undefined, index: number } {
const index = this.data.names.get(name);
if (index === undefined) return { pyramid: undefined, index: -1 };
return { pyramid: this.data.pyramids[index], index };
move() {
if (this.residueIt.hasNext) {
return this.moveStep();
} else {
this.residueIt.setSegment(this.chainIt.move());
return this.moveStep();
}
protected stepToName(entry_id: string, modelNum: number, locFirst: StructureElement.Location, locSecond: StructureElement.Location, fakeAltId_1: string, fakeAltId_2: string) {
const first = residueInfoFromLocation(locFirst);
const second = residueInfoFromLocation(locSecond);
const model_id = this.hasMultipleModels ? `-m${modelNum}` : '';
const alt_id_1 = fakeAltId_1 !== '' ? `.${fakeAltId_1}` : (first.alt_id.length ? `.${first.alt_id}` : '');
const alt_id_2 = fakeAltId_2 !== '' ? `.${fakeAltId_2}` : (second.alt_id.length ? `.${second.alt_id}` : '');
const ins_code_1 = first.ins_code.length ? `.${first.ins_code}` : '';
const ins_code_2 = second.ins_code.length ? `.${second.ins_code}` : '';
return `${entry_id}${model_id}_${first.auth_asym_id}_${first.comp_id}${alt_id_1}_${first.auth_seq_id}${ins_code_1}_${second.comp_id}${alt_id_2}_${second.auth_seq_id}${ins_code_2}`;
}
constructor(unit: Unit.Atomic) {
const prop = ConfalPyramidsProvider.get(unit.model).value;
if (prop === undefined || prop.data === undefined) throw new Error('No custom properties data');
this.data = prop.data;
this.hasMultipleModels = hasMultipleModels(unit);
this.entryId = unit.model.entryId.toLowerCase();
this.modelNum = unit.model.modelNum;
}
protected readonly data: CPT.PyramidsData;
protected readonly hasMultipleModels: boolean;
protected readonly entryId: string;
protected readonly modelNum: number;
}
export class UnitWalker extends Utility {
private getAtomIndices(names: string[], residue: Residue): ElementIndex[] {
const indices: ElementIndex[] = [];
const loc = StructureElement.Location.create(this.structure, this.unit, -1 as ElementIndex);
for (let rI = residue.start; rI <= residue.end - 1; rI++) {
loc.element = this.unit.elements[rI];
const thisName = StructureProperties.atom.label_atom_id(loc);
if (names.includes(thisName)) indices.push(loc.element);
}
if (indices.length === 0) {
let namesStr = '';
for (const n of names)
namesStr += `${n} `;
throw new Error(`Element [${namesStr}] not found on residue ${residue.index}`);
}
return indices;
}
private getAtomPositions(indices: ElementIndex[]): Vec3[] {
const pos = this.unit.conformation.invariantPosition;
const positions: Vec3[] = [];
for (const eI of indices) {
const v = Vec3.zero();
pos(eI, v);
positions.push(v);
}
return positions;
}
private handleStep(firstAtoms: FirstResidueAtoms[], secondAtoms: SecondResidueAtoms[]) {
const modelNum = this.hasMultipleModels ? this.modelNum : -1;
let ok = false;
const firstLoc = StructureElement.Location.create(this.structure, this.unit, -1 as ElementIndex);
const secondLoc = StructureElement.Location.create(this.structure, this.unit, -1 as ElementIndex);
for (let i = 0; i < firstAtoms.length; i++) {
const first = firstAtoms[i];
for (let j = 0; j < secondAtoms.length; j++) {
const second = secondAtoms[j];
firstLoc.element = first.O3.index;
secondLoc.element = second.OP1.index;
const name = this.stepToName(this.entryId, modelNum, firstLoc, secondLoc, first.O3.fakeAltId, second.OP1.fakeAltId);
const { pyramid, index } = this.getPyramidByName(name);
if (pyramid !== undefined) {
const setLoc = (loc: CPT.Location, eI: ElementIndex) => {
loc.element.structure = this.structure;
loc.element.unit = this.unit;
loc.element.element = eI;
};
const locIndex = index * 2;
setLoc(this.data.locations[locIndex], firstLoc.element);
setLoc(this.data.locations[locIndex + 1], secondLoc.element);
this.handler(pyramid, first, second, locIndex, locIndex + 1);
ok = true;
}
}
}
if (!ok) throw new Error('Bogus step');
}
private processFirstResidue(residue: Residue, possibleAltIds: string[]) {
const indO3 = this.getAtomIndices(['O3\'', 'O3*'], residue);
const posO3 = this.getAtomPositions(indO3);
const altPos: FirstResidueAtoms[] = [
{ O3: { pos: posO3[0], index: indO3[0], fakeAltId: '' } }
];
for (let i = 1; i < indO3.length; i++) {
altPos.push({ O3: { pos: posO3[i], index: indO3[i], fakeAltId: '' } });
}
if (altPos.length === 1 && possibleAltIds.length > 1) {
/* We have some alternate positions on the residue but O3 does not have any - fake them */
altPos[0].O3.fakeAltId = possibleAltIds[0];
for (let i = 1; i < possibleAltIds.length; i++)
altPos.push({ O3: { pos: posO3[0], index: indO3[0], fakeAltId: possibleAltIds[i] } });
}
return altPos;
}
private processSecondResidue(residue: Residue, possibleAltIds: string[]) {
const indOP1 = this.getAtomIndices(['OP1'], residue);
const indOP2 = this.getAtomIndices(['OP2'], residue);
const indO5 = this.getAtomIndices(['O5\'', 'O5*'], residue);
const indP = this.getAtomIndices(['P'], residue);
const posOP1 = this.getAtomPositions(indOP1);
const posOP2 = this.getAtomPositions(indOP2);
const posO5 = this.getAtomPositions(indO5);
const posP = this.getAtomPositions(indP);
const infoOP1: AtomInfo[] = [];
/* We use OP1 as "pivotal" atom. There is no specific reason
* to pick OP1, it is as good a choice as any other atom
*/
if (indOP1.length === 1 && possibleAltIds.length > 1) {
/* No altIds on OP1, fake them */
for (const altId of possibleAltIds)
infoOP1.push({ pos: posOP1[0], index: indOP1[0], fakeAltId: altId });
} else {
for (let i = 0; i < indOP1.length; i++)
infoOP1.push({ pos: posOP1[i], index: indOP1[i], fakeAltId: '' });
}
const mkInfo = (i: number, indices: ElementIndex[], positions: Vec3[], altId: string) => {
if (i >= indices.length) {
const last = indices.length - 1;
return { pos: positions[last], index: indices[last], fakeAltId: altId };
}
return { pos: positions[i], index: indices[i], fakeAltId: altId };
};
const altPos: SecondResidueAtoms[] = [];
for (let i = 0; i < infoOP1.length; i++) {
const altId = infoOP1[i].fakeAltId;
const OP2 = mkInfo(i, indOP2, posOP2, altId);
const O5 = mkInfo(i, indO5, posO5, altId);
const P = mkInfo(i, indP, posP, altId);
altPos.push({ OP1: infoOP1[i], OP2, O5, P });
}
return altPos;
}
private step(residue: Residue): { firstAtoms: FirstResidueAtoms[], secondAtoms: SecondResidueAtoms[] } {
const firstPossibleAltIds = getPossibleAltIds(residue, this.structure, this.unit);
const firstAtoms = this.processFirstResidue(residue, firstPossibleAltIds);
residue = this.residueIt.move();
const secondPossibleAltIds = getPossibleAltIds(residue, this.structure, this.unit);
const secondAtoms = this.processSecondResidue(residue, secondPossibleAltIds);
return { firstAtoms, secondAtoms };
}
walk() {
while (this.chainIt.hasNext) {
this.residueIt.setSegment(this.chainIt.move());
let residue = this.residueIt.move();
while (this.residueIt.hasNext) {
try {
const { firstAtoms, secondAtoms } = this.step(residue);
this.handleStep(firstAtoms, secondAtoms);
} catch (error) {
/* Skip and move along */
residue = this.residueIt.move();
}
}
}
}
constructor(private structure: Structure, private unit: Unit.Atomic, private handler: Handler) {
super(unit);
this.chainIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, unit.elements);
this.residueIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements);
}
private chainIt: Segmentation.SegmentIterator<ChainIndex>;
private residueIt: Segmentation.SegmentIterator<ResidueIndex>;
}
}

View File

@@ -60,6 +60,8 @@ export class GeometryExporterUI extends CollapsableControls<{}, State> {
}
componentDidMount() {
if (!this.plugin.canvas3d) return;
const merged = merge(
this.controls.behaviors.params,
this.plugin.canvas3d!.reprCount
@@ -71,6 +73,7 @@ export class GeometryExporterUI extends CollapsableControls<{}, State> {
}
componentWillUnmount() {
super.componentWillUnmount();
this._controls?.dispose();
this._controls = void 0;
}

View File

@@ -118,11 +118,13 @@ export class Mp4Controls extends PluginComponent {
}
private init() {
if (!this.plugin.canvas3d) return;
this.subscribe(this.plugin.managers.animation.events.updated.pipe(debounceTime(16)), () => {
this.sync();
});
this.subscribe(this.plugin.canvas3d?.resized!, () => this.syncInfo());
this.subscribe(this.plugin.canvas3d.resized, () => this.syncInfo());
this.subscribe(this.plugin.helpers.viewportScreenshot?.events.previewed!, () => this.syncInfo());
this.subscribe(this.plugin.behaviors.state.isBusy, b => this.updateCanApply(b));

View File

@@ -69,6 +69,7 @@ export async function encodeMp4Animation<A extends PluginStateAnimation>(plugin:
const dt = durationMs / N;
await ctx.update({ message: 'Rendering...', isIndeterminate: false, current: 0, max: N + 1 });
await params.pass.updateBackground();
await plugin.managers.animation.play(params.animation.definition, params.animation.params);
stoppedAnimation = false;

View File

@@ -102,6 +102,7 @@ export class Mp4EncoderUI extends CollapsableControls<{}, State> {
}
componentWillUnmount() {
super.componentWillUnmount();
this._controls?.dispose();
this._controls = void 0;
}

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 2022-02-27T12:49:36-08:00
// Generated on 2022-08-20T16:36:05-07:00
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
@@ -13,11 +13,8 @@ export type Scalars = {
Boolean: boolean;
Int: number;
Float: number;
/** Built-in scalar representing an instant in time */
Date: any;
/** Built-in scalar for dynamic values */
ObjectScalar: any;
/** Use SPQR's SchemaPrinter to remove this from SDL */
UNREPRESENTABLE: any;
};
@@ -659,6 +656,8 @@ export type CoreEntry = {
readonly em_staining?: Maybe<ReadonlyArray<Maybe<EmStaining>>>;
readonly em_vitrification?: Maybe<ReadonlyArray<Maybe<EmVitrification>>>;
readonly entry?: Maybe<Entry>;
/** Get all groups for this PDB entry. */
readonly entry_groups?: Maybe<ReadonlyArray<Maybe<GroupEntry>>>;
readonly exptl?: Maybe<ReadonlyArray<Maybe<Exptl>>>;
readonly exptl_crystal?: Maybe<ReadonlyArray<Maybe<ExptlCrystal>>>;
readonly exptl_crystal_grow?: Maybe<ReadonlyArray<Maybe<ExptlCrystalGrow>>>;
@@ -703,6 +702,7 @@ export type CoreEntry = {
readonly rcsb_associated_holdings?: Maybe<CurrentEntry>;
readonly rcsb_binding_affinity?: Maybe<ReadonlyArray<Maybe<RcsbBindingAffinity>>>;
readonly rcsb_entry_container_identifiers: RcsbEntryContainerIdentifiers;
readonly rcsb_entry_group_membership?: Maybe<ReadonlyArray<Maybe<RcsbEntryGroupMembership>>>;
readonly rcsb_entry_info: RcsbEntryInfo;
readonly rcsb_external_references?: Maybe<ReadonlyArray<Maybe<RcsbExternalReferences>>>;
/**
@@ -851,6 +851,8 @@ export type CorePolymerEntity = {
readonly pdbx_entity_src_syn?: Maybe<ReadonlyArray<Maybe<PdbxEntitySrcSyn>>>;
/** Get all unique Pfam annotations associated with this molecular entity. */
readonly pfams?: Maybe<ReadonlyArray<Maybe<CorePfam>>>;
/** Get all groups for this PDB entity. */
readonly polymer_entity_groups?: Maybe<ReadonlyArray<Maybe<GroupPolymerEntity>>>;
/** Get all unique polymer instances (chains) for this molecular entity. */
readonly polymer_entity_instances?: Maybe<ReadonlyArray<Maybe<CorePolymerEntityInstance>>>;
/** Get a BIRD chemical components described in this molecular entity. */
@@ -887,6 +889,7 @@ export type CorePolymerEntity = {
readonly rcsb_polymer_entity_container_identifiers: RcsbPolymerEntityContainerIdentifiers;
readonly rcsb_polymer_entity_feature?: Maybe<ReadonlyArray<Maybe<RcsbPolymerEntityFeature>>>;
readonly rcsb_polymer_entity_feature_summary?: Maybe<ReadonlyArray<Maybe<RcsbPolymerEntityFeatureSummary>>>;
readonly rcsb_polymer_entity_group_membership?: Maybe<ReadonlyArray<Maybe<RcsbPolymerEntityGroupMembership>>>;
readonly rcsb_polymer_entity_keywords?: Maybe<RcsbPolymerEntityKeywords>;
readonly rcsb_polymer_entity_name_com?: Maybe<ReadonlyArray<Maybe<RcsbPolymerEntityNameCom>>>;
readonly rcsb_polymer_entity_name_sys?: Maybe<ReadonlyArray<Maybe<RcsbPolymerEntityNameSys>>>;
@@ -3198,6 +3201,46 @@ export type GeneName = {
readonly value?: Maybe<Scalars['String']>;
};
export type GroupEntry = {
/** Get provenance associated with this group. */
readonly group_provenance?: Maybe<GroupProvenance>;
readonly rcsb_group_accession_info?: Maybe<RcsbGroupAccessionInfo>;
readonly rcsb_group_container_identifiers: RcsbGroupContainerIdentifiers;
readonly rcsb_group_info: RcsbGroupInfo;
readonly rcsb_group_related?: Maybe<ReadonlyArray<Maybe<RcsbGroupRelated>>>;
readonly rcsb_group_statistics?: Maybe<RcsbGroupStatistics>;
/** A unique textual identifier for a group */
readonly rcsb_id: Scalars['String'];
};
export type GroupMembersAlignmentScores = {
readonly query_coverage: Scalars['Int'];
readonly query_length: Scalars['Int'];
readonly target_coverage: Scalars['Int'];
readonly target_length: Scalars['Int'];
};
export type GroupPolymerEntity = {
/** Get provenance associated with this group. */
readonly group_provenance?: Maybe<GroupProvenance>;
readonly rcsb_group_accession_info?: Maybe<RcsbGroupAccessionInfo>;
readonly rcsb_group_container_identifiers: RcsbGroupContainerIdentifiers;
readonly rcsb_group_info: RcsbGroupInfo;
readonly rcsb_group_related?: Maybe<ReadonlyArray<Maybe<RcsbGroupRelated>>>;
readonly rcsb_group_statistics?: Maybe<RcsbGroupStatistics>;
/** A unique textual identifier for a group */
readonly rcsb_id: Scalars['String'];
readonly rcsb_polymer_entity_group_members_rankings?: Maybe<ReadonlyArray<Maybe<RcsbPolymerEntityGroupMembersRankings>>>;
readonly rcsb_polymer_entity_group_sequence_alignment?: Maybe<RcsbPolymerEntityGroupSequenceAlignment>;
};
export type GroupProvenance = {
readonly rcsb_group_aggregation_method?: Maybe<RcsbGroupAggregationMethod>;
readonly rcsb_group_provenance_container_identifiers?: Maybe<RcsbGroupProvenanceContainerIdentifiers>;
/** A unique textual identifier for a group provenance */
readonly rcsb_id?: Maybe<Scalars['String']>;
};
export type InterfacePartnerFeatureAdditionalProperties = {
/**
* The additional property name.
@@ -3220,6 +3263,17 @@ export type InterfacePartnerFeatureFeaturePositions = {
readonly values?: Maybe<ReadonlyArray<Maybe<Scalars['Float']>>>;
};
export type MethodDetails = {
/** A description of special aspects of the clustering process */
readonly description?: Maybe<Scalars['String']>;
/** Defines the name of the description associated with the clustering process */
readonly name?: Maybe<Scalars['String']>;
/** Defines the type of the description associated with the clustering process */
readonly type?: Maybe<Scalars['String']>;
/** Defines the value associated with the clustering process */
readonly value?: Maybe<Scalars['Float']>;
};
export type PdbxAuditRevisionCategory = {
/**
* The category updated in the pdbx_audit_revision_category record.
@@ -6779,6 +6833,12 @@ export type Query = {
readonly entries?: Maybe<ReadonlyArray<Maybe<CoreEntry>>>;
/** Get PDB entry given the PDB id. */
readonly entry?: Maybe<CoreEntry>;
/** Given a group ID get a group object formed by aggregating individual structures that share a degree of similarity */
readonly entry_group?: Maybe<GroupEntry>;
/** Given a list of group IDs get a list of group objects formed by aggregating structures that share a degree of similarity */
readonly entry_groups?: Maybe<ReadonlyArray<Maybe<GroupEntry>>>;
/** Given a group provenance ID get an object that describes aggregation method used to create groups */
readonly group_provenance?: Maybe<GroupProvenance>;
/** Get a pairwise polymeric interface given the PDB ID, ASSEMBLY ID and INTERFACE ID. */
readonly interface?: Maybe<CoreInterface>;
/** Get a list of pairwise polymeric interfaces given a list of INTERFACE IDs. Here INTERFACE ID is a compound identifier that includes entry_id, assembly_id and interface_id e.g. 1XXX-1.1. */
@@ -6795,6 +6855,10 @@ export type Query = {
readonly polymer_entities?: Maybe<ReadonlyArray<Maybe<CorePolymerEntity>>>;
/** Get a PDB polymer entity, given the PDB ID and ENTITY ID. Here ENTITY ID is a '1', '2', '3', etc. */
readonly polymer_entity?: Maybe<CorePolymerEntity>;
/** Given a group ID get a group object formed by aggregating polymer entities that share a degree of similarity */
readonly polymer_entity_group?: Maybe<GroupPolymerEntity>;
/** Given a list of group IDs get a list of group objects formed by aggregating polymer entities that share a degree of similarity */
readonly polymer_entity_groups?: Maybe<ReadonlyArray<Maybe<GroupPolymerEntity>>>;
/** Get a PDB polymer entity instance (chain), given the PDB ID and ENTITY INSTANCE ID. Here ENTITY INSTANCE ID identifies structural element in the asymmetric unit, e.g. 'A', 'B', etc. */
readonly polymer_entity_instance?: Maybe<CorePolymerEntityInstance>;
/** Get a list of PDB polymer entity instances (chains), given the list of ENTITY INSTANCE IDs. Here ENTITY INSTANCE ID identifies structural element in the asymmetric unit, e.g. 'A', 'B', etc. */
@@ -6869,6 +6933,24 @@ export type QueryEntryArgs = {
};
/** Query root */
export type QueryEntry_GroupArgs = {
group_id: Scalars['String'];
};
/** Query root */
export type QueryEntry_GroupsArgs = {
group_ids: ReadonlyArray<InputMaybe<Scalars['String']>>;
};
/** Query root */
export type QueryGroup_ProvenanceArgs = {
group_provenance_id: Scalars['String'];
};
/** Query root */
export type QueryInterfaceArgs = {
assembly_id: Scalars['String'];
@@ -6922,6 +7004,18 @@ export type QueryPolymer_EntityArgs = {
};
/** Query root */
export type QueryPolymer_Entity_GroupArgs = {
group_id: Scalars['String'];
};
/** Query root */
export type QueryPolymer_Entity_GroupsArgs = {
group_ids: ReadonlyArray<InputMaybe<Scalars['String']>>;
};
/** Query root */
export type QueryPolymer_Entity_InstanceArgs = {
asym_id: Scalars['String'];
@@ -8666,6 +8760,25 @@ export type RcsbEntryContainerIdentifiers = {
readonly water_entity_ids?: Maybe<ReadonlyArray<Maybe<Scalars['String']>>>;
};
export type RcsbEntryGroupMembership = {
/**
* Method used to establish group membership
*
* Allowable values:
* matching_deposit_group_id
*
*/
readonly aggregation_method: Scalars['String'];
/**
* A unique identifier for a group of entries
*
* Examples:
* G_1001001
*
*/
readonly group_id: Scalars['String'];
};
export type RcsbEntryInfo = {
/** The number of assemblies defined for this entry including the deposited assembly. */
readonly assembly_count?: Maybe<Scalars['Int']>;
@@ -8893,6 +9006,127 @@ export type RcsbGenomicLineage = {
readonly name?: Maybe<Scalars['String']>;
};
export type RcsbGroupAccessionInfo = {
/** Identifies the version of the groups solution */
readonly version: Scalars['Int'];
};
export type RcsbGroupAggregationMethod = {
/** The details on a method used to calculate cluster solutions */
readonly method: RcsbGroupAggregationMethodMethod;
readonly similarity_criteria?: Maybe<RcsbGroupAggregationMethodSimilarityCriteria>;
/**
* Specifies the type of similarity criteria used to aggregate members into higher levels in the hierarchy
*
* Allowable values:
* sequence_identity, matching_uniprot_accession, matching_deposit_group_id
*
*/
readonly type: Scalars['String'];
};
export type RcsbGroupAggregationMethodMethod = {
/** Additional details describing the clustering process */
readonly details?: Maybe<ReadonlyArray<Maybe<MethodDetails>>>;
/**
* The name of the software or the method used to calculate cluster solutions
*
* Allowable values:
* mmseqs2, matching_reference_identity
*
*/
readonly name: Scalars['String'];
/**
* The version of the software.
*
* Examples:
* v1.0, 3.1-2, unknown
*
*/
readonly version?: Maybe<Scalars['String']>;
};
export type RcsbGroupAggregationMethodSimilarityCriteria = {
/**
* A function or similarity measure that quantifies the similarity between two members
*
* Allowable values:
* rmsd, sequence_identity
*
*/
readonly similarity_function?: Maybe<Scalars['String']>;
};
export type RcsbGroupContainerIdentifiers = {
/** A unique textual identifier for a group */
readonly group_id: Scalars['String'];
/** Member identifiers representing a group */
readonly group_member_ids: ReadonlyArray<Maybe<Scalars['String']>>;
/**
* A unique group provenance identifier
*
* Allowable values:
* provenance_sequence_identity, provenance_matching_uniprot_accession, provenance_matching_deposit_group_id
*
*/
readonly group_provenance_id: Scalars['String'];
/** Member identifiers representing a higher level in the groping hierarchy that has parent-child relationship */
readonly parent_member_ids?: Maybe<ReadonlyArray<Maybe<Scalars['String']>>>;
};
export type RcsbGroupInfo = {
readonly group_description?: Maybe<Scalars['String']>;
readonly group_members_count: Scalars['Int'];
/**
* Granularity of group members identifiers
*
* Allowable values:
* assembly, entry, polymer_entity, polymer_entity_instance
*
*/
readonly group_members_granularity: Scalars['String'];
readonly group_name?: Maybe<Scalars['String']>;
};
export type RcsbGroupProvenanceContainerIdentifiers = {
/**
* A unique group provenance identifier
*
* Allowable values:
* provenance_sequence_identity, provenance_matching_uniprot_accession, provenance_matching_deposit_group_id
*
*/
readonly group_provenance_id: Scalars['String'];
};
export type RcsbGroupRelated = {
/**
* A unique code assigned to a reference related the group
*
* Examples:
* P69905
*
*/
readonly resource_accession_code?: Maybe<Scalars['String']>;
/**
* Defines the type of the resource describing related references
*
* Examples:
* UniProt
*
*/
readonly resource_name?: Maybe<Scalars['String']>;
};
export type RcsbGroupStatistics = {
/** The desired lower limit for the similarity between two members that belong to the same group */
readonly similarity_cutoff?: Maybe<Scalars['Float']>;
/** Similarity score between two most similar group members */
readonly similarity_score_max?: Maybe<Scalars['Float']>;
/** Similarity score between two least similar group members */
readonly similarity_score_min?: Maybe<Scalars['Float']>;
};
export type RcsbInterfaceContainerIdentifiers = {
/** This item references an assembly in pdbx_struct_assembly */
readonly assembly_id: Scalars['String'];
@@ -9902,6 +10136,7 @@ export type RcsbPolymerEntity = {
*
*/
readonly rcsb_multiple_source_flag?: Maybe<Scalars['String']>;
readonly rcsb_polymer_name_combined?: Maybe<RcsbPolymerEntityRcsbPolymerNameCombined>;
/**
* The number of biological sources for the polymer entity. Multiple source contributions
* may come from the same organism (taxonomy).
@@ -10228,6 +10463,69 @@ export type RcsbPolymerEntityFeatureSummary = {
readonly type?: Maybe<Scalars['String']>;
};
export type RcsbPolymerEntityGroupMembersRankings = {
readonly group_members: ReadonlyArray<Maybe<RcsbPolymerEntityGroupMembersRankingsGroupMembers>>;
/**
* Defines ranking option applicable to group members
*
* Allowable values:
* coverage
*
*/
readonly ranking_criteria_type: Scalars['String'];
};
export type RcsbPolymerEntityGroupMembersRankingsGroupMembers = {
readonly member_id: Scalars['String'];
/** Quantifies the criteria used for ranking */
readonly original_score?: Maybe<Scalars['Float']>;
/** Reflects a relationship between group members such that, for any two members the first is ranked higher (smaller rank value) than the second */
readonly rank: Scalars['Int'];
};
export type RcsbPolymerEntityGroupMembership = {
/**
* Method used to establish group membership
*
* Allowable values:
* sequence_identity, matching_uniprot_accession
*
*/
readonly aggregation_method: Scalars['String'];
/**
* A unique identifier for a group of entities
*
* Examples:
* 1_100, P00003
*
*/
readonly group_id: Scalars['String'];
/** Degree of similarity expressed as a floating-point number */
readonly similarity_cutoff?: Maybe<Scalars['Float']>;
};
export type RcsbPolymerEntityGroupSequenceAlignment = {
/** Abstract reference where group members can be aligned to generate a MSA */
readonly abstract_reference: RcsbPolymerEntityGroupSequenceAlignmentAbstractReference;
/** Alignment with a core_entity canonical sequence */
readonly group_members_alignment: ReadonlyArray<Maybe<RcsbPolymerEntityGroupSequenceAlignmentGroupMembersAlignment>>;
};
export type RcsbPolymerEntityGroupSequenceAlignmentAbstractReference = {
/** Abstract reference length */
readonly length: Scalars['Int'];
/** Sequence that represents the abstract reference */
readonly sequence?: Maybe<Scalars['String']>;
};
export type RcsbPolymerEntityGroupSequenceAlignmentGroupMembersAlignment = {
/** Alignment region encoded as a triplet [query_begin, target_begin, length] */
readonly aligned_regions: ReadonlyArray<Maybe<ReadonlyArray<Maybe<Scalars['Int']>>>>;
readonly member_id: Scalars['String'];
/** Alignment scores */
readonly scores: GroupMembersAlignmentScores;
};
export type RcsbPolymerEntityInstanceContainerIdentifiers = {
/** Instance identifier for this container. */
readonly asym_id: Scalars['String'];
@@ -10346,6 +10644,13 @@ export type RcsbPolymerEntityRcsbMacromolecularNamesCombined = {
readonly provenance_source?: Maybe<Scalars['String']>;
};
export type RcsbPolymerEntityRcsbPolymerNameCombined = {
/** Protein name annotated by the UniProtKB or macromolecular name assigned by the PDB */
readonly names?: Maybe<ReadonlyArray<Maybe<Scalars['String']>>>;
/** Allowable values: PDB Preferred Name, PDB Description, UniProt Name. */
readonly provenance_source?: Maybe<Scalars['String']>;
};
export type RcsbPolymerInstanceAnnotation = {
/** An identifier for the annotation. */
readonly annotation_id?: Maybe<Scalars['String']>;

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 David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -260,7 +260,8 @@ namespace Camera {
radius: 0,
radiusMax: 10,
fog: 50,
clipFar: true
clipFar: true,
minNear: 5,
};
}
@@ -276,6 +277,7 @@ namespace Camera {
radiusMax: number
fog: number
clipFar: boolean
minNear: number
}
export function copySnapshot(out: Snapshot, source?: Partial<Snapshot>) {
@@ -292,6 +294,7 @@ namespace Camera {
if (typeof source.radiusMax !== 'undefined') out.radiusMax = source.radiusMax;
if (typeof source.fog !== 'undefined') out.fog = source.fog;
if (typeof source.clipFar !== 'undefined') out.clipFar = source.clipFar;
if (typeof source.minNear !== 'undefined') out.minNear = source.minNear;
return out;
}
@@ -303,6 +306,7 @@ namespace Camera {
&& a.radiusMax === b.radiusMax
&& a.fog === b.fog
&& a.clipFar === b.clipFar
&& a.minNear === b.minNear
&& Vec3.exactEquals(a.position, b.position)
&& Vec3.exactEquals(a.up, b.up)
&& Vec3.exactEquals(a.target, b.target);
@@ -370,7 +374,7 @@ function updatePers(camera: Camera) {
}
function updateClip(camera: Camera) {
let { radius, radiusMax, mode, fog, clipFar } = camera.state;
let { radius, radiusMax, mode, fog, clipFar, minNear } = camera.state;
if (radius < 0.01) radius = 0.01;
const normalizedFar = clipFar ? radius : radiusMax;
@@ -384,12 +388,12 @@ function updateClip(camera: Camera) {
if (mode === 'perspective') {
// set at least to 5 to avoid slow sphere impostor rendering
near = Math.max(Math.min(radiusMax, 5), near);
far = Math.max(5, far);
near = Math.max(Math.min(radiusMax, minNear), near);
far = Math.max(minNear, far);
} else {
// not too close to 0 as it causes issues with outline rendering
near = Math.max(Math.min(radiusMax, 5), near);
far = Math.max(5, far);
near = Math.max(Math.min(radiusMax, minNear), near);
far = Math.max(minNear, far);
}
if (near === far) {

View File

@@ -1,5 +1,5 @@
/**
* 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 Alexander Rose <alexander.rose@weirdbyte.de>
@@ -13,8 +13,8 @@ import { Camera, ICamera } from '../camera';
import { Viewport } from './util';
export const StereoCameraParams = {
eyeSeparation: PD.Numeric(0.064, { min: 0.01, max: 0.5, step: 0.001 }),
focus: PD.Numeric(10, { min: 1, max: 100, step: 0.01 }),
eyeSeparation: PD.Numeric(0.062, { min: 0.02, max: 0.1, step: 0.001 }, { description: 'Distance between left and right camera.' }),
focus: PD.Numeric(10, { min: 1, max: 20, step: 0.1 }, { description: 'Apparent object distance.' }),
};
export const DefaultStereoCameraProps = PD.getDefaultValues(StereoCameraParams);
export type StereoCameraProps = PD.Values<typeof StereoCameraParams>

View File

@@ -1,8 +1,9 @@
/**
* 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>
* @author Gianluca Tomasello <giagitom@gmail.com>
*/
import { BehaviorSubject, Subscription } from 'rxjs';
@@ -30,7 +31,7 @@ import { PickData } from './passes/pick';
import { PickHelper } from './passes/pick';
import { ImagePass, ImageProps } from './passes/image';
import { Sphere3D } from '../mol-math/geometry';
import { isDebugMode } from '../mol-util/debug';
import { isDebugMode, isTimingMode } from '../mol-util/debug';
import { CameraHelperParams } from './helper/camera-helper';
import { produce } from 'immer';
import { HandleHelperParams } from './helper/handle-helper';
@@ -39,7 +40,10 @@ 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';
import { GraphicsRenderVariantsBlended, GraphicsRenderVariantsWboit, GraphicsRenderVariantsDpoit } from '../mol-gl/webgl/render-item';
import { degToRad, radToDeg } from '../mol-math/misc';
import { AssetManager } from '../mol-util/assets';
import { deepClone } from '../mol-util/object';
export const Canvas3DParams = {
camera: PD.Group({
@@ -49,6 +53,7 @@ export const Canvas3DParams = {
on: PD.Group(StereoCameraParams),
off: PD.Group({})
}, { cycle: true, hideIf: p => p?.mode !== 'perspective' }),
fov: PD.Numeric(45, { min: 10, max: 130, step: 1 }, { label: 'Field of View' }),
manualReset: PD.Boolean(false, { isHidden: true }),
}, { pivot: 'mode' }),
cameraFog: PD.MappedStatic('on', {
@@ -60,6 +65,7 @@ export const Canvas3DParams = {
cameraClipping: PD.Group({
radius: PD.Numeric(100, { min: 0, max: 99, step: 1 }, { label: 'Clipping', description: 'How much of the scene to show.' }),
far: PD.Boolean(true, { description: 'Hide scene in the distance' }),
minNear: PD.Numeric(5, { min: 0.1, max: 10, step: 0.1 }, { description: 'Note, may cause performance issues rendering impostors when set too small and cause issues with outline rendering when too close to 0.' }),
}, { pivot: 'radius' }),
viewport: PD.MappedStatic('canvas', {
canvas: PD.Group({}),
@@ -78,7 +84,9 @@ export const Canvas3DParams = {
}),
cameraResetDurationMs: PD.Numeric(250, { min: 0, max: 1000, step: 1 }, { description: 'The time it takes to reset the camera.' }),
sceneRadiusFactor: PD.Numeric(1, { min: 1, max: 10, step: 0.1 }),
transparentBackground: PD.Boolean(false),
dpoitIterations: PD.Numeric(2, { min: 1, max: 10, step: 1 }),
multiSample: PD.Group(MultiSampleParams),
postprocessing: PD.Group(PostprocessingParams),
@@ -106,11 +114,13 @@ interface Canvas3DContext {
readonly attribs: Readonly<Canvas3DContext.Attribs>
readonly contextLost: BehaviorSubject<now.Timestamp>
readonly contextRestored: BehaviorSubject<now.Timestamp>
readonly assetManager: AssetManager
dispose: (options?: Partial<{ doNotForceWebGLContextLoss: boolean }>) => void
}
namespace Canvas3DContext {
export const DefaultAttribs = {
failIfMajorPerformanceCaveat: false,
/** true by default to avoid issues with Safari (Jan 2021) */
antialias: true,
/** true to support multiple Canvas3D objects with a single context */
@@ -120,14 +130,19 @@ namespace Canvas3DContext {
/** extra pixels to around target to check in case target is empty */
pickPadding: 1,
enableWboit: true,
enableDpoit: false,
preferWebGl1: false
};
export type Attribs = typeof DefaultAttribs
export function fromCanvas(canvas: HTMLCanvasElement, attribs: Partial<Attribs> = {}): Canvas3DContext {
export function fromCanvas(canvas: HTMLCanvasElement, assetManager: AssetManager, attribs: Partial<Attribs> = {}): Canvas3DContext {
const a = { ...DefaultAttribs, ...attribs };
const { antialias, preserveDrawingBuffer, pixelScale, preferWebGl1 } = a;
if (a.enableWboit && a.enableDpoit) throw new Error('Multiple transparency methods not allowed.');
const { failIfMajorPerformanceCaveat, antialias, preserveDrawingBuffer, pixelScale, preferWebGl1 } = a;
const gl = getGLContext(canvas, {
failIfMajorPerformanceCaveat,
antialias,
preserveDrawingBuffer,
alpha: true, // the renderer requires an alpha channel
@@ -139,7 +154,7 @@ namespace Canvas3DContext {
const input = InputObserver.fromElement(canvas, { pixelScale, preventGestures: true });
const webgl = createContext(gl, { pixelScale });
const passes = new Passes(webgl, attribs);
const passes = new Passes(webgl, assetManager, a);
if (isDebugMode) {
const loseContextExt = gl.getExtension('WEBGL_lose_context');
@@ -192,6 +207,7 @@ namespace Canvas3DContext {
attribs: a,
contextLost,
contextRestored: webgl.contextRestored,
assetManager,
dispose: (options?: Partial<{ doNotForceWebGLContextLoss: boolean }>) => {
input.dispose();
@@ -278,8 +294,8 @@ namespace Canvas3D {
export interface DragEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys, pageStart: Vec2, pageEnd: Vec2 }
export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys, page?: Vec2, position?: Vec3 }
export function create({ webgl, input, passes, attribs }: Canvas3DContext, props: Partial<Canvas3DProps> = {}): Canvas3D {
const p: Canvas3DProps = { ...DefaultCanvas3DParams, ...props };
export function create({ webgl, input, passes, attribs, assetManager }: Canvas3DContext, props: Partial<Canvas3DProps> = {}): Canvas3D {
const p: Canvas3DProps = { ...deepClone(DefaultCanvas3DParams), ...deepClone(props) };
const reprRenderObjects = new Map<Representation.Any, Set<GraphicsRenderObject>>();
const reprUpdatedSubscriptions = new Map<Representation.Any, Subscription>();
@@ -296,14 +312,19 @@ namespace Canvas3D {
let width = 128;
let height = 128;
updateViewport();
const scene = Scene.create(webgl, passes.draw.dpoitEnabled ? GraphicsRenderVariantsDpoit : (passes.draw.wboitEnabled ? GraphicsRenderVariantsWboit : GraphicsRenderVariantsBlended));
const scene = Scene.create(webgl, passes.draw.wboitEnabled ? GraphicsRenderVariantsWboit : GraphicsRenderVariantsBlended);
function getSceneRadius() {
return scene.boundingSphere.radius * p.sceneRadiusFactor;
}
const camera = new Camera({
position: Vec3.create(0, 0, 100),
mode: p.camera.mode,
fog: p.cameraFog.name === 'on' ? p.cameraFog.params.intensity : 0,
clipFar: p.cameraClipping.far
clipFar: p.cameraClipping.far,
minNear: p.cameraClipping.minNear,
fov: degToRad(p.camera.fov),
}, { x, y, width, height }, { pixelScale: attribs.pixelScale });
const stereoCamera = new StereoCamera(camera, p.camera.stereo.params);
@@ -315,6 +336,10 @@ namespace Canvas3D {
const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input, camera, p.interaction);
const multiSampleHelper = new MultiSampleHelper(passes.multiSample);
passes.draw.postprocessing.background.update(camera, p.postprocessing.background, changed => {
if (changed) requestDraw();
});
let cameraResetRequested = false;
let nextCameraResetDuration: number | undefined = void 0;
let nextCameraResetSnapshot: Camera.SnapshotProvider | undefined = void 0;
@@ -372,12 +397,12 @@ namespace Canvas3D {
const { repr, loci } = reprLoci;
let changed = false;
if (repr) {
changed = repr.mark(loci, action);
changed = repr.mark(loci, action) || changed;
} else {
changed = helper.handle.mark(loci, action);
changed = helper.camera.mark(loci, action) || changed;
reprRenderObjects.forEach((_, _repr) => { changed = _repr.mark(loci, action) || changed; });
}
changed = helper.handle.mark(loci, action) || changed;
changed = helper.camera.mark(loci, action) || changed;
return changed;
}
@@ -395,7 +420,7 @@ namespace Canvas3D {
y > gl.drawingBufferHeight || y + height < 0
) return false;
const markingUpdated = resolveMarking();
const markingUpdated = resolveMarking() && (renderer.props.colorMarker || p.marking.enabled);
let didRender = false;
controls.update(currentTime);
@@ -413,6 +438,7 @@ namespace Canvas3D {
cam = stereoCamera;
}
if (isTimingMode) webgl.timer.mark('Canvas3D.render');
const ctx = { renderer, camera: cam, scene, helper };
if (MultiSamplePass.isEnabled(p.multiSample)) {
const forceOn = !cameraChanged && markingUpdated && !controls.isAnimating;
@@ -420,6 +446,8 @@ namespace Canvas3D {
} else {
passes.draw.render(ctx, p, true);
}
if (isTimingMode) webgl.timer.markEnd('Canvas3D.render');
// if only marking has updated, do not set the flag to dirty
pickHelper.dirty = pickHelper.dirty || shouldRender;
didRender = true;
@@ -520,7 +548,7 @@ namespace Canvas3D {
const focus = camera.getFocus(center, radius);
const next = typeof nextCameraResetSnapshot === 'function' ? nextCameraResetSnapshot(scene, camera) : nextCameraResetSnapshot;
const snapshot = next ? { ...focus, ...next } : focus;
camera.setState({ ...snapshot, radiusMax: scene.boundingSphere.radius }, duration);
camera.setState({ ...snapshot, radiusMax: getSceneRadius() }, duration);
}
nextCameraResetDuration = void 0;
@@ -571,7 +599,7 @@ namespace Canvas3D {
}
if (oldBoundingSphereVisible.radius === 0) nextCameraResetDuration = 0;
camera.setState({ radiusMax: scene.boundingSphere.radius }, 0);
if (!p.camera.manualReset) camera.setState({ radiusMax: getSceneRadius() }, 0);
reprCount.next(reprRenderObjects.size);
if (isDebugMode) consoleStats();
@@ -647,7 +675,7 @@ namespace Canvas3D {
function getProps(): Canvas3DProps {
const radius = scene.boundingSphere.radius > 0
? 100 - Math.round((camera.transition.target.radius / scene.boundingSphere.radius) * 100)
? 100 - Math.round((camera.transition.target.radius / getSceneRadius()) * 100)
: 0;
return {
@@ -655,14 +683,17 @@ namespace Canvas3D {
mode: camera.state.mode,
helper: { ...helper.camera.props },
stereo: { ...p.camera.stereo },
fov: Math.round(radToDeg(camera.state.fov)),
manualReset: !!p.camera.manualReset
},
cameraFog: camera.state.fog > 0
? { name: 'on' as const, params: { intensity: camera.state.fog } }
: { name: 'off' as const, params: {} },
cameraClipping: { far: camera.state.clipFar, radius },
cameraClipping: { far: camera.state.clipFar, radius, minNear: camera.state.minNear },
cameraResetDurationMs: p.cameraResetDurationMs,
sceneRadiusFactor: p.sceneRadiusFactor,
transparentBackground: p.transparentBackground,
dpoitIterations: p.dpoitIterations,
viewport: p.viewport,
postprocessing: { ...p.postprocessing },
@@ -764,10 +795,19 @@ namespace Canvas3D {
? produce(getProps(), properties as any)
: properties;
if (props.sceneRadiusFactor !== undefined) {
p.sceneRadiusFactor = props.sceneRadiusFactor;
camera.setState({ radiusMax: getSceneRadius() }, 0);
}
const cameraState: Partial<Camera.Snapshot> = Object.create(null);
if (props.camera && props.camera.mode !== undefined && props.camera.mode !== camera.state.mode) {
cameraState.mode = props.camera.mode;
}
const oldFov = Math.round(radToDeg(camera.state.fov));
if (props.camera && props.camera.fov !== undefined && props.camera.fov !== oldFov) {
cameraState.fov = degToRad(props.camera.fov);
}
if (props.cameraFog !== undefined && props.cameraFog.params) {
const newFog = props.cameraFog.name === 'on' ? props.cameraFog.params.intensity : 0;
if (newFog !== camera.state.fog) cameraState.fog = newFog;
@@ -776,8 +816,11 @@ namespace Canvas3D {
if (props.cameraClipping.far !== undefined && props.cameraClipping.far !== camera.state.clipFar) {
cameraState.clipFar = props.cameraClipping.far;
}
if (props.cameraClipping.minNear !== undefined && props.cameraClipping.minNear !== camera.state.minNear) {
cameraState.minNear = props.cameraClipping.minNear;
}
if (props.cameraClipping.radius !== undefined) {
const radius = (scene.boundingSphere.radius / 100) * (100 - props.cameraClipping.radius);
const radius = (getSceneRadius() / 100) * (100 - props.cameraClipping.radius);
if (radius > 0 && radius !== cameraState.radius) {
// if radius = 0, NaNs happen
cameraState.radius = Math.max(radius, 0.01);
@@ -788,9 +831,13 @@ namespace Canvas3D {
if (props.camera?.helper) helper.camera.setProps(props.camera.helper);
if (props.camera?.manualReset !== undefined) p.camera.manualReset = props.camera.manualReset;
if (props.camera?.stereo !== undefined) Object.assign(p.camera.stereo, props.camera.stereo);
if (props.camera?.stereo !== undefined) {
Object.assign(p.camera.stereo, props.camera.stereo);
stereoCamera.setProps(p.camera.stereo.params);
}
if (props.cameraResetDurationMs !== undefined) p.cameraResetDurationMs = props.cameraResetDurationMs;
if (props.transparentBackground !== undefined) p.transparentBackground = props.transparentBackground;
if (props.dpoitIterations !== undefined) p.dpoitIterations = props.dpoitIterations;
if (props.viewport !== undefined) {
const doNotUpdate = p.viewport === props.viewport ||
(p.viewport.name === props.viewport.name && shallowEqual(p.viewport.params, props.viewport.params));
@@ -802,6 +849,12 @@ namespace Canvas3D {
}
}
if (props.postprocessing?.background) {
Object.assign(p.postprocessing.background, props.postprocessing.background);
passes.draw.postprocessing.background.update(camera, p.postprocessing.background, changed => {
if (changed && !doNotRequestDraw) requestDraw();
});
}
if (props.postprocessing) Object.assign(p.postprocessing, props.postprocessing);
if (props.marking) Object.assign(p.marking, props.marking);
if (props.multiSample) Object.assign(p.multiSample, props.multiSample);
@@ -820,7 +873,7 @@ namespace Canvas3D {
}
},
getImagePass: (props: Partial<ImageProps> = {}) => {
return new ImagePass(webgl, renderer, scene, camera, helper, passes.draw.wboitEnabled, props);
return new ImagePass(webgl, assetManager, renderer, scene, camera, helper, passes.draw.wboitEnabled, passes.draw.dpoitEnabled, props);
},
getRenderObjects(): GraphicsRenderObject[] {
const renderObjects: GraphicsRenderObject[] = [];
@@ -885,4 +938,4 @@ namespace Canvas3D {
Viewport.set(controls.viewport, x, y, width, height);
}
}
}
}

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>
*/
@@ -17,7 +17,7 @@ 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';
import { DataLoci, EmptyLoci, isEveryLoci, Loci } from '../../mol-model/loci';
import { Shape } from '../../mol-model/shape';
import { Visual } from '../../mol-repr/visual';
import { ColorNames } from '../../mol-util/color/names';
@@ -113,8 +113,10 @@ export class CameraHelper {
mark(loci: Loci, action: MarkerAction) {
if (!MarkerActions.is(MarkerActions.Highlighting, action)) return false;
if (!isCameraAxesLoci(loci)) return false;
if (loci.data !== this) return false;
if (!isEveryLoci(loci)) {
if (!isCameraAxesLoci(loci)) return false;
if (loci.data !== this) return false;
}
return Visual.mark(this.renderObject, loci, action, this.eachGroup);
}

View File

@@ -1,5 +1,5 @@
/**
* 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -20,7 +20,7 @@ import produce from 'immer';
import { Shape } from '../../mol-model/shape';
import { PickingId } from '../../mol-geo/geometry/picking';
import { Camera } from '../camera';
import { DataLoci, EmptyLoci, Loci } from '../../mol-model/loci';
import { DataLoci, EmptyLoci, isEveryLoci, 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';
@@ -121,8 +121,10 @@ export class HandleHelper {
mark(loci: Loci, action: MarkerAction) {
if (!MarkerActions.is(MarkerActions.Highlighting, action)) return false;
if (!isHandleLoci(loci)) return false;
if (loci.data !== this) return false;
if (!isEveryLoci(loci)) {
if (!isHandleLoci(loci)) return false;
if (loci.data !== this) return false;
}
return Visual.mark(this.renderObject, loci, action, this.eachGroup);
}

View File

@@ -0,0 +1,467 @@
/**
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { QuadPositions, } from '../../mol-gl/compute/util';
import { ComputeRenderable, createComputeRenderable } from '../../mol-gl/renderable';
import { AttributeSpec, DefineSpec, TextureSpec, UniformSpec, Values, ValueSpec } from '../../mol-gl/renderable/schema';
import { ShaderCode } from '../../mol-gl/shader-code';
import { background_frag } from '../../mol-gl/shader/background.frag';
import { background_vert } from '../../mol-gl/shader/background.vert';
import { WebGLContext } from '../../mol-gl/webgl/context';
import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
import { createNullTexture, CubeFaces, Texture } from '../../mol-gl/webgl/texture';
import { Mat4 } from '../../mol-math/linear-algebra/3d/mat4';
import { ValueCell } from '../../mol-util/value-cell';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { isTimingMode } from '../../mol-util/debug';
import { Camera, ICamera } from '../camera';
import { Vec3 } from '../../mol-math/linear-algebra/3d/vec3';
import { Vec2 } from '../../mol-math/linear-algebra/3d/vec2';
import { Color } from '../../mol-util/color';
import { Asset, AssetManager } from '../../mol-util/assets';
import { Vec4 } from '../../mol-math/linear-algebra/3d/vec4';
const SharedParams = {
opacity: PD.Numeric(1, { min: 0.0, max: 1.0, step: 0.01 }),
saturation: PD.Numeric(0, { min: -1, max: 1, step: 0.01 }),
lightness: PD.Numeric(0, { min: -1, max: 1, step: 0.01 }),
};
const SkyboxParams = {
faces: PD.MappedStatic('urls', {
urls: PD.Group({
nx: PD.Text('', { label: 'Negative X / Left' }),
ny: PD.Text('', { label: 'Negative Y / Bottom' }),
nz: PD.Text('', { label: 'Negative Z / Back' }),
px: PD.Text('', { label: 'Positive X / Right' }),
py: PD.Text('', { label: 'Positive Y / Top' }),
pz: PD.Text('', { label: 'Positive Z / Front' }),
}, { isExpanded: true, label: 'URLs' }),
files: PD.Group({
nx: PD.File({ label: 'Negative X / Left', accept: 'image/*' }),
ny: PD.File({ label: 'Negative Y / Bottom', accept: 'image/*' }),
nz: PD.File({ label: 'Negative Z / Back', accept: 'image/*' }),
px: PD.File({ label: 'Positive X / Right', accept: 'image/*' }),
py: PD.File({ label: 'Positive Y / Top', accept: 'image/*' }),
pz: PD.File({ label: 'Positive Z / Front', accept: 'image/*' }),
}, { isExpanded: true, label: 'Files' }),
}),
blur: PD.Numeric(0, { min: 0.0, max: 1.0, step: 0.01 }, { description: 'Note, this only works in WebGL2 or when "EXT_shader_texture_lod" is available.' }),
...SharedParams,
};
type SkyboxProps = PD.Values<typeof SkyboxParams>
const ImageParams = {
source: PD.MappedStatic('url', {
url: PD.Text(''),
file: PD.File({ accept: 'image/*' }),
}),
...SharedParams,
coverage: PD.Select('viewport', PD.arrayToOptions(['viewport', 'canvas'])),
};
type ImageProps = PD.Values<typeof ImageParams>
const HorizontalGradientParams = {
topColor: PD.Color(Color(0xDDDDDD)),
bottomColor: PD.Color(Color(0xEEEEEE)),
ratio: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
coverage: PD.Select('viewport', PD.arrayToOptions(['viewport', 'canvas'])),
};
const RadialGradientParams = {
centerColor: PD.Color(Color(0xDDDDDD)),
edgeColor: PD.Color(Color(0xEEEEEE)),
ratio: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
coverage: PD.Select('viewport', PD.arrayToOptions(['viewport', 'canvas'])),
};
export const BackgroundParams = {
variant: PD.MappedStatic('off', {
off: PD.EmptyGroup(),
skybox: PD.Group(SkyboxParams, { isExpanded: true }),
image: PD.Group(ImageParams, { isExpanded: true }),
horizontalGradient: PD.Group(HorizontalGradientParams, { isExpanded: true }),
radialGradient: PD.Group(RadialGradientParams, { isExpanded: true }),
}, { label: 'Environment' }),
};
export type BackgroundProps = PD.Values<typeof BackgroundParams>
export class BackgroundPass {
private renderable: BackgroundRenderable;
private skybox: {
texture: Texture
props: SkyboxProps
assets: Asset[]
loaded: boolean
} | undefined;
private image: {
texture: Texture
props: ImageProps
asset: Asset
loaded: boolean
} | undefined;
private readonly camera = new Camera();
private readonly target = Vec3();
private readonly position = Vec3();
private readonly dir = Vec3();
readonly texture: Texture;
constructor(private readonly webgl: WebGLContext, private readonly assetManager: AssetManager, width: number, height: number) {
this.renderable = getBackgroundRenderable(webgl, width, height);
}
setSize(width: number, height: number) {
const [w, h] = this.renderable.values.uTexSize.ref.value;
if (width !== w || height !== h) {
ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height));
}
}
private clearSkybox() {
if (this.skybox !== undefined) {
this.skybox.texture.destroy();
this.skybox.assets.forEach(a => this.assetManager.release(a));
this.skybox = undefined;
}
}
private updateSkybox(camera: ICamera, props: SkyboxProps, onload?: (changed: boolean) => void) {
const tf = this.skybox?.props.faces;
const f = props.faces.params;
if (!f.nx || !f.ny || !f.nz || !f.px || !f.py || !f.pz) {
this.clearSkybox();
onload?.(false);
return;
}
if (!this.skybox || !tf || !areSkyboxTexturePropsEqual(props.faces, this.skybox.props.faces)) {
this.clearSkybox();
const { texture, assets } = getSkyboxTexture(this.webgl, this.assetManager, props.faces, errored => {
if (this.skybox) this.skybox.loaded = !errored;
onload?.(true);
});
this.skybox = { texture, props: { ...props }, assets, loaded: false };
ValueCell.update(this.renderable.values.tSkybox, texture);
this.renderable.update();
} else {
onload?.(false);
}
if (!this.skybox) return;
let cam = camera;
if (camera.state.mode === 'orthographic') {
this.camera.setState({ ...camera.state, mode: 'perspective' });
this.camera.update();
cam = this.camera;
}
const m = this.renderable.values.uViewDirectionProjectionInverse.ref.value;
Vec3.sub(this.dir, cam.state.position, cam.state.target);
Vec3.setMagnitude(this.dir, this.dir, 0.1);
Vec3.copy(this.position, this.dir);
Mat4.lookAt(m, this.position, this.target, cam.state.up);
Mat4.mul(m, cam.projection, m);
Mat4.invert(m, m);
ValueCell.update(this.renderable.values.uViewDirectionProjectionInverse, m);
ValueCell.updateIfChanged(this.renderable.values.uBlur, props.blur);
ValueCell.updateIfChanged(this.renderable.values.uOpacity, props.opacity);
ValueCell.updateIfChanged(this.renderable.values.uSaturation, props.saturation);
ValueCell.updateIfChanged(this.renderable.values.uLightness, props.lightness);
ValueCell.updateIfChanged(this.renderable.values.dVariant, 'skybox');
this.renderable.update();
}
private clearImage() {
if (this.image !== undefined) {
this.image.texture.destroy();
this.assetManager.release(this.image.asset);
this.image = undefined;
}
}
private updateImage(props: ImageProps, onload?: (loaded: boolean) => void) {
if (!props.source.params) {
this.clearImage();
onload?.(false);
return;
}
if (!this.image || !this.image.props.source.params || !areImageTexturePropsEqual(props.source, this.image.props.source)) {
this.clearImage();
const { texture, asset } = getImageTexture(this.webgl, this.assetManager, props.source, errored => {
if (this.image) this.image.loaded = !errored;
onload?.(true);
});
this.image = { texture, props: { ...props }, asset, loaded: false };
ValueCell.update(this.renderable.values.tImage, texture);
this.renderable.update();
} else {
onload?.(false);
}
if (!this.image) return;
ValueCell.updateIfChanged(this.renderable.values.uOpacity, props.opacity);
ValueCell.updateIfChanged(this.renderable.values.uSaturation, props.saturation);
ValueCell.updateIfChanged(this.renderable.values.uLightness, props.lightness);
ValueCell.updateIfChanged(this.renderable.values.uViewportAdjusted, props.coverage === 'viewport' ? true : false);
ValueCell.updateIfChanged(this.renderable.values.dVariant, 'image');
this.renderable.update();
}
private updateImageScaling() {
const v = this.renderable.values;
const [w, h] = v.uTexSize.ref.value;
const iw = this.image?.texture.getWidth() || 0;
const ih = this.image?.texture.getHeight() || 0;
const r = w / h;
const ir = iw / ih;
// responsive scaling with offset
if (r < ir) {
ValueCell.update(v.uImageScale, Vec2.set(v.uImageScale.ref.value, iw * h / ih, h));
} else {
ValueCell.update(v.uImageScale, Vec2.set(v.uImageScale.ref.value, w, ih * w / iw));
}
const [rw, rh] = v.uImageScale.ref.value;
const sr = rw / rh;
if (sr > r) {
ValueCell.update(v.uImageOffset, Vec2.set(v.uImageOffset.ref.value, (1 - r / sr) / 2, 0));
} else {
ValueCell.update(v.uImageOffset, Vec2.set(v.uImageOffset.ref.value, 0, (1 - sr / r) / 2));
}
}
private updateGradient(colorA: Color, colorB: Color, ratio: number, variant: 'horizontalGradient' | 'radialGradient', viewportAdjusted: boolean) {
ValueCell.update(this.renderable.values.uGradientColorA, Color.toVec3Normalized(this.renderable.values.uGradientColorA.ref.value, colorA));
ValueCell.update(this.renderable.values.uGradientColorB, Color.toVec3Normalized(this.renderable.values.uGradientColorB.ref.value, colorB));
ValueCell.updateIfChanged(this.renderable.values.uGradientRatio, ratio);
ValueCell.updateIfChanged(this.renderable.values.uViewportAdjusted, viewportAdjusted);
ValueCell.updateIfChanged(this.renderable.values.dVariant, variant);
this.renderable.update();
}
update(camera: ICamera, props: BackgroundProps, onload?: (changed: boolean) => void) {
if (props.variant.name === 'off') {
this.clearSkybox();
this.clearImage();
onload?.(false);
return;
} else if (props.variant.name === 'skybox') {
this.clearImage();
this.updateSkybox(camera, props.variant.params, onload);
} else if (props.variant.name === 'image') {
this.clearSkybox();
this.updateImage(props.variant.params, onload);
} else if (props.variant.name === 'horizontalGradient') {
this.clearSkybox();
this.clearImage();
this.updateGradient(props.variant.params.topColor, props.variant.params.bottomColor, props.variant.params.ratio, props.variant.name, props.variant.params.coverage === 'viewport' ? true : false);
onload?.(false);
} else if (props.variant.name === 'radialGradient') {
this.clearSkybox();
this.clearImage();
this.updateGradient(props.variant.params.centerColor, props.variant.params.edgeColor, props.variant.params.ratio, props.variant.name, props.variant.params.coverage === 'viewport' ? true : false);
onload?.(false);
}
const { x, y, width, height } = camera.viewport;
ValueCell.update(this.renderable.values.uViewport, Vec4.set(this.renderable.values.uViewport.ref.value, x, y, width, height));
}
isEnabled(props: BackgroundProps) {
return !!(
(this.skybox && this.skybox.loaded) ||
(this.image && this.image.loaded) ||
props.variant.name === 'horizontalGradient' ||
props.variant.name === 'radialGradient'
);
}
private isReady() {
return !!(
(this.skybox && this.skybox.loaded) ||
(this.image && this.image.loaded) ||
this.renderable.values.dVariant.ref.value === 'horizontalGradient' ||
this.renderable.values.dVariant.ref.value === 'radialGradient'
);
}
render() {
if (!this.isReady()) return;
if (this.renderable.values.dVariant.ref.value === 'image') {
this.updateImageScaling();
}
if (isTimingMode) this.webgl.timer.mark('BackgroundPass.render');
this.renderable.render();
if (isTimingMode) this.webgl.timer.markEnd('BackgroundPass.render');
}
dispose() {
this.clearSkybox();
this.clearImage();
}
}
//
const SkyboxName = 'background-skybox';
type CubeAssets = { [k in keyof CubeFaces]: Asset };
function getCubeAssets(assetManager: AssetManager, faces: SkyboxProps['faces']): CubeAssets {
if (faces.name === 'urls') {
return {
nx: Asset.getUrlAsset(assetManager, faces.params.nx),
ny: Asset.getUrlAsset(assetManager, faces.params.ny),
nz: Asset.getUrlAsset(assetManager, faces.params.nz),
px: Asset.getUrlAsset(assetManager, faces.params.px),
py: Asset.getUrlAsset(assetManager, faces.params.py),
pz: Asset.getUrlAsset(assetManager, faces.params.pz),
};
} else {
return {
nx: faces.params.nx!,
ny: faces.params.ny!,
nz: faces.params.nz!,
px: faces.params.px!,
py: faces.params.py!,
pz: faces.params.pz!,
};
}
}
function getCubeFaces(assetManager: AssetManager, cubeAssets: CubeAssets): CubeFaces {
const resolve = (asset: Asset) => {
return assetManager.resolve(asset, 'binary').run().then(a => new Blob([a.data]));
};
return {
nx: resolve(cubeAssets.nx),
ny: resolve(cubeAssets.ny),
nz: resolve(cubeAssets.nz),
px: resolve(cubeAssets.px),
py: resolve(cubeAssets.py),
pz: resolve(cubeAssets.pz),
};
}
function getSkyboxHash(faces: SkyboxProps['faces']) {
if (faces.name === 'urls') {
return `${SkyboxName}_${faces.params.nx}|${faces.params.ny}|${faces.params.nz}|${faces.params.px}|${faces.params.py}|${faces.params.pz}`;
} else {
return `${SkyboxName}_${faces.params.nx?.id}|${faces.params.ny?.id}|${faces.params.nz?.id}|${faces.params.px?.id}|${faces.params.py?.id}|${faces.params.pz?.id}`;
}
}
function areSkyboxTexturePropsEqual(facesA: SkyboxProps['faces'], facesB: SkyboxProps['faces']) {
return getSkyboxHash(facesA) === getSkyboxHash(facesB);
}
function getSkyboxTexture(ctx: WebGLContext, assetManager: AssetManager, faces: SkyboxProps['faces'], onload?: (errored?: boolean) => void): { texture: Texture, assets: Asset[] } {
const cubeAssets = getCubeAssets(assetManager, faces);
const cubeFaces = getCubeFaces(assetManager, cubeAssets);
const assets = [cubeAssets.nx, cubeAssets.ny, cubeAssets.nz, cubeAssets.px, cubeAssets.py, cubeAssets.pz];
const texture = ctx.resources.cubeTexture(cubeFaces, true, onload);
return { texture, assets };
}
//
const ImageName = 'background-image';
function getImageHash(source: ImageProps['source']) {
if (source.name === 'url') {
return `${ImageName}_${source.params}`;
} else {
return `${ImageName}_${source.params?.id}`;
}
}
function areImageTexturePropsEqual(sourceA: ImageProps['source'], sourceB: ImageProps['source']) {
return getImageHash(sourceA) === getImageHash(sourceB);
}
function getImageTexture(ctx: WebGLContext, assetManager: AssetManager, source: ImageProps['source'], onload?: (errored?: boolean) => void): { texture: Texture, asset: Asset } {
const texture = ctx.resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
const img = new Image();
img.onload = () => {
texture.load(img);
onload?.();
};
img.onerror = () => {
onload?.(true);
};
const asset = source.name === 'url'
? Asset.getUrlAsset(assetManager, source.params)
: source.params!;
assetManager.resolve(asset, 'binary').run().then(a => {
const blob = new Blob([a.data]);
img.src = URL.createObjectURL(blob);
});
return { texture, asset };
}
//
const BackgroundSchema = {
drawCount: ValueSpec('number'),
instanceCount: ValueSpec('number'),
aPosition: AttributeSpec('float32', 2, 0),
tSkybox: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
tImage: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
uImageScale: UniformSpec('v2'),
uImageOffset: UniformSpec('v2'),
uTexSize: UniformSpec('v2'),
uViewport: UniformSpec('v4'),
uViewportAdjusted: UniformSpec('b'),
uViewDirectionProjectionInverse: UniformSpec('m4'),
uGradientColorA: UniformSpec('v3'),
uGradientColorB: UniformSpec('v3'),
uGradientRatio: UniformSpec('f'),
uBlur: UniformSpec('f'),
uOpacity: UniformSpec('f'),
uSaturation: UniformSpec('f'),
uLightness: UniformSpec('f'),
dVariant: DefineSpec('string', ['skybox', 'image', 'verticalGradient', 'horizontalGradient', 'radialGradient']),
};
const SkyboxShaderCode = ShaderCode('background', background_vert, background_frag, {
shaderTextureLod: 'optional'
});
type BackgroundRenderable = ComputeRenderable<Values<typeof BackgroundSchema>>
function getBackgroundRenderable(ctx: WebGLContext, width: number, height: number): BackgroundRenderable {
const values: Values<typeof BackgroundSchema> = {
drawCount: ValueCell.create(6),
instanceCount: ValueCell.create(1),
aPosition: ValueCell.create(QuadPositions),
tSkybox: ValueCell.create(createNullTexture()),
tImage: ValueCell.create(createNullTexture()),
uImageScale: ValueCell.create(Vec2()),
uImageOffset: ValueCell.create(Vec2()),
uTexSize: ValueCell.create(Vec2.create(width, height)),
uViewport: ValueCell.create(Vec4()),
uViewportAdjusted: ValueCell.create(true),
uViewDirectionProjectionInverse: ValueCell.create(Mat4()),
uGradientColorA: ValueCell.create(Vec3()),
uGradientColorB: ValueCell.create(Vec3()),
uGradientRatio: ValueCell.create(0.5),
uBlur: ValueCell.create(0),
uOpacity: ValueCell.create(1),
uSaturation: ValueCell.create(0),
uLightness: ValueCell.create(0),
dVariant: ValueCell.create('skybox'),
};
const schema = { ...BackgroundSchema };
const renderItem = createComputeRenderItem(ctx, 'triangles', SkyboxShaderCode, schema, values);
return createComputeRenderable(renderItem, values);
}

View File

@@ -0,0 +1,309 @@
/**
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Gianluca Tomasello <giagitom@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*
* Adapted from https://github.com/tsherif/webgl2examples, The MIT License, Copyright © 2017 Tarek Sherif, Shuai Shao
*/
import { QuadSchema, QuadValues } from '../../mol-gl/compute/util';
import { ComputeRenderable, createComputeRenderable } from '../../mol-gl/renderable';
import { TextureSpec, UniformSpec, Values } from '../../mol-gl/renderable/schema';
import { ShaderCode } from '../../mol-gl/shader-code';
import { WebGLContext } from '../../mol-gl/webgl/context';
import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
import { Texture } from '../../mol-gl/webgl/texture';
import { ValueCell } from '../../mol-util';
import { quad_vert } from '../../mol-gl/shader/quad.vert';
import { evaluateDpoit_frag } from '../../mol-gl/shader/evaluate-dpoit.frag';
import { blendBackDpoit_frag } from '../../mol-gl/shader/blend-back-dpoit.frag';
import { Framebuffer } from '../../mol-gl/webgl/framebuffer';
import { Vec2 } from '../../mol-math/linear-algebra';
import { isDebugMode, isTimingMode } from '../../mol-util/debug';
import { isWebGL2 } from '../../mol-gl/webgl/compat';
const BlendBackDpoitSchema = {
...QuadSchema,
tDpoitBackColor: TextureSpec('texture', 'rgba', 'float', 'nearest'),
uTexSize: UniformSpec('v2'),
};
const BlendBackDpoitShaderCode = ShaderCode('blend-back-dpoit', quad_vert, blendBackDpoit_frag);
type BlendBackDpoitRenderable = ComputeRenderable<Values<typeof BlendBackDpoitSchema>>
function getBlendBackDpoitRenderable(ctx: WebGLContext, dopitBlendBackTexture: Texture): BlendBackDpoitRenderable {
const values: Values<typeof BlendBackDpoitSchema> = {
...QuadValues,
tDpoitBackColor: ValueCell.create(dopitBlendBackTexture),
uTexSize: ValueCell.create(Vec2.create(dopitBlendBackTexture.getWidth(), dopitBlendBackTexture.getHeight())),
};
const schema = { ...BlendBackDpoitSchema };
const renderItem = createComputeRenderItem(ctx, 'triangles', BlendBackDpoitShaderCode, schema, values);
return createComputeRenderable(renderItem, values);
}
const EvaluateDpoitSchema = {
...QuadSchema,
tDpoitFrontColor: TextureSpec('texture', 'rgba', 'float', 'nearest'),
uTexSize: UniformSpec('v2'),
};
const EvaluateDpoitShaderCode = ShaderCode('evaluate-dpoit', quad_vert, evaluateDpoit_frag);
type EvaluateDpoitRenderable = ComputeRenderable<Values<typeof EvaluateDpoitSchema>>
function getEvaluateDpoitRenderable(ctx: WebGLContext, dpoitFrontColorTexture: Texture): EvaluateDpoitRenderable {
const values: Values<typeof EvaluateDpoitSchema> = {
...QuadValues,
tDpoitFrontColor: ValueCell.create(dpoitFrontColorTexture),
uTexSize: ValueCell.create(Vec2.create(dpoitFrontColorTexture.getWidth(), dpoitFrontColorTexture.getHeight())),
};
const schema = { ...EvaluateDpoitSchema };
const renderItem = createComputeRenderItem(ctx, 'triangles', EvaluateDpoitShaderCode, schema, values);
return createComputeRenderable(renderItem, values);
}
export class DpoitPass {
private readonly DEPTH_CLEAR_VALUE = -99999.0; // NOTE same constant is set in shaders
private readonly MAX_DEPTH = 1.0;
private readonly MIN_DEPTH = 0.0;
private passCount = 0;
private writeId: number;
private readId: number;
private readonly blendBackRenderable: BlendBackDpoitRenderable;
private readonly renderable: EvaluateDpoitRenderable;
private readonly depthFramebuffers: Framebuffer[];
private readonly colorFramebuffers: Framebuffer[];
private readonly depthTextures: Texture[];
private readonly colorFrontTextures: Texture[];
private readonly colorBackTextures: Texture[];
private _supported = false;
get supported() {
return this._supported;
}
bind() {
const { state, gl, extensions: { blendMinMax } } = this.webgl;
// initialize
this.passCount = 0;
this.depthFramebuffers[0].bind();
state.clearColor(this.DEPTH_CLEAR_VALUE, this.DEPTH_CLEAR_VALUE, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
this.depthFramebuffers[1].bind();
state.clearColor(-this.MIN_DEPTH, this.MAX_DEPTH, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
this.colorFramebuffers[0].bind();
state.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
this.colorFramebuffers[1].bind();
state.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
this.depthFramebuffers[0].bind();
state.blendEquation(blendMinMax!.MAX);
state.depthMask(false);
return {
depth: this.depthTextures[1],
frontColor: this.colorFrontTextures[1],
backColor: this.colorBackTextures[1]
};
}
bindDualDepthPeeling() {
const { state, gl, extensions: { blendMinMax } } = this.webgl;
this.readId = this.passCount % 2;
this.writeId = 1 - this.readId; // ping-pong: 0 or 1
this.passCount += 1; // increment for next pass
this.depthFramebuffers[this.writeId].bind();
state.clearColor(this.DEPTH_CLEAR_VALUE, this.DEPTH_CLEAR_VALUE, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
this.colorFramebuffers[this.writeId].bind();
state.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
this.depthFramebuffers[this.writeId].bind();
state.blendEquation(blendMinMax!.MAX);
state.depthMask(false);
return {
depth: this.depthTextures[this.readId],
frontColor: this.colorFrontTextures[this.readId],
backColor: this.colorBackTextures[this.readId]
};
}
renderBlendBack() {
if (isTimingMode) this.webgl.timer.mark('DpoitPass.renderBlendBack');
const { state, gl } = this.webgl;
state.blendEquation(gl.FUNC_ADD);
state.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
ValueCell.update(this.blendBackRenderable.values.tDpoitBackColor, this.colorBackTextures[this.writeId]);
this.blendBackRenderable.update();
this.blendBackRenderable.render();
if (isTimingMode) this.webgl.timer.markEnd('DpoitPass.renderBlendBack');
}
render() {
if (isTimingMode) this.webgl.timer.mark('DpoitPass.render');
const { state, gl } = this.webgl;
state.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
ValueCell.update(this.renderable.values.tDpoitFrontColor, this.colorFrontTextures[this.writeId]);
this.renderable.update();
this.renderable.render();
if (isTimingMode) this.webgl.timer.markEnd('DpoitPass.render');
}
setSize(width: number, height: number) {
const [w, h] = this.renderable.values.uTexSize.ref.value;
if (width !== w || height !== h) {
for (let i = 0; i < 2; i++) {
this.depthTextures[i].define(width, height);
this.colorFrontTextures[i].define(width, height);
this.colorBackTextures[i].define(width, height);
}
ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height));
ValueCell.update(this.blendBackRenderable.values.uTexSize, Vec2.set(this.blendBackRenderable.values.uTexSize.ref.value, width, height));
}
}
reset() {
if (this._supported) this._init();
}
private _init() {
const { extensions: { drawBuffers } } = this.webgl;
for (let i = 0; i < 2; i++) {
// depth
this.depthFramebuffers[i].bind();
drawBuffers!.drawBuffers([
drawBuffers!.COLOR_ATTACHMENT0,
drawBuffers!.COLOR_ATTACHMENT1,
drawBuffers!.COLOR_ATTACHMENT2
]);
this.colorFrontTextures[i].attachFramebuffer(this.depthFramebuffers[i], 'color0');
this.colorBackTextures[i].attachFramebuffer(this.depthFramebuffers[i], 'color1');
this.depthTextures[i].attachFramebuffer(this.depthFramebuffers[i], 'color2');
// color
this.colorFramebuffers[i].bind();
drawBuffers!.drawBuffers([
drawBuffers!.COLOR_ATTACHMENT0,
drawBuffers!.COLOR_ATTACHMENT1
]);
this.colorFrontTextures[i].attachFramebuffer(this.colorFramebuffers[i], 'color0');
this.colorBackTextures[i].attachFramebuffer(this.colorFramebuffers[i], 'color1');
}
}
static isSupported(webgl: WebGLContext) {
const { extensions: { drawBuffers, textureFloat, colorBufferFloat, blendMinMax } } = webgl;
if (!textureFloat || !colorBufferFloat || !drawBuffers || !blendMinMax) {
if (isDebugMode) {
const missing: string[] = [];
if (!textureFloat) missing.push('textureFloat');
if (!colorBufferFloat) missing.push('colorBufferFloat');
if (!drawBuffers) missing.push('drawBuffers');
if (!blendMinMax) missing.push('blendMinMax');
console.log(`Missing "${missing.join('", "')}" extensions required for "dpoit"`);
}
return false;
} else {
return true;
}
}
constructor(private webgl: WebGLContext, width: number, height: number) {
if (!DpoitPass.isSupported(webgl)) return;
const { resources, extensions: { colorBufferHalfFloat, textureHalfFloat } } = webgl;
// textures
if (isWebGL2(webgl.gl)) {
this.depthTextures = [
resources.texture('image-float32', 'rg', 'float', 'nearest'),
resources.texture('image-float32', 'rg', 'float', 'nearest')
];
this.colorFrontTextures = colorBufferHalfFloat && textureHalfFloat ? [
resources.texture('image-float16', 'rgba', 'fp16', 'nearest'),
resources.texture('image-float16', 'rgba', 'fp16', 'nearest')
] : [
resources.texture('image-float32', 'rgba', 'float', 'nearest'),
resources.texture('image-float32', 'rgba', 'float', 'nearest')
];
this.colorBackTextures = colorBufferHalfFloat && textureHalfFloat ? [
resources.texture('image-float16', 'rgba', 'fp16', 'nearest'),
resources.texture('image-float16', 'rgba', 'fp16', 'nearest')
] : [
resources.texture('image-float32', 'rgba', 'float', 'nearest'),
resources.texture('image-float32', 'rgba', 'float', 'nearest')
];
} else {
// in webgl1 drawbuffers must be in the same format for some reason
this.depthTextures = [
resources.texture('image-float32', 'rgba', 'float', 'nearest'),
resources.texture('image-float32', 'rgba', 'float', 'nearest')
];
this.colorFrontTextures = [
resources.texture('image-float32', 'rgba', 'float', 'nearest'),
resources.texture('image-float32', 'rgba', 'float', 'nearest')
];
this.colorBackTextures = [
resources.texture('image-float32', 'rgba', 'float', 'nearest'),
resources.texture('image-float32', 'rgba', 'float', 'nearest')
];
}
this.depthTextures[0].define(width, height);
this.depthTextures[1].define(width, height);
this.colorFrontTextures[0].define(width, height);
this.colorFrontTextures[1].define(width, height);
this.colorBackTextures[0].define(width, height);
this.colorBackTextures[1].define(width, height);
// framebuffers
this.depthFramebuffers = [resources.framebuffer(), resources.framebuffer()];
this.colorFramebuffers = [resources.framebuffer(), resources.framebuffer()];
// renderables
this.blendBackRenderable = getBlendBackDpoitRenderable(webgl, this.colorBackTextures[0]);
this.renderable = getEvaluateDpoitRenderable(webgl, this.colorFrontTextures[0]);
this._supported = true;
this._init();
}
}

View File

@@ -3,6 +3,7 @@
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
* @author Gianluca Tomasello <giagitom@gmail.com>
*/
import { WebGLContext } from '../../mol-gl/webgl/context';
@@ -11,52 +12,24 @@ import { Renderer } from '../../mol-gl/renderer';
import { Scene } from '../../mol-gl/scene';
import { Texture } from '../../mol-gl/webgl/texture';
import { Camera, ICamera } from '../camera';
import { QuadSchema, QuadValues } from '../../mol-gl/compute/util';
import { DefineSpec, TextureSpec, UniformSpec, Values } from '../../mol-gl/renderable/schema';
import { ComputeRenderable, createComputeRenderable } from '../../mol-gl/renderable';
import { ShaderCode } from '../../mol-gl/shader-code';
import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
import { ValueCell } from '../../mol-util';
import { Vec2 } from '../../mol-math/linear-algebra';
import { Helper } from '../helper/helper';
import { quad_vert } from '../../mol-gl/shader/quad.vert';
import { depthMerge_frag } from '../../mol-gl/shader/depth-merge.frag';
import { StereoCamera } from '../camera/stereo';
import { WboitPass } from './wboit';
import { DpoitPass } from './dpoit';
import { AntialiasingPass, PostprocessingPass, PostprocessingProps } from './postprocessing';
import { MarkingPass, MarkingProps } from './marking';
import { CopyRenderable, createCopyRenderable } from '../../mol-gl/compute/util';
const DepthMergeSchema = {
...QuadSchema,
tDepthPrimitives: TextureSpec('texture', 'depth', 'ushort', 'nearest'),
tDepthVolumes: TextureSpec('texture', 'depth', 'ushort', 'nearest'),
uTexSize: UniformSpec('v2'),
dPackedDepth: DefineSpec('boolean'),
};
const DepthMergeShaderCode = ShaderCode('depth-merge', quad_vert, depthMerge_frag);
type DepthMergeRenderable = ComputeRenderable<Values<typeof DepthMergeSchema>>
function getDepthMergeRenderable(ctx: WebGLContext, depthTexturePrimitives: Texture, depthTextureVolumes: Texture, packedDepth: boolean): DepthMergeRenderable {
const values: Values<typeof DepthMergeSchema> = {
...QuadValues,
tDepthPrimitives: ValueCell.create(depthTexturePrimitives),
tDepthVolumes: ValueCell.create(depthTextureVolumes),
uTexSize: ValueCell.create(Vec2.create(depthTexturePrimitives.getWidth(), depthTexturePrimitives.getHeight())),
dPackedDepth: ValueCell.create(packedDepth),
};
const schema = { ...DepthMergeSchema };
const renderItem = createComputeRenderItem(ctx, 'triangles', DepthMergeShaderCode, schema, values);
return createComputeRenderable(renderItem, values);
}
import { isTimingMode } from '../../mol-util/debug';
import { AssetManager } from '../../mol-util/assets';
type Props = {
postprocessing: PostprocessingProps
marking: MarkingProps
postprocessing: PostprocessingProps;
marking: MarkingProps;
transparentBackground: boolean;
dpoitIterations: number;
}
type RenderContext = {
@@ -70,21 +43,19 @@ export class DrawPass {
private readonly drawTarget: RenderTarget;
readonly colorTarget: RenderTarget;
readonly depthTexture: Texture;
readonly depthTexturePrimitives: Texture;
readonly depthTextureTransparent: Texture;
readonly depthTextureOpaque: Texture;
readonly packedDepth: boolean;
private depthTarget: RenderTarget;
private depthTargetPrimitives: RenderTarget | null;
private depthTargetVolumes: RenderTarget | null;
private depthTextureVolumes: Texture;
private depthMerge: DepthMergeRenderable;
private depthTargetTransparent: RenderTarget;
private depthTargetOpaque: RenderTarget | null;
private copyFboTarget: CopyRenderable;
private copyFboPostprocessing: CopyRenderable;
private wboit: WboitPass | undefined;
private readonly wboit: WboitPass | undefined;
private readonly dpoit: DpoitPass | undefined;
private readonly marking: MarkingPass;
readonly postprocessing: PostprocessingPass;
private readonly antialiasing: AntialiasingPass;
@@ -93,31 +64,30 @@ export class DrawPass {
return !!this.wboit?.supported;
}
constructor(private webgl: WebGLContext, width: number, height: number, enableWboit: boolean) {
get dpoitEnabled() {
return !!this.dpoit?.supported;
}
constructor(private webgl: WebGLContext, assetManager: AssetManager, width: number, height: number, enableWboit: boolean, enableDpoit: boolean) {
const { extensions, resources, isWebGL2 } = webgl;
this.drawTarget = createNullRenderTarget(webgl.gl);
this.colorTarget = webgl.createRenderTarget(width, height, true, 'uint8', 'linear');
this.packedDepth = !extensions.depthTexture;
this.depthTarget = webgl.createRenderTarget(width, height);
this.depthTexture = this.depthTarget.texture;
this.depthTargetTransparent = webgl.createRenderTarget(width, height);
this.depthTextureTransparent = this.depthTargetTransparent.texture;
this.depthTargetPrimitives = this.packedDepth ? webgl.createRenderTarget(width, height) : null;
this.depthTargetVolumes = this.packedDepth ? webgl.createRenderTarget(width, height) : null;
this.depthTargetOpaque = this.packedDepth ? webgl.createRenderTarget(width, height) : null;
this.depthTexturePrimitives = this.depthTargetPrimitives ? this.depthTargetPrimitives.texture : resources.texture('image-depth', 'depth', isWebGL2 ? 'float' : 'ushort', 'nearest');
this.depthTextureVolumes = this.depthTargetVolumes ? this.depthTargetVolumes.texture : resources.texture('image-depth', 'depth', isWebGL2 ? 'float' : 'ushort', 'nearest');
this.depthTextureOpaque = this.depthTargetOpaque ? this.depthTargetOpaque.texture : resources.texture('image-depth', 'depth', isWebGL2 ? 'float' : 'ushort', 'nearest');
if (!this.packedDepth) {
this.depthTexturePrimitives.define(width, height);
this.depthTextureVolumes.define(width, height);
this.depthTextureOpaque.define(width, height);
}
this.depthMerge = getDepthMergeRenderable(webgl, this.depthTexturePrimitives, this.depthTextureVolumes, this.packedDepth);
this.wboit = enableWboit ? new WboitPass(webgl, width, height) : undefined;
this.dpoit = enableDpoit ? new DpoitPass(webgl, width, height) : undefined;
this.marking = new MarkingPass(webgl, width, height);
this.postprocessing = new PostprocessingPass(webgl, this);
this.postprocessing = new PostprocessingPass(webgl, assetManager, this);
this.antialiasing = new AntialiasingPass(webgl, this);
this.copyFboTarget = createCopyRenderable(webgl, this.colorTarget.texture);
@@ -126,6 +96,7 @@ export class DrawPass {
reset() {
this.wboit?.reset();
this.dpoit?.reset();
}
setSize(width: number, height: number) {
@@ -134,22 +105,14 @@ export class DrawPass {
if (width !== w || height !== h) {
this.colorTarget.setSize(width, height);
this.depthTarget.setSize(width, height);
this.depthTargetTransparent.setSize(width, height);
if (this.depthTargetPrimitives) {
this.depthTargetPrimitives.setSize(width, height);
if (this.depthTargetOpaque) {
this.depthTargetOpaque.setSize(width, height);
} else {
this.depthTexturePrimitives.define(width, height);
this.depthTextureOpaque.define(width, height);
}
if (this.depthTargetVolumes) {
this.depthTargetVolumes.setSize(width, height);
} else {
this.depthTextureVolumes.define(width, height);
}
ValueCell.update(this.depthMerge.values.uTexSize, Vec2.set(this.depthMerge.values.uTexSize.ref.value, width, height));
ValueCell.update(this.copyFboTarget.values.uTexSize, Vec2.set(this.copyFboTarget.values.uTexSize.ref.value, width, height));
ValueCell.update(this.copyFboPostprocessing.values.uTexSize, Vec2.set(this.copyFboPostprocessing.values.uTexSize.ref.value, width, height));
@@ -157,127 +120,191 @@ export class DrawPass {
this.wboit.setSize(width, height);
}
if (this.dpoit?.supported) {
this.dpoit.setSize(width, height);
}
this.marking.setSize(width, height);
this.postprocessing.setSize(width, height);
this.antialiasing.setSize(width, height);
}
}
private _depthMerge() {
const { state, gl } = this.webgl;
private _renderDpoit(renderer: Renderer, camera: ICamera, scene: Scene, iterations: number, transparentBackground: boolean, postprocessingProps: PostprocessingProps) {
if (!this.dpoit?.supported) throw new Error('expected dpoit to be supported');
this.depthMerge.update();
this.depthTarget.bind();
state.disable(gl.BLEND);
state.disable(gl.DEPTH_TEST);
state.disable(gl.CULL_FACE);
state.depthMask(false);
state.clearColor(1, 1, 1, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
this.depthMerge.render();
this.depthTextureOpaque.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
renderer.clear(true);
// render opaque primitives
if (scene.hasOpaque) {
renderer.renderDpoitOpaque(scene.primitives, camera, null);
}
if (PostprocessingPass.isEnabled(postprocessingProps)) {
if (PostprocessingPass.isOutlineEnabled(postprocessingProps)) {
this.depthTargetTransparent.bind();
renderer.clearDepth(true);
if (scene.opacityAverage < 1) {
renderer.renderDepthTransparent(scene.primitives, camera, this.depthTextureOpaque);
}
}
this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps);
}
this.depthTextureOpaque.detachFramebuffer(this.colorTarget.framebuffer, 'depth');
// render transparent primitives
if (scene.opacityAverage < 1) {
const target = PostprocessingPass.isEnabled(postprocessingProps)
? this.postprocessing.target : this.colorTarget;
const dpoitTextures = this.dpoit.bind();
renderer.renderDpoitTransparent(scene.primitives, camera, this.depthTextureOpaque, dpoitTextures);
for (let i = 0; i < iterations; i++) {
if (isTimingMode) this.webgl.timer.mark('DpoitPass.layer');
const dpoitTextures = this.dpoit.bindDualDepthPeeling();
renderer.renderDpoitTransparent(scene.primitives, camera, this.depthTextureOpaque, dpoitTextures);
target.bind();
this.dpoit.renderBlendBack();
if (isTimingMode) this.webgl.timer.markEnd('DpoitPass.layer');
}
// evaluate dpoit
target.bind();
this.dpoit.render();
}
// render transparent volumes
if (scene.volumes.renderables.length > 0) {
renderer.renderDpoitVolume(scene.volumes, camera, this.depthTextureOpaque);
}
}
private _renderWboit(renderer: Renderer, camera: ICamera, scene: Scene, transparentBackground: boolean, postprocessingProps: PostprocessingProps) {
if (!this.wboit?.supported) throw new Error('expected wboit to be supported');
this.colorTarget.bind();
this.depthTextureOpaque.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
renderer.clear(true);
// render opaque primitives
this.depthTexturePrimitives.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
this.colorTarget.bind();
renderer.clearDepth();
renderer.renderWboitOpaque(scene.primitives, camera, null);
// render opaque volumes
this.depthTextureVolumes.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
this.colorTarget.bind();
renderer.clearDepth();
renderer.renderWboitOpaque(scene.volumes, camera, this.depthTexturePrimitives);
// merge depth of opaque primitives and volumes
this._depthMerge();
if (scene.hasOpaque) {
renderer.renderWboitOpaque(scene.primitives, camera, null);
}
if (PostprocessingPass.isEnabled(postprocessingProps)) {
if (PostprocessingPass.isOutlineEnabled(postprocessingProps)) {
this.depthTargetTransparent.bind();
renderer.clearDepth(true);
if (scene.opacityAverage < 1) {
renderer.renderDepthTransparent(scene.primitives, camera, this.depthTextureOpaque);
}
}
this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps);
}
// render transparent primitives and volumes
this.wboit.bind();
renderer.renderWboitTransparent(scene.primitives, camera, this.depthTexture);
renderer.renderWboitTransparent(scene.volumes, camera, this.depthTexture);
if (scene.opacityAverage < 1 || scene.volumes.renderables.length > 0) {
this.wboit.bind();
if (scene.opacityAverage < 1) {
renderer.renderWboitTransparent(scene.primitives, camera, this.depthTextureOpaque);
}
if (scene.volumes.renderables.length > 0) {
renderer.renderWboitTransparent(scene.volumes, camera, this.depthTextureOpaque);
}
// evaluate wboit
if (PostprocessingPass.isEnabled(postprocessingProps)) {
this.depthTexturePrimitives.attachFramebuffer(this.postprocessing.target.framebuffer, 'depth');
this.postprocessing.target.bind();
} else {
this.depthTexturePrimitives.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
this.colorTarget.bind();
// evaluate wboit
if (PostprocessingPass.isEnabled(postprocessingProps)) {
this.postprocessing.target.bind();
} else {
this.colorTarget.bind();
}
this.wboit.render();
}
this.wboit.render();
}
private _renderBlended(renderer: Renderer, camera: ICamera, scene: Scene, toDrawingBuffer: boolean, transparentBackground: boolean, postprocessingProps: PostprocessingProps) {
if (toDrawingBuffer) {
this.drawTarget.bind();
} else {
this.colorTarget.bind();
if (!this.packedDepth) {
this.depthTexturePrimitives.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
this.depthTextureOpaque.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
} else {
this.colorTarget.bind();
}
}
renderer.clear(true);
renderer.renderBlendedOpaque(scene.primitives, camera, null);
if (scene.hasOpaque) {
renderer.renderBlendedOpaque(scene.primitives, camera, null);
}
if (!toDrawingBuffer) {
// do a depth pass if not rendering to drawing buffer and
// extensions.depthTexture is unsupported (i.e. depthTarget is set)
if (this.depthTargetPrimitives) {
this.depthTargetPrimitives.bind();
renderer.clear(false);
// TODO: this should only render opaque
renderer.renderDepth(scene.primitives, camera, null);
if (this.depthTargetOpaque) {
this.depthTargetOpaque.bind();
renderer.clearDepth(true);
renderer.renderDepthOpaque(scene.primitives, camera, null);
this.colorTarget.bind();
}
// do direct-volume rendering
if (!this.packedDepth) {
this.depthTextureVolumes.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
renderer.clearDepth(); // from previous frame
}
renderer.renderBlendedVolumeOpaque(scene.volumes, camera, this.depthTexturePrimitives);
// do volume depth pass if extensions.depthTexture is unsupported (i.e. depthTarget is set)
if (this.depthTargetVolumes) {
this.depthTargetVolumes.bind();
renderer.clear(false);
renderer.renderDepth(scene.volumes, camera, this.depthTexturePrimitives);
this.colorTarget.bind();
}
// merge depths from primitive and volume rendering
this._depthMerge();
this.colorTarget.bind();
if (PostprocessingPass.isEnabled(postprocessingProps)) {
this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps);
}
renderer.renderBlendedVolumeTransparent(scene.volumes, camera, this.depthTexturePrimitives);
if (!this.packedDepth) {
this.depthTextureOpaque.detachFramebuffer(this.postprocessing.target.framebuffer, 'depth');
} else {
this.colorTarget.depthRenderbuffer?.detachFramebuffer(this.postprocessing.target.framebuffer);
}
const target = PostprocessingPass.isEnabled(postprocessingProps)
? this.postprocessing.target : this.colorTarget;
if (!this.packedDepth) {
this.depthTexturePrimitives.attachFramebuffer(target.framebuffer, 'depth');
if (PostprocessingPass.isOutlineEnabled(postprocessingProps)) {
this.depthTargetTransparent.bind();
renderer.clearDepth(true);
if (scene.opacityAverage < 1) {
renderer.renderDepthTransparent(scene.primitives, camera, this.depthTextureOpaque);
}
}
this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps);
if (!this.packedDepth) {
this.depthTextureOpaque.attachFramebuffer(this.postprocessing.target.framebuffer, 'depth');
} else {
this.colorTarget.depthRenderbuffer?.attachFramebuffer(this.postprocessing.target.framebuffer);
}
}
if (scene.volumes.renderables.length > 0) {
const target = PostprocessingPass.isEnabled(postprocessingProps)
? this.postprocessing.target : this.colorTarget;
if (!this.packedDepth) {
this.depthTextureOpaque.detachFramebuffer(target.framebuffer, 'depth');
} else {
this.colorTarget.depthRenderbuffer?.detachFramebuffer(target.framebuffer);
}
target.bind();
renderer.renderBlendedVolume(scene.volumes, camera, this.depthTextureOpaque);
if (!this.packedDepth) {
this.depthTextureOpaque.attachFramebuffer(target.framebuffer, 'depth');
} else {
this.colorTarget.depthRenderbuffer?.attachFramebuffer(target.framebuffer);
}
target.bind();
}
target.bind();
}
renderer.renderBlendedTransparent(scene.primitives, camera, null);
if (scene.opacityAverage < 1) {
renderer.renderBlendedTransparent(scene.primitives, camera, null);
}
}
private _render(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, toDrawingBuffer: boolean, props: Props) {
private _render(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, toDrawingBuffer: boolean, transparentBackground: boolean, props: Props) {
const volumeRendering = scene.volumes.renderables.length > 0;
const postprocessingEnabled = PostprocessingPass.isEnabled(props.postprocessing);
const antialiasingEnabled = AntialiasingPass.isEnabled(props.postprocessing);
@@ -287,55 +314,54 @@ export class DrawPass {
renderer.setViewport(x, y, width, height);
renderer.update(camera);
if (props.transparentBackground && !antialiasingEnabled && toDrawingBuffer) {
if (transparentBackground && !antialiasingEnabled && toDrawingBuffer) {
this.drawTarget.bind();
renderer.clear(false);
}
if (this.wboitEnabled) {
this._renderWboit(renderer, camera, scene, props.transparentBackground, props.postprocessing);
this._renderWboit(renderer, camera, scene, transparentBackground, props.postprocessing);
} else if (this.dpoitEnabled) {
this._renderDpoit(renderer, camera, scene, props.dpoitIterations, transparentBackground, props.postprocessing);
} else {
this._renderBlended(renderer, camera, scene, !volumeRendering && !postprocessingEnabled && !antialiasingEnabled && toDrawingBuffer, props.transparentBackground, props.postprocessing);
this._renderBlended(renderer, camera, scene, !volumeRendering && !postprocessingEnabled && !antialiasingEnabled && toDrawingBuffer, transparentBackground, props.postprocessing);
}
if (postprocessingEnabled) {
this.postprocessing.target.bind();
} else if (!toDrawingBuffer || volumeRendering || this.wboitEnabled) {
this.colorTarget.bind();
} else {
this.drawTarget.bind();
}
const target = postprocessingEnabled
? this.postprocessing.target
: !toDrawingBuffer || volumeRendering || this.wboitEnabled || this.dpoitEnabled
? this.colorTarget
: this.drawTarget;
if (markingEnabled) {
const markerAverage = scene.getMarkerAverage();
if (markerAverage > 0) {
const markingDepthTest = props.marking.ghostEdgeStrength < 1;
if (markingDepthTest && markerAverage !== 1) {
this.marking.depthTarget.bind();
renderer.clear(false, true);
renderer.renderMarkingDepth(scene.primitives, camera, null);
}
this.marking.maskTarget.bind();
if (markingEnabled && scene.markerAverage > 0) {
const markingDepthTest = props.marking.ghostEdgeStrength < 1;
if (markingDepthTest && scene.markerAverage !== 1) {
this.marking.depthTarget.bind();
renderer.clear(false, true);
renderer.renderMarkingMask(scene.primitives, camera, markingDepthTest ? this.marking.depthTarget.texture : null);
this.marking.update(props.marking);
this.marking.render(camera.viewport, postprocessingEnabled ? this.postprocessing.target : this.colorTarget);
renderer.renderMarkingDepth(scene.primitives, camera, null);
}
this.marking.maskTarget.bind();
renderer.clear(false, true);
renderer.renderMarkingMask(scene.primitives, camera, markingDepthTest ? this.marking.depthTarget.texture : null);
this.marking.update(props.marking);
this.marking.render(camera.viewport, target);
} else {
target.bind();
}
if (helper.debug.isEnabled) {
helper.debug.syncVisibility();
renderer.renderBlended(helper.debug.scene, camera, null);
renderer.renderBlended(helper.debug.scene, camera);
}
if (helper.handle.isEnabled) {
renderer.renderBlended(helper.handle.scene, camera, null);
renderer.renderBlended(helper.handle.scene, camera);
}
if (helper.camera.isEnabled) {
helper.camera.update(camera);
renderer.update(helper.camera.camera);
renderer.renderBlended(helper.camera.scene, helper.camera.camera, null);
renderer.renderBlended(helper.camera.scene, helper.camera.camera);
}
if (antialiasingEnabled) {
@@ -346,7 +372,7 @@ export class DrawPass {
this.webgl.state.disable(this.webgl.gl.DEPTH_TEST);
if (postprocessingEnabled) {
this.copyFboPostprocessing.render();
} else if (volumeRendering || this.wboitEnabled) {
} else if (volumeRendering || this.wboitEnabled || this.dpoitEnabled) {
this.copyFboTarget.render();
}
}
@@ -355,17 +381,27 @@ export class DrawPass {
}
render(ctx: RenderContext, props: Props, toDrawingBuffer: boolean) {
if (isTimingMode) this.webgl.timer.mark('DrawPass.render');
const { renderer, camera, scene, helper } = ctx;
renderer.setTransparentBackground(props.transparentBackground);
this.postprocessing.setTransparentBackground(props.transparentBackground);
const transparentBackground = props.transparentBackground || this.postprocessing.background.isEnabled(props.postprocessing.background);
renderer.setTransparentBackground(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, props);
this._render(renderer, camera.right, scene, helper, toDrawingBuffer, props);
if (isTimingMode) this.webgl.timer.mark('StereoCamera.left');
this._render(renderer, camera.left, scene, helper, toDrawingBuffer, transparentBackground, props);
if (isTimingMode) this.webgl.timer.markEnd('StereoCamera.left');
if (isTimingMode) this.webgl.timer.mark('StereoCamera.right');
this._render(renderer, camera.right, scene, helper, toDrawingBuffer, transparentBackground, props);
if (isTimingMode) this.webgl.timer.markEnd('StereoCamera.right');
} else {
this._render(renderer, camera, scene, helper, toDrawingBuffer, props);
this._render(renderer, camera, scene, helper, toDrawingBuffer, transparentBackground, props);
}
if (isTimingMode) this.webgl.timer.markEnd('DrawPass.render');
}
getColorTarget(postprocessingProps: PostprocessingProps): RenderTarget {
@@ -376,4 +412,4 @@ export class DrawPass {
}
return this.colorTarget;
}
}
}

View File

@@ -18,6 +18,7 @@ import { quad_vert } from '../../mol-gl/shader/quad.vert';
import { fxaa_frag } from '../../mol-gl/shader/fxaa.frag';
import { Viewport } from '../camera/util';
import { RenderTarget } from '../../mol-gl/webgl/render-target';
import { isTimingMode } from '../../mol-util/debug';
export const FxaaParams = {
edgeThresholdMin: PD.Numeric(0.0312, { min: 0.0312, max: 0.0833, step: 0.0001 }, { description: 'Trims the algorithm from processing darks.' }),
@@ -43,8 +44,8 @@ export class FxaaPass {
state.depthMask(false);
const { x, y, width, height } = viewport;
gl.viewport(x, y, width, height);
gl.scissor(x, y, width, height);
state.viewport(x, y, width, height);
state.scissor(x, y, width, height);
state.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
@@ -83,6 +84,7 @@ export class FxaaPass {
}
render(viewport: Viewport, target: RenderTarget | undefined) {
if (isTimingMode) this.webgl.timer.mark('FxaaPass.render');
if (target) {
target.bind();
} else {
@@ -90,6 +92,7 @@ export class FxaaPass {
}
this.updateState(viewport);
this.renderable.render();
if (isTimingMode) this.webgl.timer.markEnd('FxaaPass.render');
}
}

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>
*/
@@ -18,9 +18,11 @@ import { PixelData } from '../../mol-util/image';
import { Helper } from '../helper/helper';
import { CameraHelper, CameraHelperParams } from '../helper/camera-helper';
import { MarkingParams } from './marking';
import { AssetManager } from '../../mol-util/assets';
export const ImageParams = {
transparentBackground: PD.Boolean(false),
dpoitIterations: PD.Numeric(2, { min: 1, max: 10, step: 1 }),
multiSample: PD.Group(MultiSampleParams),
postprocessing: PD.Group(PostprocessingParams),
marking: PD.Group(MarkingParams),
@@ -47,10 +49,10 @@ export class ImagePass {
get width() { return this._width; }
get height() { return this._height; }
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, helper: Helper, enableWboit: boolean, props: Partial<ImageProps>) {
constructor(private webgl: WebGLContext, assetManager: AssetManager, private renderer: Renderer, private scene: Scene, private camera: Camera, helper: Helper, enableWboit: boolean, enableDpoit: boolean, props: Partial<ImageProps>) {
this.props = { ...PD.getDefaultValues(ImageParams), ...props };
this.drawPass = new DrawPass(webgl, 128, 128, enableWboit);
this.drawPass = new DrawPass(webgl, assetManager, 128, 128, enableWboit, enableDpoit);
this.multiSamplePass = new MultiSamplePass(webgl, this.drawPass);
this.multiSampleHelper = new MultiSampleHelper(this.multiSamplePass);
@@ -63,6 +65,14 @@ export class ImagePass {
this.setSize(1024, 768);
}
updateBackground() {
return new Promise<void>(resolve => {
this.drawPass.postprocessing.background.update(this.camera, this.props.postprocessing.background, () => {
resolve();
});
});
}
setSize(width: number, height: number) {
if (width === this._width && height === this._height) return;

View File

@@ -20,6 +20,7 @@ import { Viewport } from '../camera/util';
import { RenderTarget } from '../../mol-gl/webgl/render-target';
import { Color } from '../../mol-util/color';
import { edge_frag } from '../../mol-gl/shader/marking/edge.frag';
import { isTimingMode } from '../../mol-util/debug';
export const MarkingParams = {
enabled: PD.Boolean(true),
@@ -63,8 +64,8 @@ export class MarkingPass {
state.depthMask(false);
const { x, y, width, height } = viewport;
gl.viewport(x, y, width, height);
gl.scissor(x, y, width, height);
state.viewport(x, y, width, height);
state.scissor(x, y, width, height);
state.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
@@ -81,8 +82,8 @@ export class MarkingPass {
state.depthMask(false);
const { x, y, width, height } = viewport;
gl.viewport(x, y, width, height);
gl.scissor(x, y, width, height);
state.viewport(x, y, width, height);
state.scissor(x, y, width, height);
}
setSize(width: number, height: number) {
@@ -117,6 +118,7 @@ export class MarkingPass {
}
render(viewport: Viewport, target: RenderTarget | undefined) {
if (isTimingMode) this.webgl.timer.mark('MarkingPass.render');
this.edgesTarget.bind();
this.setEdgeState(viewport);
this.edge.render();
@@ -128,6 +130,7 @@ export class MarkingPass {
}
this.setOverlayState(viewport);
this.overlay.render();
if (isTimingMode) this.webgl.timer.markEnd('MarkingPass.render');
}
}

View File

@@ -25,6 +25,7 @@ import { StereoCamera } from '../camera/stereo';
import { quad_vert } from '../../mol-gl/shader/quad.vert';
import { compose_frag } from '../../mol-gl/shader/compose.frag';
import { MarkingProps } from './marking';
import { isTimingMode } from '../../mol-util/debug';
const ComposeSchema = {
...QuadSchema,
@@ -60,6 +61,7 @@ type Props = {
postprocessing: PostprocessingProps
marking: MarkingProps
transparentBackground: boolean;
dpoitIterations: number;
}
type RenderContext = {
@@ -126,6 +128,7 @@ export class MultiSamplePass {
const { camera } = ctx;
const { compose, composeTarget, drawPass, webgl } = this;
const { gl, state } = webgl;
if (isTimingMode) webgl.timer.mark('MultiSamplePass.renderMultiSample');
// based on the Multisample Anti-Aliasing Render Pass
// contributed to three.js by bhouston / http://clara.io/
@@ -174,8 +177,8 @@ export class MultiSamplePass {
state.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE);
state.disable(gl.DEPTH_TEST);
state.depthMask(false);
gl.viewport(x, y, width, height);
gl.scissor(x, y, width, height);
state.viewport(x, y, width, height);
state.scissor(x, y, width, height);
if (i === 0) {
state.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
@@ -190,20 +193,22 @@ export class MultiSamplePass {
compose.update();
this.bindOutputTarget(toDrawingBuffer);
gl.viewport(x, y, width, height);
gl.scissor(x, y, width, height);
state.viewport(x, y, width, height);
state.scissor(x, y, width, height);
state.disable(gl.BLEND);
compose.render();
camera.viewOffset.enabled = false;
camera.update();
if (isTimingMode) webgl.timer.markEnd('MultiSamplePass.renderMultiSample');
}
private renderTemporalMultiSample(sampleIndex: number, ctx: RenderContext, props: Props, toDrawingBuffer: boolean) {
const { camera } = ctx;
const { compose, composeTarget, holdTarget, drawPass, webgl } = this;
const { gl, state } = webgl;
if (isTimingMode) webgl.timer.mark('MultiSamplePass.renderTemporalMultiSample');
// based on the Multisample Anti-Aliasing Render Pass
// contributed to three.js by bhouston / http://clara.io/
@@ -227,8 +232,8 @@ export class MultiSamplePass {
state.disable(gl.BLEND);
state.disable(gl.DEPTH_TEST);
state.depthMask(false);
gl.viewport(x, y, width, height);
gl.scissor(x, y, width, height);
state.viewport(x, y, width, height);
state.scissor(x, y, width, height);
compose.render();
sampleIndex += 1;
} else {
@@ -263,8 +268,8 @@ export class MultiSamplePass {
state.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE);
state.disable(gl.DEPTH_TEST);
state.depthMask(false);
gl.viewport(x, y, width, height);
gl.scissor(x, y, width, height);
state.viewport(x, y, width, height);
state.scissor(x, y, width, height);
if (sampleIndex === 0) {
state.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
@@ -279,8 +284,8 @@ export class MultiSamplePass {
drawPass.postprocessing.setOcclusionOffset(0, 0);
this.bindOutputTarget(toDrawingBuffer);
gl.viewport(x, y, width, height);
gl.scissor(x, y, width, height);
state.viewport(x, y, width, height);
state.scissor(x, y, width, height);
const accumulationWeight = sampleIndex * sampleWeight;
if (accumulationWeight > 0) {
@@ -301,6 +306,7 @@ export class MultiSamplePass {
camera.viewOffset.enabled = false;
camera.update();
if (isTimingMode) webgl.timer.markEnd('MultiSamplePass.renderTemporalMultiSample');
return sampleIndex >= offsetList.length ? -2 : sampleIndex;
}

View File

@@ -8,22 +8,26 @@ import { DrawPass } from './draw';
import { PickPass } from './pick';
import { MultiSamplePass } from './multi-sample';
import { WebGLContext } from '../../mol-gl/webgl/context';
import { AssetManager } from '../../mol-util/assets';
export class Passes {
readonly draw: DrawPass;
readonly pick: PickPass;
readonly multiSample: MultiSamplePass;
constructor(private webgl: WebGLContext, attribs: Partial<{ pickScale: number, enableWboit: boolean }> = {}) {
constructor(private webgl: WebGLContext, assetManager: AssetManager, attribs: Partial<{ pickScale: number, enableWboit: boolean, enableDpoit: boolean }> = {}) {
const { gl } = webgl;
this.draw = new DrawPass(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight, attribs.enableWboit || false);
this.draw = new DrawPass(webgl, assetManager, gl.drawingBufferWidth, gl.drawingBufferHeight, attribs.enableWboit || false, attribs.enableDpoit || false);
this.pick = new PickPass(webgl, this.draw, attribs.pickScale || 0.25);
this.multiSample = new MultiSamplePass(webgl, this.draw);
}
updateSize() {
const { gl } = this.webgl;
this.draw.setSize(gl.drawingBufferWidth, gl.drawingBufferHeight);
// Avoid setting dimensions to 0x0 because it causes "empty textures are not allowed" error.
const width = Math.max(gl.drawingBufferWidth, 2);
const height = Math.max(gl.drawingBufferHeight, 2);
this.draw.setSize(width, height);
this.pick.syncSize();
this.multiSample.syncSize();
}

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>
*/
@@ -7,11 +7,15 @@
import { PickingId } from '../../mol-geo/geometry/picking';
import { PickType, Renderer } from '../../mol-gl/renderer';
import { Scene } from '../../mol-gl/scene';
import { isWebGL2 } from '../../mol-gl/webgl/compat';
import { WebGLContext } from '../../mol-gl/webgl/context';
import { GraphicsRenderVariant } from '../../mol-gl/webgl/render-item';
import { Framebuffer } from '../../mol-gl/webgl/framebuffer';
import { RenderTarget } from '../../mol-gl/webgl/render-target';
import { Renderbuffer } from '../../mol-gl/webgl/renderbuffer';
import { Texture } from '../../mol-gl/webgl/texture';
import { Vec3 } from '../../mol-math/linear-algebra';
import { spiral2d } from '../../mol-math/misc';
import { isTimingMode } from '../../mol-util/debug';
import { unpackRGBToInt, unpackRGBAToDepth } from '../../mol-util/number-packing';
import { Camera, ICamera } from '../camera';
import { StereoCamera } from '../camera/stereo';
@@ -25,10 +29,24 @@ const NullId = Math.pow(2, 24) - 2;
export type PickData = { id: PickingId, position: Vec3 }
export class PickPass {
readonly objectPickTarget: RenderTarget;
readonly instancePickTarget: RenderTarget;
readonly groupPickTarget: RenderTarget;
readonly depthPickTarget: RenderTarget;
private readonly objectPickTarget: RenderTarget;
private readonly instancePickTarget: RenderTarget;
private readonly groupPickTarget: RenderTarget;
private readonly depthPickTarget: RenderTarget;
private readonly framebuffer: Framebuffer;
private readonly objectPickTexture: Texture;
private readonly instancePickTexture: Texture;
private readonly groupPickTexture: Texture;
private readonly depthPickTexture: Texture;
private readonly objectPickFramebuffer: Framebuffer;
private readonly instancePickFramebuffer: Framebuffer;
private readonly groupPickFramebuffer: Framebuffer;
private readonly depthPickFramebuffer: Framebuffer;
private readonly depthRenderbuffer: Renderbuffer;
private pickWidth: number;
private pickHeight: number;
@@ -38,10 +56,89 @@ export class PickPass {
this.pickWidth = Math.ceil(drawPass.colorTarget.getWidth() * pickScale);
this.pickHeight = Math.ceil(drawPass.colorTarget.getHeight() * pickScale);
this.objectPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
this.instancePickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
this.groupPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
this.depthPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
const { resources, extensions: { drawBuffers }, gl } = webgl;
if (drawBuffers) {
this.objectPickTexture = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
this.objectPickTexture.define(this.pickWidth, this.pickHeight);
this.instancePickTexture = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
this.instancePickTexture.define(this.pickWidth, this.pickHeight);
this.groupPickTexture = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
this.groupPickTexture.define(this.pickWidth, this.pickHeight);
this.depthPickTexture = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
this.depthPickTexture.define(this.pickWidth, this.pickHeight);
this.framebuffer = resources.framebuffer();
this.objectPickFramebuffer = resources.framebuffer();
this.instancePickFramebuffer = resources.framebuffer();
this.groupPickFramebuffer = resources.framebuffer();
this.depthPickFramebuffer = resources.framebuffer();
this.framebuffer.bind();
drawBuffers!.drawBuffers([
drawBuffers!.COLOR_ATTACHMENT0,
drawBuffers!.COLOR_ATTACHMENT1,
drawBuffers!.COLOR_ATTACHMENT2,
drawBuffers!.COLOR_ATTACHMENT3,
]);
this.objectPickTexture.attachFramebuffer(this.framebuffer, 'color0');
this.instancePickTexture.attachFramebuffer(this.framebuffer, 'color1');
this.groupPickTexture.attachFramebuffer(this.framebuffer, 'color2');
this.depthPickTexture.attachFramebuffer(this.framebuffer, 'color3');
this.depthRenderbuffer = isWebGL2(gl)
? resources.renderbuffer('depth32f', 'depth', this.pickWidth, this.pickHeight)
: resources.renderbuffer('depth16', 'depth', this.pickWidth, this.pickHeight);
this.depthRenderbuffer.attachFramebuffer(this.framebuffer);
this.objectPickTexture.attachFramebuffer(this.objectPickFramebuffer, 'color0');
this.instancePickTexture.attachFramebuffer(this.instancePickFramebuffer, 'color0');
this.groupPickTexture.attachFramebuffer(this.groupPickFramebuffer, 'color0');
this.depthPickTexture.attachFramebuffer(this.depthPickFramebuffer, 'color0');
} else {
this.objectPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
this.instancePickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
this.groupPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
this.depthPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
}
}
bindObject() {
if (this.webgl.extensions.drawBuffers) {
this.objectPickFramebuffer.bind();
} else {
this.objectPickTarget.bind();
}
}
bindInstance() {
if (this.webgl.extensions.drawBuffers) {
this.instancePickFramebuffer.bind();
} else {
this.instancePickTarget.bind();
}
}
bindGroup() {
if (this.webgl.extensions.drawBuffers) {
this.groupPickFramebuffer.bind();
} else {
this.groupPickTarget.bind();
}
}
bindDepth() {
if (this.webgl.extensions.drawBuffers) {
this.depthPickFramebuffer.bind();
} else {
this.depthPickTarget.bind();
}
}
get drawingBufferHeight() {
@@ -57,21 +154,30 @@ export class PickPass {
this.pickWidth = pickWidth;
this.pickHeight = pickHeight;
this.objectPickTarget.setSize(this.pickWidth, this.pickHeight);
this.instancePickTarget.setSize(this.pickWidth, this.pickHeight);
this.groupPickTarget.setSize(this.pickWidth, this.pickHeight);
this.depthPickTarget.setSize(this.pickWidth, this.pickHeight);
if (this.webgl.extensions.drawBuffers) {
this.objectPickTexture.define(this.pickWidth, this.pickHeight);
this.instancePickTexture.define(this.pickWidth, this.pickHeight);
this.groupPickTexture.define(this.pickWidth, this.pickHeight);
this.depthPickTexture.define(this.pickWidth, this.pickHeight);
this.depthRenderbuffer.setSize(this.pickWidth, this.pickHeight);
} else {
this.objectPickTarget.setSize(this.pickWidth, this.pickHeight);
this.instancePickTarget.setSize(this.pickWidth, this.pickHeight);
this.groupPickTarget.setSize(this.pickWidth, this.pickHeight);
this.depthPickTarget.setSize(this.pickWidth, this.pickHeight);
}
}
}
private renderVariant(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, variant: GraphicsRenderVariant, pickType: number) {
const depth = this.drawPass.depthTexturePrimitives;
private renderVariant(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, variant: 'pick' | 'depth', pickType: number) {
renderer.clear(false);
renderer.update(camera);
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.handle.isEnabled) {
renderer.renderPick(helper.handle.scene, camera, variant, null, pickType);
}
if (helper.camera.isEnabled) {
helper.camera.update(camera);
@@ -81,18 +187,23 @@ export class PickPass {
}
render(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper) {
this.objectPickTarget.bind();
this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Object);
if (this.webgl.extensions.drawBuffers) {
this.framebuffer.bind();
this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.None);
} else {
this.objectPickTarget.bind();
this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Object);
this.instancePickTarget.bind();
this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Instance);
this.instancePickTarget.bind();
this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Instance);
this.groupPickTarget.bind();
this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Group);
// printTexture(this.webgl, this.groupPickTarget.texture, { id: 'group' })
this.groupPickTarget.bind();
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', PickType.None);
this.depthPickTarget.bind();
this.renderVariant(renderer, camera, scene, helper, 'depth', PickType.None);
}
}
}
@@ -147,19 +258,21 @@ export class PickHelper {
}
private syncBuffers() {
if (isTimingMode) this.webgl.timer.mark('PickHelper.syncBuffers');
const { pickX, pickY, pickWidth, pickHeight } = this;
this.pickPass.objectPickTarget.bind();
this.pickPass.bindObject();
this.webgl.readPixels(pickX, pickY, pickWidth, pickHeight, this.objectBuffer);
this.pickPass.instancePickTarget.bind();
this.pickPass.bindInstance();
this.webgl.readPixels(pickX, pickY, pickWidth, pickHeight, this.instanceBuffer);
this.pickPass.groupPickTarget.bind();
this.pickPass.bindGroup();
this.webgl.readPixels(pickX, pickY, pickWidth, pickHeight, this.groupBuffer);
this.pickPass.depthPickTarget.bind();
this.pickPass.bindDepth();
this.webgl.readPixels(pickX, pickY, pickWidth, pickHeight, this.depthBuffer);
if (isTimingMode) this.webgl.timer.markEnd('PickHelper.syncBuffers');
}
private getBufferIdx(x: number, y: number): number {
@@ -178,11 +291,12 @@ export class PickHelper {
}
private render(camera: Camera | StereoCamera) {
if (isTimingMode) this.webgl.timer.mark('PickHelper.render');
const { pickX, pickY, pickWidth, pickHeight, halfPickWidth } = this;
const { renderer, scene, helper } = this;
renderer.setTransparentBackground(false);
renderer.setDrawingBufferSize(this.pickPass.objectPickTarget.getWidth(), this.pickPass.objectPickTarget.getHeight());
renderer.setDrawingBufferSize(pickWidth, pickHeight);
renderer.setPixelRatio(this.pickScale);
if (StereoCamera.is(camera)) {
@@ -197,6 +311,7 @@ export class PickHelper {
}
this.dirty = false;
if (isTimingMode) this.webgl.timer.markEnd('PickHelper.render');
}
private identifyInternal(x: number, y: number, camera: Camera | StereoCamera): PickData | undefined {
@@ -217,8 +332,10 @@ export class PickHelper {
) return;
if (this.dirty) {
if (isTimingMode) this.webgl.timer.mark('PickHelper.identify');
this.render(camera);
this.syncBuffers();
if (isTimingMode) this.webgl.timer.markEnd('PickHelper.identify');
}
const xv = x - viewport.x;
@@ -240,6 +357,7 @@ export class PickHelper {
if (groupId === -1 || groupId === NullId) return;
const z = this.getDepth(xp, yp);
// console.log('z', z);
const position = Vec3.create(x, viewport.height - y, z);
if (StereoCamera.is(camera)) {
const halfWidth = Math.floor(viewport.width / 2);
@@ -254,7 +372,7 @@ export class PickHelper {
cameraUnproject(position, position, viewport, camera.inverseProjectionView);
}
// console.log({ { objectId, instanceId, groupId }, position} );
// console.log({ id: { objectId, instanceId, groupId }, position });
return { id: { objectId, instanceId, groupId }, position };
}

View File

@@ -27,10 +27,14 @@ import { Framebuffer } from '../../mol-gl/webgl/framebuffer';
import { Color } from '../../mol-util/color';
import { FxaaParams, FxaaPass } from './fxaa';
import { SmaaParams, SmaaPass } from './smaa';
import { isTimingMode } from '../../mol-util/debug';
import { BackgroundParams, BackgroundPass } from './background';
import { AssetManager } from '../../mol-util/assets';
const OutlinesSchema = {
...QuadSchema,
tDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tDepthOpaque: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tDepthTransparent: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
uTexSize: UniformSpec('v2'),
dOrthographic: DefineSpec('number'),
@@ -41,11 +45,15 @@ const OutlinesSchema = {
};
type OutlinesRenderable = ComputeRenderable<Values<typeof OutlinesSchema>>
function getOutlinesRenderable(ctx: WebGLContext, depthTexture: Texture): OutlinesRenderable {
function getOutlinesRenderable(ctx: WebGLContext, depthTextureOpaque: Texture, depthTextureTransparent: Texture): OutlinesRenderable {
const width = depthTextureOpaque.getWidth();
const height = depthTextureOpaque.getHeight();
const values: Values<typeof OutlinesSchema> = {
...QuadValues,
tDepth: ValueCell.create(depthTexture),
uTexSize: ValueCell.create(Vec2.create(depthTexture.getWidth(), depthTexture.getHeight())),
tDepthOpaque: ValueCell.create(depthTextureOpaque),
tDepthTransparent: ValueCell.create(depthTextureTransparent),
uTexSize: ValueCell.create(Vec2.create(width, height)),
dOrthographic: ValueCell.create(0),
uNear: ValueCell.create(1),
@@ -85,7 +93,7 @@ function getSsaoRenderable(ctx: WebGLContext, depthTexture: Texture): SsaoRender
...QuadValues,
tDepth: ValueCell.create(depthTexture),
uSamples: ValueCell.create([0.0, 0.0, 1.0]),
uSamples: ValueCell.create(getSamples(32)),
dNSamples: ValueCell.create(32),
uProjection: ValueCell.create(Mat4.identity()),
@@ -132,7 +140,7 @@ function getSsaoBlurRenderable(ctx: WebGLContext, ssaoDepthTexture: Texture, dir
tSsaoDepth: ValueCell.create(ssaoDepthTexture),
uTexSize: ValueCell.create(Vec2.create(ssaoDepthTexture.getWidth(), ssaoDepthTexture.getHeight())),
uKernel: ValueCell.create([0.0]),
uKernel: ValueCell.create(getBlurKernel(15)),
dOcclusionKernelSize: ValueCell.create(15),
uBlurDirectionX: ValueCell.create(direction === 'horizontal' ? 1 : 0),
@@ -165,15 +173,26 @@ function getBlurKernel(kernelSize: number): number[] {
return kernel;
}
function getSamples(vectorSamples: Vec3[], nSamples: number): number[] {
const RandomHemisphereVector: Vec3[] = [];
for (let i = 0; i < 256; i++) {
const v = Vec3();
v[0] = Math.random() * 2.0 - 1.0;
v[1] = Math.random() * 2.0 - 1.0;
v[2] = Math.random();
Vec3.normalize(v, v);
Vec3.scale(v, v, Math.random());
RandomHemisphereVector.push(v);
}
function getSamples(nSamples: number): number[] {
const samples = [];
for (let i = 0; i < nSamples; i++) {
let scale = (i * i + 2.0 * i + 1) / (nSamples * nSamples);
scale = 0.1 + scale * (1.0 - 0.1);
samples.push(vectorSamples[i][0] * scale);
samples.push(vectorSamples[i][1] * scale);
samples.push(vectorSamples[i][2] * scale);
samples.push(RandomHemisphereVector[i][0] * scale);
samples.push(RandomHemisphereVector[i][1] * scale);
samples.push(RandomHemisphereVector[i][2] * scale);
}
return samples;
@@ -183,7 +202,8 @@ const PostprocessingSchema = {
...QuadSchema,
tSsaoDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tDepthOpaque: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tDepthTransparent: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tOutlines: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
uTexSize: UniformSpec('v2'),
@@ -207,12 +227,13 @@ const PostprocessingSchema = {
};
type PostprocessingRenderable = ComputeRenderable<Values<typeof PostprocessingSchema>>
function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, depthTexture: Texture, outlinesTexture: Texture, ssaoDepthTexture: Texture): PostprocessingRenderable {
function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, depthTextureOpaque: Texture, depthTextureTransparent: Texture, outlinesTexture: Texture, ssaoDepthTexture: Texture): PostprocessingRenderable {
const values: Values<typeof PostprocessingSchema> = {
...QuadValues,
tSsaoDepth: ValueCell.create(ssaoDepthTexture),
tColor: ValueCell.create(colorTexture),
tDepth: ValueCell.create(depthTexture),
tDepthOpaque: ValueCell.create(depthTextureOpaque),
tDepthTransparent: ValueCell.create(depthTextureTransparent),
tOutlines: ValueCell.create(outlinesTexture),
uTexSize: ValueCell.create(Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())),
@@ -266,12 +287,17 @@ export const PostprocessingParams = {
smaa: PD.Group(SmaaParams),
off: PD.Group({})
}, { options: [['fxaa', 'FXAA'], ['smaa', 'SMAA'], ['off', 'Off']], description: 'Smooth pixel edges' }),
background: PD.Group(BackgroundParams, { isFlat: true }),
};
export type PostprocessingProps = PD.Values<typeof PostprocessingParams>
export class PostprocessingPass {
static isEnabled(props: PostprocessingProps) {
return props.occlusion.name === 'on' || props.outline.name === 'on';
return props.occlusion.name === 'on' || props.outline.name === 'on' || props.background.variant.name !== 'off';
}
static isOutlineEnabled(props: PostprocessingProps) {
return props.outline.name === 'on';
}
readonly target: RenderTarget;
@@ -279,7 +305,6 @@ export class PostprocessingPass {
private readonly outlinesTarget: RenderTarget;
private readonly outlinesRenderable: OutlinesRenderable;
private readonly randomHemisphereVector: Vec3[];
private readonly ssaoFramebuffer: Framebuffer;
private readonly ssaoBlurFirstPassFramebuffer: Framebuffer;
private readonly ssaoBlurSecondPassFramebuffer: Framebuffer;
@@ -306,8 +331,11 @@ export class PostprocessingPass {
return Math.min(1, 1 / this.webgl.pixelRatio) * this.downsampleFactor;
}
constructor(private webgl: WebGLContext, private drawPass: DrawPass) {
const { colorTarget, depthTexture } = drawPass;
private readonly bgColor = Vec3();
readonly background: BackgroundPass;
constructor(private readonly webgl: WebGLContext, assetManager: AssetManager, private readonly drawPass: DrawPass) {
const { colorTarget, depthTextureTransparent, depthTextureOpaque } = drawPass;
const width = colorTarget.getWidth();
const height = colorTarget.getHeight();
@@ -320,18 +348,8 @@ export class PostprocessingPass {
this.target = webgl.createRenderTarget(width, height, false, 'uint8', 'linear');
this.outlinesTarget = webgl.createRenderTarget(width, height, false);
this.outlinesRenderable = getOutlinesRenderable(webgl, depthTexture);
this.outlinesRenderable = getOutlinesRenderable(webgl, depthTextureOpaque, depthTextureTransparent);
this.randomHemisphereVector = [];
for (let i = 0; i < 256; i++) {
const v = Vec3();
v[0] = Math.random() * 2.0 - 1.0;
v[1] = Math.random() * 2.0 - 1.0;
v[2] = Math.random();
Vec3.normalize(v, v);
Vec3.scale(v, v, Math.random());
this.randomHemisphereVector.push(v);
}
this.ssaoFramebuffer = webgl.resources.framebuffer();
this.ssaoBlurFirstPassFramebuffer = webgl.resources.framebuffer();
this.ssaoBlurSecondPassFramebuffer = webgl.resources.framebuffer();
@@ -340,7 +358,7 @@ export class PostprocessingPass {
const sh = Math.floor(height * this.ssaoScale);
this.downsampledDepthTarget = webgl.createRenderTarget(sw, sh, false, 'uint8', 'linear');
this.downsampleDepthRenderable = createCopyRenderable(webgl, depthTexture);
this.downsampleDepthRenderable = createCopyRenderable(webgl, depthTextureOpaque);
this.ssaoDepthTexture = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
this.ssaoDepthTexture.define(sw, sh);
@@ -352,10 +370,12 @@ export class PostprocessingPass {
this.ssaoDepthTexture.attachFramebuffer(this.ssaoBlurSecondPassFramebuffer, 'color0');
this.ssaoRenderable = getSsaoRenderable(webgl, this.downsampleFactor === 1 ? depthTexture : this.downsampledDepthTarget.texture);
this.ssaoRenderable = getSsaoRenderable(webgl, this.downsampleFactor === 1 ? depthTextureOpaque : this.downsampledDepthTarget.texture);
this.ssaoBlurFirstPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthTexture, 'horizontal');
this.ssaoBlurSecondPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthBlurProxyTexture, 'vertical');
this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTexture, this.outlinesTarget.texture, this.ssaoDepthTexture);
this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTextureOpaque, depthTextureTransparent, this.outlinesTarget.texture, this.ssaoDepthTexture);
this.background = new BackgroundPass(webgl, assetManager, width, height);
}
setSize(width: number, height: number) {
@@ -379,6 +399,8 @@ export class PostprocessingPass {
ValueCell.update(this.ssaoRenderable.values.uTexSize, Vec2.set(this.ssaoRenderable.values.uTexSize.ref.value, sw, sh));
ValueCell.update(this.ssaoBlurFirstPassRenderable.values.uTexSize, Vec2.set(this.ssaoBlurFirstPassRenderable.values.uTexSize.ref.value, sw, sh));
ValueCell.update(this.ssaoBlurSecondPassRenderable.values.uTexSize, Vec2.set(this.ssaoBlurSecondPassRenderable.values.uTexSize.ref.value, sw, sh));
this.background.setSize(width, height);
}
}
@@ -428,7 +450,7 @@ export class PostprocessingPass {
needsUpdateSsao = true;
this.nSamples = props.occlusion.params.samples;
ValueCell.update(this.ssaoRenderable.values.uSamples, getSamples(this.randomHemisphereVector, this.nSamples));
ValueCell.update(this.ssaoRenderable.values.uSamples, getSamples(this.nSamples));
ValueCell.updateIfChanged(this.ssaoRenderable.values.dNSamples, this.nSamples);
}
ValueCell.updateIfChanged(this.ssaoRenderable.values.uRadius, Math.pow(2, props.occlusion.params.radius));
@@ -460,7 +482,7 @@ export class PostprocessingPass {
this.ssaoDepthBlurProxyTexture.define(sw, sh);
if (this.ssaoScale === 1) {
ValueCell.update(this.ssaoRenderable.values.tDepth, this.drawPass.depthTexture);
ValueCell.update(this.ssaoRenderable.values.tDepth, this.drawPass.depthTextureTransparent);
} else {
ValueCell.update(this.ssaoRenderable.values.tDepth, this.downsampledDepthTarget.texture);
}
@@ -526,8 +548,8 @@ export class PostprocessingPass {
state.depthMask(false);
const { x, y, width, height } = camera.viewport;
gl.viewport(x, y, width, height);
gl.scissor(x, y, width, height);
state.viewport(x, y, width, height);
state.scissor(x, y, width, height);
}
private occlusionOffset: [x: number, y: number] = [0, 0];
@@ -537,7 +559,13 @@ export class PostprocessingPass {
ValueCell.update(this.renderable.values.uOcclusionOffset, Vec2.set(this.renderable.values.uOcclusionOffset.ref.value, x, y));
}
private transparentBackground = false;
setTransparentBackground(value: boolean) {
this.transparentBackground = value;
}
render(camera: ICamera, toDrawingBuffer: boolean, transparentBackground: boolean, backgroundColor: Color, props: PostprocessingProps) {
if (isTimingMode) this.webgl.timer.mark('PostprocessingPass.render');
this.updateState(camera, transparentBackground, backgroundColor, props);
if (props.outline.name === 'on') {
@@ -570,10 +598,26 @@ export class PostprocessingPass {
}
const { gl, state } = this.webgl;
state.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
this.background.update(camera, props.background);
if (this.background.isEnabled(props.background)) {
if (this.transparentBackground) {
state.clearColor(0, 0, 0, 0);
} else {
Color.toVec3Normalized(this.bgColor, backgroundColor);
state.clearColor(this.bgColor[0], this.bgColor[1], this.bgColor[2], 1);
}
gl.clear(gl.COLOR_BUFFER_BIT);
state.enable(gl.BLEND);
state.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
this.background.render();
} else {
state.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
}
this.renderable.render();
if (isTimingMode) this.webgl.timer.markEnd('PostprocessingPass.render');
}
}

View File

@@ -1,5 +1,5 @@
/**
* 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -11,7 +11,7 @@ import { ShaderCode } from '../../mol-gl/shader-code';
import { WebGLContext } from '../../mol-gl/webgl/context';
import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
import { RenderTarget } from '../../mol-gl/webgl/render-target';
import { createTexture, loadImageTexture, Texture } from '../../mol-gl/webgl/texture';
import { loadImageTexture, Texture } from '../../mol-gl/webgl/texture';
import { Vec2, Vec4 } from '../../mol-math/linear-algebra';
import { ValueCell } from '../../mol-util';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
@@ -22,7 +22,7 @@ import { weights_frag } from '../../mol-gl/shader/smaa/weights.frag';
import { edges_vert } from '../../mol-gl/shader/smaa/edges.vert';
import { edges_frag } from '../../mol-gl/shader/smaa/edges.frag';
import { Viewport } from '../camera/util';
import { isDebugMode } from '../../mol-util/debug';
import { isDebugMode, isTimingMode } from '../../mol-util/debug';
export const SmaaParams = {
edgeThreshold: PD.Numeric(0.1, { min: 0.05, max: 0.15, step: 0.01 }),
@@ -71,9 +71,10 @@ export class SmaaPass {
state.depthMask(false);
const { x, y, width, height } = viewport;
gl.viewport(x, y, width, height);
gl.scissor(x, y, width, height);
state.viewport(x, y, width, height);
state.scissor(x, y, width, height);
state.colorMask(true, true, true, true);
state.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
@@ -120,6 +121,7 @@ export class SmaaPass {
}
render(viewport: Viewport, target: RenderTarget | undefined) {
if (isTimingMode) this.webgl.timer.mark('SmaaPass.render');
this.edgesTarget.bind();
this.updateState(viewport);
this.edgesRenderable.render();
@@ -135,8 +137,8 @@ export class SmaaPass {
}
this.updateState(viewport);
this.blendRenderable.render();
if (isTimingMode) this.webgl.timer.markEnd('SmaaPass.render');
}
}
//
@@ -190,8 +192,8 @@ function getWeightsRenderable(ctx: WebGLContext, edgesTexture: Texture): Weights
const width = edgesTexture.getWidth();
const height = edgesTexture.getHeight();
const areaTexture = createTexture(ctx.gl, ctx.extensions, 'image-uint8', 'rgb', 'ubyte', 'linear');
const searchTexture = createTexture(ctx.gl, ctx.extensions, 'image-uint8', 'rgba', 'ubyte', 'nearest');
const areaTexture = ctx.resources.texture('image-uint8', 'rgb', 'ubyte', 'linear');
const searchTexture = ctx.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
const values: Values<typeof WeightsSchema> = {
...QuadValues,

View File

@@ -17,7 +17,9 @@ import { quad_vert } from '../../mol-gl/shader/quad.vert';
import { evaluateWboit_frag } from '../../mol-gl/shader/evaluate-wboit.frag';
import { Framebuffer } from '../../mol-gl/webgl/framebuffer';
import { Vec2 } from '../../mol-math/linear-algebra';
import { isDebugMode } from '../../mol-util/debug';
import { isDebugMode, isTimingMode } from '../../mol-util/debug';
import { isWebGL2 } from '../../mol-gl/webgl/compat';
import { Renderbuffer } from '../../mol-gl/webgl/renderbuffer';
const EvaluateWboitSchema = {
...QuadSchema,
@@ -50,6 +52,7 @@ export class WboitPass {
private readonly framebuffer: Framebuffer;
private readonly textureA: Texture;
private readonly textureB: Texture;
private readonly depthRenderbuffer: Renderbuffer;
private _supported = false;
get supported() {
@@ -71,6 +74,7 @@ export class WboitPass {
}
render() {
if (isTimingMode) this.webgl.timer.mark('WboitPass.render');
const { state, gl } = this.webgl;
state.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
@@ -78,6 +82,7 @@ export class WboitPass {
this.renderable.update();
this.renderable.render();
if (isTimingMode) this.webgl.timer.markEnd('WboitPass.render');
}
setSize(width: number, height: number) {
@@ -85,6 +90,7 @@ export class WboitPass {
if (width !== w || height !== h) {
this.textureA.define(width, height);
this.textureB.define(width, height);
this.depthRenderbuffer.setSize(width, height);
ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height));
}
}
@@ -104,6 +110,8 @@ export class WboitPass {
this.textureA.attachFramebuffer(this.framebuffer, 'color0');
this.textureB.attachFramebuffer(this.framebuffer, 'color1');
this.depthRenderbuffer.attachFramebuffer(this.framebuffer);
}
static isSupported(webgl: WebGLContext) {
@@ -126,7 +134,7 @@ export class WboitPass {
constructor(private webgl: WebGLContext, width: number, height: number) {
if (!WboitPass.isSupported(webgl)) return;
const { resources } = webgl;
const { resources, gl } = webgl;
this.textureA = resources.texture('image-float32', 'rgba', 'float', 'nearest');
this.textureA.define(width, height);
@@ -134,6 +142,10 @@ export class WboitPass {
this.textureB = resources.texture('image-float32', 'rgba', 'float', 'nearest');
this.textureB.define(width, height);
this.depthRenderbuffer = isWebGL2(gl)
? resources.renderbuffer('depth32f', 'depth', width, height)
: resources.renderbuffer('depth16', 'depth', width, height);
this.renderable = getEvaluateWboitRenderable(webgl, this.textureA, this.textureB);
this.framebuffer = resources.framebuffer();

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>
*/
@@ -83,6 +83,7 @@ export namespace BaseGeometry {
quality: PD.Select<VisualQuality>('auto', VisualQualityOptions, { isEssential: true, description: 'Visual/rendering quality of the representation.' }),
material: Material.getParam(),
clip: PD.Group(Clip.Params),
instanceGranularity: PD.Boolean(false, { description: 'Use instance granularity for marker, transparency, clipping, overpaint, substance data to save memory.' }),
};
export type Params = typeof Params
@@ -110,6 +111,7 @@ export namespace BaseGeometry {
uRoughness: ValueCell.create(props.material.roughness),
uBumpiness: ValueCell.create(props.material.bumpiness),
dLightCount: ValueCell.create(1),
dColorMarker: ValueCell.create(true),
dClipObjectCount: ValueCell.create(clip.objects.count),
dClipVariant: ValueCell.create(clip.variant),
@@ -118,6 +120,8 @@ export namespace BaseGeometry {
uClipObjectPosition: ValueCell.create(clip.objects.position),
uClipObjectRotation: ValueCell.create(clip.objects.rotation),
uClipObjectScale: ValueCell.create(clip.objects.scale),
instanceGranularity: ValueCell.create(props.instanceGranularity),
};
}
@@ -135,6 +139,8 @@ export namespace BaseGeometry {
ValueCell.update(values.uClipObjectPosition, clip.objects.position);
ValueCell.update(values.uClipObjectRotation, clip.objects.rotation);
ValueCell.update(values.uClipObjectScale, clip.objects.scale);
ValueCell.updateIfChanged(values.instanceGranularity, props.instanceGranularity);
}
export function createRenderableState(props: Partial<PD.Values<Params>> = {}): RenderableState {

View File

@@ -9,10 +9,13 @@ import { Vec2 } from '../../mol-math/linear-algebra';
import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
import { Clipping } from '../../mol-theme/clipping';
export type ClippingType = 'instance' | 'groupInstance';
export type ClippingData = {
tClipping: ValueCell<TextureImage<Uint8Array>>
uClippingTexDim: ValueCell<Vec2>
dClipping: ValueCell<boolean>,
dClipping: ValueCell<boolean>
dClippingType: ValueCell<string>
}
export function applyClippingGroups(array: Uint8Array, start: number, end: number, groups: Clipping.Groups) {
@@ -24,18 +27,20 @@ export function clearClipping(array: Uint8Array, start: number, end: number) {
array.fill(0, start, end);
}
export function createClipping(count: number, clippingData?: ClippingData): ClippingData {
export function createClipping(count: number, type: ClippingType, clippingData?: ClippingData): ClippingData {
const clipping = createTextureImage(Math.max(1, count), 1, Uint8Array, clippingData && clippingData.tClipping.ref.value.array);
if (clippingData) {
ValueCell.update(clippingData.tClipping, clipping);
ValueCell.update(clippingData.uClippingTexDim, Vec2.create(clipping.width, clipping.height));
ValueCell.updateIfChanged(clippingData.dClipping, count > 0);
ValueCell.updateIfChanged(clippingData.dClippingType, type);
return clippingData;
} else {
return {
tClipping: ValueCell.create(clipping),
uClippingTexDim: ValueCell.create(Vec2.create(clipping.width, clipping.height)),
dClipping: ValueCell.create(count > 0),
dClippingType: ValueCell.create(type),
};
}
}
@@ -52,6 +57,7 @@ export function createEmptyClipping(clippingData?: ClippingData): ClippingData {
tClipping: ValueCell.create(emptyClippingTexture),
uClippingTexDim: ValueCell.create(Vec2.create(1, 1)),
dClipping: ValueCell.create(false),
dClippingType: ValueCell.create('groupInstance'),
};
}
}

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>
@@ -11,11 +11,13 @@ import { Color } from '../../mol-util/color';
import { Vec2, Vec3, Vec4 } from '../../mol-math/linear-algebra';
import { LocationIterator } from '../util/location-iterator';
import { NullLocation } from '../../mol-model/location';
import { LocationColor, ColorTheme } from '../../mol-theme/color';
import { Geometry } from './geometry';
import { LocationColor, ColorTheme, ColorVolume } from '../../mol-theme/color';
import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
export type ColorType = 'uniform' | 'instance' | 'group' | 'groupInstance' | 'vertex' | 'vertexInstance' | 'volume' | 'volumeInstance' | 'direct'
export type ColorTypeLocation = 'uniform' | 'instance' | 'group' | 'groupInstance' | 'vertex' | 'vertexInstance';
export type ColorTypeGrid = 'volume' | 'volumeInstance';
export type ColorTypeDirect = 'direct';
export type ColorType = ColorTypeLocation | ColorTypeGrid | ColorTypeDirect;
export type ColorData = {
uColor: ValueCell<Vec3>,
@@ -29,7 +31,7 @@ export type ColorData = {
dUsePalette: ValueCell<boolean>,
}
export function createColors(locationIt: LocationIterator, positionIt: LocationIterator, colorTheme: ColorTheme<any>, colorData?: ColorData): ColorData {
export function createColors(locationIt: LocationIterator, positionIt: LocationIterator, colorTheme: ColorTheme<any, any>, colorData?: ColorData): ColorData {
const data = _createColors(locationIt, positionIt, colorTheme, colorData);
if (colorTheme.palette) {
ValueCell.updateIfChanged(data.dUsePalette, true);
@@ -40,16 +42,19 @@ export function createColors(locationIt: LocationIterator, positionIt: LocationI
return data;
}
function _createColors(locationIt: LocationIterator, positionIt: LocationIterator, colorTheme: ColorTheme<any>, colorData?: ColorData): ColorData {
switch (Geometry.getGranularity(locationIt, colorTheme.granularity)) {
function _createColors(locationIt: LocationIterator, positionIt: LocationIterator, colorTheme: ColorTheme<any, any>, colorData?: ColorData): ColorData {
switch (colorTheme.granularity) {
case 'uniform': return createUniformColor(locationIt, colorTheme.color, colorData);
case 'instance': return createInstanceColor(locationIt, colorTheme.color, colorData);
case 'instance':
return locationIt.nonInstanceable
? createGroupColor(locationIt, colorTheme.color, colorData)
: createInstanceColor(locationIt, colorTheme.color, colorData);
case 'group': return createGroupColor(locationIt, colorTheme.color, colorData);
case 'groupInstance': return createGroupInstanceColor(locationIt, colorTheme.color, colorData);
case 'vertex': return createVertexColor(positionIt, colorTheme.color, colorData);
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 'volume': return createGridColor(colorTheme.grid, 'volume', colorData);
case 'volumeInstance': return createGridColor(colorTheme.grid, 'volumeInstance', colorData);
case 'direct': return createDirectColor(colorData);
}
}
@@ -207,12 +212,6 @@ function createVertexInstanceColor(locationIt: LocationIterator, color: Location
//
interface ColorVolume {
colors: Texture
dimension: Vec3
transform: Vec4
}
export function createGridColor(grid: ColorVolume, type: ColorType, colorData?: ColorData): ColorData {
const { colors, dimension, transform } = grid;
const width = colors.getWidth();

View File

@@ -157,7 +157,7 @@ export namespace Cylinders {
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
allowTransparentBackfaces: PD.Boolean(false, BaseGeometry.ShadingCategory),
transparentBackfaces: PD.Select('off', PD.arrayToOptions(['off', 'on', 'opaque']), BaseGeometry.ShadingCategory),
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
bumpAmplitude: PD.Numeric(1, { min: 0, max: 5, step: 0.1 }, BaseGeometry.ShadingCategory),
};
@@ -201,7 +201,9 @@ export namespace Cylinders {
const color = createColors(locationIt, positionIt, theme.color);
const size = createSizes(locationIt, theme.size);
const marker = createMarkers(instanceCount * groupCount);
const marker = props.instanceGranularity
? createMarkers(instanceCount, 'instance')
: createMarkers(instanceCount * groupCount, 'groupInstance');
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const material = createEmptySubstance();
@@ -242,6 +244,7 @@ export namespace Cylinders {
uDoubleSided: ValueCell.create(props.doubleSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
dTransparentBackfaces: ValueCell.create(props.transparentBackfaces),
uBumpFrequency: ValueCell.create(props.bumpFrequency),
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
};
@@ -259,6 +262,7 @@ export namespace Cylinders {
ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces);
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
}

View File

@@ -211,7 +211,9 @@ export namespace DirectVolume {
const positionIt = Utils.createPositionIterator(directVolume, transform);
const color = createColors(locationIt, positionIt, theme.color);
const marker = createMarkers(instanceCount * groupCount);
const marker = props.instanceGranularity
? createMarkers(instanceCount, 'instance')
: createMarkers(instanceCount * groupCount, 'groupInstance');
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const material = createEmptySubstance();

View File

@@ -143,7 +143,9 @@ namespace Image {
const positionIt = Utils.createPositionIterator(image, transform);
const color = createColors(locationIt, positionIt, theme.color);
const marker = createMarkers(instanceCount * groupCount);
const marker = props.instanceGranularity
? createMarkers(instanceCount, 'instance')
: createMarkers(instanceCount * groupCount, 'groupInstance');
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const material = createEmptySubstance();

View File

@@ -208,7 +208,9 @@ export namespace Lines {
const color = createColors(locationIt, positionIt, theme.color);
const size = createSizes(locationIt, theme.size);
const marker = createMarkers(instanceCount * groupCount);
const marker = props.instanceGranularity
? createMarkers(instanceCount, 'instance')
: createMarkers(instanceCount * groupCount, 'groupInstance');
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const material = createEmptySubstance();

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,12 +8,15 @@ import { ValueCell } from '../../mol-util/value-cell';
import { Vec2 } from '../../mol-math/linear-algebra';
import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
export type MarkerType = 'instance' | 'groupInstance';
export type MarkerData = {
uMarker: ValueCell<number>,
uMarker: ValueCell<number>
tMarker: ValueCell<TextureImage<Uint8Array>>
uMarkerTexDim: ValueCell<Vec2>
markerAverage: ValueCell<number>
markerStatus: ValueCell<number>
dMarkerType: ValueCell<string>
}
const MarkerCountLut = new Uint8Array(0x0303 + 1);
@@ -64,7 +67,7 @@ export function getMarkersAverage(array: Uint8Array, count: number): number {
return sum / count;
}
export function createMarkers(count: number, markerData?: MarkerData): MarkerData {
export function createMarkers(count: number, type: MarkerType, markerData?: MarkerData): MarkerData {
const markers = createTextureImage(Math.max(1, count), 1, Uint8Array, markerData && markerData.tMarker.ref.value.array);
const average = getMarkersAverage(markers.array, count);
const status = average === 0 ? 0 : -1;
@@ -74,6 +77,7 @@ export function createMarkers(count: number, markerData?: MarkerData): MarkerDat
ValueCell.update(markerData.uMarkerTexDim, Vec2.create(markers.width, markers.height));
ValueCell.updateIfChanged(markerData.markerAverage, average);
ValueCell.updateIfChanged(markerData.markerStatus, status);
ValueCell.updateIfChanged(markerData.dMarkerType, type);
return markerData;
} else {
return {
@@ -82,6 +86,7 @@ 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(type),
};
}
}
@@ -102,6 +107,7 @@ export function createEmptyMarkers(markerData?: MarkerData): MarkerData {
uMarkerTexDim: ValueCell.create(Vec2.create(1, 1)),
markerAverage: ValueCell.create(0),
markerStatus: ValueCell.create(0),
dMarkerType: ValueCell.create('groupInstance'),
};
}
}

View File

@@ -45,6 +45,8 @@ export interface Mesh {
readonly normalBuffer: ValueCell<Float32Array>,
/** Group buffer as array of group ids for each vertex wrapped in a value cell */
readonly groupBuffer: ValueCell<Float32Array>,
/** Indicates that group may vary within a triangle, wrapped in a value cell */
readonly varyingGroup: ValueCell<boolean>,
/** Bounding sphere of the mesh */
readonly boundingSphere: Sphere3D
@@ -95,6 +97,7 @@ export namespace Mesh {
indexBuffer: ValueCell.create(indices),
normalBuffer: ValueCell.create(normals),
groupBuffer: ValueCell.create(groups),
varyingGroup: ValueCell.create(false),
get boundingSphere() {
const newHash = hashCode(mesh);
if (newHash !== currentHash) {
@@ -625,7 +628,7 @@ export namespace Mesh {
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
allowTransparentBackfaces: PD.Boolean(false, BaseGeometry.ShadingCategory),
transparentBackfaces: PD.Select('off', PD.arrayToOptions(['off', 'on', 'opaque']), BaseGeometry.ShadingCategory),
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
bumpAmplitude: PD.Numeric(1, { min: 0, max: 5, step: 0.1 }, BaseGeometry.ShadingCategory),
};
@@ -666,7 +669,9 @@ export namespace Mesh {
const positionIt = createPositionIterator(mesh, transform);
const color = createColors(locationIt, positionIt, theme.color);
const marker = createMarkers(instanceCount * groupCount);
const marker = props.instanceGranularity
? createMarkers(instanceCount, 'instance')
: createMarkers(instanceCount * groupCount, 'groupInstance');
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const material = createEmptySubstance();
@@ -684,6 +689,7 @@ export namespace Mesh {
aNormal: mesh.normalBuffer,
aGroup: mesh.groupBuffer,
elements: mesh.indexBuffer,
dVaryingGroup: mesh.varyingGroup,
boundingSphere: ValueCell.create(boundingSphere),
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
@@ -701,6 +707,7 @@ export namespace Mesh {
dFlipSided: ValueCell.create(props.flipSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
dTransparentBackfaces: ValueCell.create(props.transparentBackfaces),
uBumpFrequency: ValueCell.create(props.bumpFrequency),
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
@@ -721,6 +728,7 @@ export namespace Mesh {
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces);
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
}

View File

@@ -10,6 +10,8 @@ import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
import { Color } from '../../mol-util/color';
import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
export type OverpaintType = 'instance' | 'groupInstance' | 'volumeInstance';
export type OverpaintData = {
tOverpaint: ValueCell<TextureImage<Uint8Array>>
uOverpaintTexDim: ValueCell<Vec2>
@@ -34,12 +36,13 @@ export function clearOverpaint(array: Uint8Array, start: number, end: number) {
return true;
}
export function createOverpaint(count: number, overpaintData?: OverpaintData): OverpaintData {
export function createOverpaint(count: number, type: OverpaintType, overpaintData?: OverpaintData): OverpaintData {
const overpaint = createTextureImage(Math.max(1, count), 4, Uint8Array, overpaintData && overpaintData.tOverpaint.ref.value.array);
if (overpaintData) {
ValueCell.update(overpaintData.tOverpaint, overpaint);
ValueCell.update(overpaintData.uOverpaintTexDim, Vec2.create(overpaint.width, overpaint.height));
ValueCell.updateIfChanged(overpaintData.dOverpaint, count > 0);
ValueCell.updateIfChanged(overpaintData.dOverpaintType, type);
return overpaintData;
} else {
return {
@@ -50,7 +53,7 @@ export function createOverpaint(count: number, overpaintData?: OverpaintData): O
tOverpaintGrid: ValueCell.create(createNullTexture()),
uOverpaintGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
uOverpaintGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
dOverpaintType: ValueCell.create('groupInstance'),
dOverpaintType: ValueCell.create(type),
};
}
}

View File

@@ -170,7 +170,9 @@ export namespace Points {
const color = createColors(locationIt, positionIt, theme.color);
const size = createSizes(locationIt, theme.size);
const marker = createMarkers(instanceCount * groupCount);
const marker = props.instanceGranularity
? createMarkers(instanceCount, 'instance')
: createMarkers(instanceCount * groupCount, 'groupInstance');
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const material = createEmptySubstance();

View File

@@ -129,7 +129,7 @@ export namespace Spheres {
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
allowTransparentBackfaces: PD.Boolean(false, BaseGeometry.ShadingCategory),
transparentBackfaces: PD.Select('off', PD.arrayToOptions(['off', 'on', 'opaque']), BaseGeometry.ShadingCategory),
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
bumpAmplitude: PD.Numeric(1, { min: 0, max: 5, step: 0.1 }, BaseGeometry.ShadingCategory),
};
@@ -171,7 +171,9 @@ export namespace Spheres {
const color = createColors(locationIt, positionIt, theme.color);
const size = createSizes(locationIt, theme.size);
const marker = createMarkers(instanceCount * groupCount);
const marker = props.instanceGranularity
? createMarkers(instanceCount, 'instance')
: createMarkers(instanceCount * groupCount, 'groupInstance');
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const material = createEmptySubstance();
@@ -209,6 +211,7 @@ export namespace Spheres {
uDoubleSided: ValueCell.create(props.doubleSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
dTransparentBackfaces: ValueCell.create(props.transparentBackfaces),
uBumpFrequency: ValueCell.create(props.bumpFrequency),
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
};
@@ -226,6 +229,7 @@ export namespace Spheres {
ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces);
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
}

View File

@@ -10,6 +10,8 @@ import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
import { Material } from '../../mol-util/material';
export type SubstanceType = 'instance' | 'groupInstance' | 'volumeInstance';
export type SubstanceData = {
tSubstance: ValueCell<TextureImage<Uint8Array>>
uSubstanceTexDim: ValueCell<Vec2>
@@ -34,12 +36,13 @@ export function clearSubstance(array: Uint8Array, start: number, end: number) {
return true;
}
export function createSubstance(count: number, substanceData?: SubstanceData): SubstanceData {
export function createSubstance(count: number, type: SubstanceType, substanceData?: SubstanceData): SubstanceData {
const substance = createTextureImage(Math.max(1, count), 4, Uint8Array, substanceData && substanceData.tSubstance.ref.value.array);
if (substanceData) {
ValueCell.update(substanceData.tSubstance, substance);
ValueCell.update(substanceData.uSubstanceTexDim, Vec2.create(substance.width, substance.height));
ValueCell.updateIfChanged(substanceData.dSubstance, count > 0);
ValueCell.updateIfChanged(substanceData.dSubstanceType, type);
return substanceData;
} else {
return {
@@ -50,7 +53,7 @@ export function createSubstance(count: number, substanceData?: SubstanceData): S
tSubstanceGrid: ValueCell.create(createNullTexture()),
uSubstanceGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
uSubstanceGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
dSubstanceType: ValueCell.create('groupInstance'),
dSubstanceType: ValueCell.create(type),
};
}
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 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>
*/
@@ -88,7 +88,7 @@ export class FontAtlas {
this.scratchCanvas.width = this.maxWidth;
this.scratchCanvas.height = this.lineHeight;
this.scratchContext = this.scratchCanvas.getContext('2d')!;
this.scratchContext = this.scratchCanvas.getContext('2d', { willReadFrequently: true })!;
this.scratchContext.font = `${p.fontStyle} ${p.fontVariant} ${p.fontWeight} ${fontSize}px ${p.fontFamily}`;
this.scratchContext.fillStyle = 'black';
this.scratchContext.textBaseline = 'middle';

View File

@@ -211,7 +211,9 @@ export namespace Text {
const color = createColors(locationIt, positionIt, theme.color);
const size = createSizes(locationIt, theme.size);
const marker = createMarkers(instanceCount * groupCount);
const marker = props.instanceGranularity
? createMarkers(instanceCount, 'instance')
: createMarkers(instanceCount * groupCount, 'groupInstance');
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const substance = createEmptySubstance();

View File

@@ -20,26 +20,25 @@ import { accumulate_frag } from '../../../mol-gl/shader/compute/color-smoothing/
import { accumulate_vert } from '../../../mol-gl/shader/compute/color-smoothing/accumulate.vert';
import { isWebGL2 } from '../../../mol-gl/webgl/compat';
import { TextureMeshValues } from '../../../mol-gl/renderable/texture-mesh';
import { isTimingMode } from '../../../mol-util/debug';
export const ColorAccumulateSchema = {
drawCount: ValueSpec('number'),
instanceCount: ValueSpec('number'),
stride: ValueSpec('number'),
uTotalCount: UniformSpec('i'),
uInstanceCount: UniformSpec('i'),
uGroupCount: UniformSpec('i'),
uGroupCount: UniformSpec('i', 'material'),
aTransform: AttributeSpec('float32', 16, 1),
aInstance: AttributeSpec('float32', 1, 1),
aSample: AttributeSpec('float32', 1, 0),
uGeoTexDim: UniformSpec('v2', 'buffered'),
tPosition: TextureSpec('texture', 'rgba', 'float', 'nearest'),
tGroup: TextureSpec('texture', 'rgba', 'float', 'nearest'),
uGeoTexDim: UniformSpec('v2', 'material'),
tPosition: TextureSpec('texture', 'rgba', 'float', 'nearest', 'material'),
tGroup: TextureSpec('texture', 'rgba', 'float', 'nearest', 'material'),
uColorTexDim: UniformSpec('v2'),
tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
uColorTexDim: UniformSpec('v2', 'material'),
tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest', 'material'),
dColorType: DefineSpec('string', ['group', 'groupInstance', 'vertex', 'vertexInstance']),
uCurrentSlice: UniformSpec('f'),
@@ -87,8 +86,6 @@ function getAccumulateRenderable(ctx: WebGLContext, input: AccumulateInput, box:
ValueCell.updateIfChanged(v.instanceCount, input.instanceCount);
ValueCell.updateIfChanged(v.stride, stride);
ValueCell.updateIfChanged(v.uTotalCount, input.vertexCount);
ValueCell.updateIfChanged(v.uInstanceCount, input.instanceCount);
ValueCell.updateIfChanged(v.uGroupCount, input.groupCount);
ValueCell.update(v.aTransform, input.transformBuffer);
@@ -125,8 +122,6 @@ function createAccumulateRenderable(ctx: WebGLContext, input: AccumulateInput, b
instanceCount: ValueCell.create(input.instanceCount),
stride: ValueCell.create(stride),
uTotalCount: ValueCell.create(input.vertexCount),
uInstanceCount: ValueCell.create(input.instanceCount),
uGroupCount: ValueCell.create(input.groupCount),
aTransform: ValueCell.create(input.transformBuffer),
@@ -255,6 +250,7 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
const { drawBuffers } = webgl.extensions;
if (!drawBuffers) throw new Error('need WebGL draw buffers');
if (isTimingMode) webgl.timer.mark('calcTextureMeshColorSmoothing');
const { gl, resources, state, extensions: { colorBufferHalfFloat, textureHalfFloat } } = webgl;
const isInstanceType = input.colorType.endsWith('Instance');
@@ -321,9 +317,10 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
const { uCurrentSlice, uCurrentX, uCurrentY } = accumulateRenderable.values;
if (isTimingMode) webgl.timer.mark('ColorAccumulate.render');
setAccumulateDefaults(webgl);
gl.viewport(0, 0, width, height);
gl.scissor(0, 0, width, height);
state.viewport(0, 0, width, height);
state.scissor(0, 0, width, height);
gl.clear(gl.COLOR_BUFFER_BIT);
ValueCell.update(uCurrentY, 0);
let currCol = 0;
@@ -339,8 +336,8 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
// console.log({ i, currX, currY });
ValueCell.update(uCurrentX, currX);
ValueCell.update(uCurrentSlice, i);
gl.viewport(currX, currY, dx, dy);
gl.scissor(currX, currY, dx, dy);
state.viewport(currX, currY, dx, dy);
state.scissor(currX, currY, dx, dy);
accumulateRenderable.render();
++currCol;
currX += dx;
@@ -349,6 +346,7 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
accumulateTexture.detachFramebuffer(framebuffer, 0);
countTexture.detachFramebuffer(framebuffer, 1);
drawBuffers.drawBuffers([gl.COLOR_ATTACHMENT0, gl.NONE]);
if (isTimingMode) webgl.timer.markEnd('ColorAccumulate.render');
// const accImage = new Float32Array(width * height * 4);
// accumulateTexture.attachFramebuffer(framebuffer, 0);
@@ -364,6 +362,7 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
// normalize
if (isTimingMode) webgl.timer.mark('ColorNormalize.render');
if (!texture) texture = resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
texture.define(width, height);
@@ -372,10 +371,11 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
setNormalizeDefaults(webgl);
texture.attachFramebuffer(framebuffer, 0);
gl.viewport(0, 0, width, height);
gl.scissor(0, 0, width, height);
state.viewport(0, 0, width, height);
state.scissor(0, 0, width, height);
gl.clear(gl.COLOR_BUFFER_BIT);
normalizeRenderable.render();
if (isTimingMode) webgl.timer.markEnd('ColorNormalize.render');
// const normImage = new Uint8Array(width * height * 4);
// texture.attachFramebuffer(framebuffer, 0);
@@ -385,6 +385,9 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
const gridTransform = Vec4.create(min[0], min[1], min[2], scaleFactor);
const type = isInstanceType ? 'volumeInstance' : 'volume';
if (isTimingMode) webgl.timer.markEnd('calcTextureMeshColorSmoothing');
// printTextureImage(readTexture(webgl, texture), { scale: 0.75 });
return { texture, gridDim, gridTexDim: Vec2.create(width, height), gridTransform, type };
}

View File

@@ -24,6 +24,7 @@ import { Vec2, Vec4 } from '../../../mol-math/linear-algebra';
import { createEmptyClipping } from '../clipping-data';
import { NullLocation } from '../../../mol-model/location';
import { createEmptySubstance } from '../substance-data';
import { RenderableState } from '../../../mol-gl/renderable';
export interface TextureMesh {
readonly kind: 'texture-mesh',
@@ -37,6 +38,7 @@ export interface TextureMesh {
readonly vertexTexture: ValueCell<Texture>,
readonly groupTexture: ValueCell<Texture>,
readonly normalTexture: ValueCell<Texture>,
readonly varyingGroup: ValueCell<boolean>,
readonly doubleBuffer: TextureMesh.DoubleBuffer
readonly boundingSphere: Sphere3D
@@ -91,6 +93,7 @@ export namespace TextureMesh {
vertexTexture: ValueCell.create(vertexTexture),
groupTexture: ValueCell.create(groupTexture),
normalTexture: ValueCell.create(normalTexture),
varyingGroup: ValueCell.create(false),
doubleBuffer: new DoubleBuffer(),
boundingSphere: Sphere3D.clone(boundingSphere),
meta: {}
@@ -113,7 +116,7 @@ export namespace TextureMesh {
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
allowTransparentBackfaces: PD.Boolean(false, BaseGeometry.ShadingCategory),
transparentBackfaces: PD.Select('off', PD.arrayToOptions(['off', 'on', 'opaque']), BaseGeometry.ShadingCategory),
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
bumpAmplitude: PD.Numeric(1, { min: 0, max: 5, step: 0.1 }, BaseGeometry.ShadingCategory),
};
@@ -126,8 +129,8 @@ export namespace TextureMesh {
createValuesSimple,
updateValues,
updateBoundingSphere,
createRenderableState: BaseGeometry.createRenderableState,
updateRenderableState: BaseGeometry.updateRenderableState,
createRenderableState,
updateRenderableState,
createPositionIterator: () => LocationIterator(1, 1, 1, () => NullLocation)
};
@@ -136,7 +139,9 @@ export namespace TextureMesh {
const positionIt = Utils.createPositionIterator(textureMesh, transform);
const color = createColors(locationIt, positionIt, theme.color);
const marker = createMarkers(instanceCount * groupCount);
const marker = props.instanceGranularity
? createMarkers(instanceCount, 'instance')
: createMarkers(instanceCount * groupCount, 'groupInstance');
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const substance = createEmptySubstance();
@@ -154,6 +159,7 @@ export namespace TextureMesh {
tPosition: textureMesh.vertexTexture,
tGroup: textureMesh.groupTexture,
tNormal: textureMesh.normalTexture,
dVaryingGroup: textureMesh.varyingGroup,
boundingSphere: ValueCell.create(boundingSphere),
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
@@ -173,6 +179,7 @@ export namespace TextureMesh {
dFlipSided: ValueCell.create(props.flipSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
dTransparentBackfaces: ValueCell.create(props.transparentBackfaces),
uBumpFrequency: ValueCell.create(props.bumpFrequency),
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
@@ -193,6 +200,7 @@ export namespace TextureMesh {
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces);
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
}
@@ -209,4 +217,16 @@ export namespace TextureMesh {
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
}
}
function createRenderableState(props: PD.Values<Params>): RenderableState {
const state = BaseGeometry.createRenderableState(props);
updateRenderableState(state, props);
return state;
}
function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
BaseGeometry.updateRenderableState(state, props);
state.opaque = state.opaque && !props.xrayShaded;
state.writeDepth = state.opaque;
}
}

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>
*/
@@ -9,6 +9,8 @@ import { Vec2, Vec3, Vec4 } from '../../mol-math/linear-algebra';
import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
export type TransparencyType = 'instance' | 'groupInstance' | 'volumeInstance';
export type TransparencyData = {
tTransparency: ValueCell<TextureImage<Uint8Array>>
uTransparencyTexDim: ValueCell<Vec2>
@@ -29,6 +31,7 @@ export function applyTransparencyValue(array: Uint8Array, start: number, end: nu
}
export function getTransparencyAverage(array: Uint8Array, count: number): number {
if (count === 0 || array.length < count) return 0;
let sum = 0;
for (let i = 0; i < count; ++i) {
sum += array[i];
@@ -40,13 +43,14 @@ export function clearTransparency(array: Uint8Array, start: number, end: number)
array.fill(0, start, end);
}
export function createTransparency(count: number, transparencyData?: TransparencyData): TransparencyData {
export function createTransparency(count: number, type: TransparencyType, transparencyData?: TransparencyData): TransparencyData {
const transparency = createTextureImage(Math.max(1, count), 1, Uint8Array, transparencyData && transparencyData.tTransparency.ref.value.array);
if (transparencyData) {
ValueCell.update(transparencyData.tTransparency, transparency);
ValueCell.update(transparencyData.uTransparencyTexDim, Vec2.create(transparency.width, transparency.height));
ValueCell.updateIfChanged(transparencyData.dTransparency, count > 0);
ValueCell.updateIfChanged(transparencyData.transparencyAverage, getTransparencyAverage(transparency.array, count));
ValueCell.updateIfChanged(transparencyData.dTransparencyType, type);
return transparencyData;
} else {
return {
@@ -58,7 +62,7 @@ export function createTransparency(count: number, transparencyData?: Transparenc
tTransparencyGrid: ValueCell.create(createNullTexture()),
uTransparencyGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
uTransparencyGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
dTransparencyType: ValueCell.create('groupInstance'),
dTransparencyType: ValueCell.create(type),
};
}
}

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(9);
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);
expect(ctx.stats.resourceCounts.vertexArray).toBe(ctx.extensions.vertexArrayObject ? 6 : 0);
expect(ctx.stats.resourceCounts.program).toBe(6);
expect(ctx.stats.resourceCounts.shader).toBe(12);
scene.remove(points);
scene.commit();
expect(ctx.stats.resourceCounts.attribute).toBe(0);
expect(ctx.stats.resourceCounts.texture).toBe(1);
expect(ctx.stats.resourceCounts.vertexArray).toBe(0);
expect(ctx.stats.resourceCounts.program).toBe(5);
expect(ctx.stats.resourceCounts.shader).toBe(10);
expect(ctx.stats.resourceCounts.program).toBe(6);
expect(ctx.stats.resourceCounts.shader).toBe(12);
ctx.resources.destroy();
expect(ctx.stats.resourceCounts.program).toBe(0);

View File

@@ -1,5 +1,5 @@
/**
* 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>
*/
@@ -18,8 +18,9 @@ import { createComputeRenderItem } from '../webgl/render-item';
import { createComputeRenderable } from '../renderable';
import { isLittleEndian } from '../../mol-util/is-little-endian';
import { RuntimeContext } from '../../mol-task';
import { isTimingMode } from '../../mol-util/debug';
export function canComputeGrid3dOnGPU(webgl?: WebGLContext) {
export function canComputeGrid3dOnGPU(webgl?: WebGLContext): webgl is WebGLContext {
return !!webgl?.extensions.textureFloat;
}
@@ -159,7 +160,8 @@ export function createGrid3dComputeRenderable<S extends RenderableSchema, P, CS>
const array = new Uint8Array(uWidth * uWidth * 4);
if (spec.cumulative) {
const { gl } = webgl;
const { gl, state } = webgl;
if (isTimingMode) webgl.timer.mark('Grid3dCompute.renderCumulative');
const states = spec.cumulative.states(params);
@@ -167,7 +169,7 @@ export function createGrid3dComputeRenderable<S extends RenderableSchema, P, CS>
tex[1].define(uWidth, uWidth);
resetGl(webgl, uWidth);
gl.clearColor(0, 0, 0, 0);
state.clearColor(0, 0, 0, 0);
tex[0].attachFramebuffer(framebuffer, 'color0');
gl.clear(gl.COLOR_BUFFER_BIT);
@@ -175,12 +177,13 @@ export function createGrid3dComputeRenderable<S extends RenderableSchema, P, CS>
tex[1].attachFramebuffer(framebuffer, 'color0');
gl.clear(gl.COLOR_BUFFER_BIT);
if (spec.cumulative.yieldPeriod) {
if (spec.cumulative.yieldPeriod && !isTimingMode) {
await ctx.update({ message: 'Computing...', isIndeterminate: false, current: 0, max: states.length });
}
const yieldPeriod = Math.max(1, spec.cumulative.yieldPeriod ?? 1 | 0);
if (isTimingMode) webgl.timer.mark('Grid3dCompute.renderBatch');
for (let i = 0; i < states.length; i++) {
ValueCell.update(cells.tCumulativeSum, tex[(i + 1) % 2]);
tex[i % 2].attachFramebuffer(framebuffer, 'color0');
@@ -191,31 +194,39 @@ export function createGrid3dComputeRenderable<S extends RenderableSchema, P, CS>
if (spec.cumulative.yieldPeriod && i !== states.length - 1) {
if (i % yieldPeriod === yieldPeriod - 1) {
webgl.readPixels(0, 0, 1, 1, array);
webgl.waitForGpuCommandsCompleteSync();
if (isTimingMode) webgl.timer.markEnd('Grid3dCompute.renderBatch');
if (isTimingMode) webgl.timer.mark('Grid3dCompute.renderBatch');
}
if (ctx.shouldUpdate) {
if (ctx.shouldUpdate && !isTimingMode) {
await ctx.update({ current: i + 1 });
}
}
}
if (isTimingMode) webgl.timer.markEnd('Grid3dCompute.renderBatch');
if (isTimingMode) webgl.timer.markEnd('Grid3dCompute.renderCumulative');
} else {
if (isTimingMode) webgl.timer.mark('Grid3dCompute.render');
tex[0].define(uWidth, uWidth);
tex[0].attachFramebuffer(framebuffer, 'color0');
framebuffer.bind();
resetGl(webgl, uWidth);
renderable.update();
renderable.render();
if (isTimingMode) webgl.timer.markEnd('Grid3dCompute.render');
}
if (isTimingMode) webgl.timer.mark('Grid3dCompute.readPixels');
webgl.readPixels(0, 0, uWidth, uWidth, array);
if (isTimingMode) webgl.timer.markEnd('Grid3dCompute.readPixels');
return new Float32Array(array.buffer, array.byteOffset, nx * ny * nz);
};
}
function resetGl(webgl: WebGLContext, w: number) {
const { gl, state } = webgl;
gl.viewport(0, 0, w, w);
gl.scissor(0, 0, w, w);
state.viewport(0, 0, w, w);
state.scissor(0, 0, w, w);
state.disable(gl.SCISSOR_TEST);
state.disable(gl.BLEND);
state.disable(gl.DEPTH_TEST);

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>
*/
@@ -19,6 +19,7 @@ import { isPowerOfTwo } from '../../../mol-math/misc';
import { quad_vert } from '../../../mol-gl/shader/quad.vert';
import { reduction_frag } from '../../../mol-gl/shader/histogram-pyramid/reduction.frag';
import { isWebGL2 } from '../../webgl/compat';
import { isTimingMode } from '../../../mol-util/debug';
const HistopyramidReductionSchema = {
...QuadSchema,
@@ -120,7 +121,8 @@ export interface HistogramPyramid {
}
export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture, scale: Vec2, gridTexDim: Vec3): HistogramPyramid {
const { gl } = ctx;
if (isTimingMode) ctx.timer.mark('createHistogramPyramid');
const { gl, state } = ctx;
const w = inputTexture.getWidth();
const h = inputTexture.getHeight();
@@ -144,7 +146,7 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture,
const framebuffer = getFramebuffer('pyramid', ctx);
pyramidTex.attachFramebuffer(framebuffer, 0);
gl.viewport(0, 0, maxSizeX, maxSizeY);
state.viewport(0, 0, maxSizeX, maxSizeY);
if (isWebGL2(gl)) {
gl.clearBufferiv(gl.COLOR, 0, [0, 0, 0, 0]);
} else {
@@ -155,7 +157,7 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture,
for (let i = 0; i < levels; ++i) levelTexturesFramebuffers.push(getLevelTextureFramebuffer(ctx, i));
const renderable = getHistopyramidReductionRenderable(ctx, inputTexture, levelTexturesFramebuffers[0].texture);
ctx.state.currentRenderItemId = -1;
state.currentRenderItemId = -1;
setRenderingDefaults(ctx);
let offset = 0;
@@ -174,15 +176,15 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture,
ValueCell.update(renderable.values.tPreviousLevel, levelTexturesFramebuffers[levels - i].texture);
renderable.update();
}
ctx.state.currentRenderItemId = -1;
gl.viewport(0, 0, size, size);
gl.scissor(0, 0, size, size);
state.currentRenderItemId = -1;
state.viewport(0, 0, size, size);
state.scissor(0, 0, size, size);
if (isWebGL2(gl)) {
gl.clearBufferiv(gl.COLOR, 0, [0, 0, 0, 0]);
} else {
gl.clear(gl.COLOR_BUFFER_BIT);
}
gl.scissor(0, 0, gridTexDim[0], gridTexDim[1]);
state.scissor(0, 0, gridTexDim[0], gridTexDim[1]);
renderable.render();
pyramidTex.bind(0);
@@ -193,8 +195,9 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture,
}
gl.finish();
if (isTimingMode) ctx.timer.markEnd('createHistogramPyramid');
// printTexture(ctx, pyramidTex, 2)
// printTextureImage(readTexture(ctx, pyramidTex), { scale: 0.75 });
//

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>
*/
@@ -16,6 +16,7 @@ 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';
import { isWebGL2 } from '../../webgl/compat';
import { isTimingMode } from '../../../mol-util/debug';
const HistopyramidSumSchema = {
...QuadSchema,
@@ -66,7 +67,8 @@ const sumBytes = new Uint8Array(4);
const sumInts = new Int32Array(4);
export function getHistopyramidSum(ctx: WebGLContext, pyramidTopTexture: Texture) {
const { gl, resources } = ctx;
if (isTimingMode) ctx.timer.mark('getHistopyramidSum');
const { gl, state, resources } = ctx;
const renderable = getHistopyramidSumRenderable(ctx, pyramidTopTexture);
ctx.state.currentRenderItemId = -1;
@@ -87,12 +89,13 @@ export function getHistopyramidSum(ctx: WebGLContext, pyramidTopTexture: Texture
setRenderingDefaults(ctx);
gl.viewport(0, 0, 1, 1);
state.viewport(0, 0, 1, 1);
renderable.render();
gl.finish();
ctx.readPixels(0, 0, 1, 1, isWebGL2(gl) ? sumInts : sumBytes);
ctx.unbindFramebuffer();
if (isTimingMode) ctx.timer.markEnd('getHistopyramidSum');
return isWebGL2(gl)
? sumInts[0]

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>
*/
@@ -16,6 +16,7 @@ import { QuadSchema, QuadValues } from '../util';
import { getTriCount } from './tables';
import { quad_vert } from '../../../mol-gl/shader/quad.vert';
import { activeVoxels_frag } from '../../../mol-gl/shader/marching-cubes/active-voxels.frag';
import { isTimingMode } from '../../../mol-util/debug';
const ActiveVoxelsSchema = {
...QuadSchema,
@@ -83,7 +84,8 @@ function setRenderingDefaults(ctx: WebGLContext) {
}
export function calcActiveVoxels(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, isoValue: number, gridScale: Vec2) {
const { gl, resources } = ctx;
if (isTimingMode) ctx.timer.mark('calcActiveVoxels');
const { gl, state, resources } = ctx;
const width = volumeData.getWidth();
const height = volumeData.getHeight();
@@ -104,17 +106,19 @@ export function calcActiveVoxels(ctx: WebGLContext, volumeData: Texture, gridDim
activeVoxelsTex.attachFramebuffer(framebuffer, 0);
setRenderingDefaults(ctx);
gl.viewport(0, 0, width, height);
gl.scissor(0, 0, width, height);
state.viewport(0, 0, width, height);
state.scissor(0, 0, width, height);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.scissor(0, 0, gridTexDim[0], gridTexDim[1]);
state.scissor(0, 0, gridTexDim[0], gridTexDim[1]);
renderable.render();
// console.log('gridScale', gridScale, 'gridTexDim', gridTexDim, 'gridDim', gridDim);
// console.log('volumeData', volumeData);
// console.log('at', readTexture(ctx, activeVoxelsTex));
// printTextureImage(readTexture(ctx, activeVoxelsTex), { scale: 0.75 });
gl.finish();
if (isTimingMode) ctx.timer.markEnd('calcActiveVoxels');
return activeVoxelsTex;
}

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>
*/
@@ -19,6 +19,7 @@ import { quad_vert } from '../../../mol-gl/shader/quad.vert';
import { isosurface_frag } from '../../../mol-gl/shader/marching-cubes/isosurface.frag';
import { calcActiveVoxels } from './active-voxels';
import { isWebGL2 } from '../../webgl/compat';
import { isTimingMode } from '../../../mol-util/debug';
const IsosurfaceSchema = {
...QuadSchema,
@@ -41,12 +42,13 @@ const IsosurfaceSchema = {
dPackedGroup: DefineSpec('boolean'),
dAxisOrder: DefineSpec('string', ['012', '021', '102', '120', '201', '210']),
dConstantGroup: DefineSpec('boolean'),
};
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, axisOrder: Vec3): 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, constantGroup: boolean): ComputeRenderable<IsosurfaceValues> {
if (ctx.namedComputeRenderables[IsosurfaceName]) {
const v = ctx.namedComputeRenderables[IsosurfaceName].values as IsosurfaceValues;
@@ -65,17 +67,18 @@ function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture
ValueCell.update(v.uGridTransform, transform);
ValueCell.update(v.uScale, scale);
ValueCell.update(v.dPackedGroup, packedGroup);
ValueCell.updateIfChanged(v.dPackedGroup, packedGroup);
ValueCell.updateIfChanged(v.dAxisOrder, axisOrder.join(''));
ValueCell.updateIfChanged(v.dConstantGroup, constantGroup);
ctx.namedComputeRenderables[IsosurfaceName].update();
} else {
ctx.namedComputeRenderables[IsosurfaceName] = createIsosurfaceRenderable(ctx, activeVoxelsPyramid, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder);
ctx.namedComputeRenderables[IsosurfaceName] = createIsosurfaceRenderable(ctx, activeVoxelsPyramid, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder, constantGroup);
}
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, axisOrder: Vec3) {
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, constantGroup: boolean) {
// console.log('uSize', Math.pow(2, levels))
const values: IsosurfaceValues = {
...QuadValues,
@@ -98,6 +101,7 @@ function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Text
dPackedGroup: ValueCell.create(packedGroup),
dAxisOrder: ValueCell.create(axisOrder.join('')),
dConstantGroup: ValueCell.create(constantGroup),
};
const schema = { ...IsosurfaceSchema };
@@ -118,11 +122,12 @@ 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, axisOrder: Vec3, 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, constantGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
const { drawBuffers } = ctx.extensions;
if (!drawBuffers) throw new Error('need WebGL draw buffers');
const { gl, resources, extensions } = ctx;
if (isTimingMode) ctx.timer.mark('createIsosurfaceBuffers');
const { gl, state, resources, extensions } = ctx;
const { pyramidTex, height, levels, scale, count } = histogramPyramid;
const width = pyramidTex.getWidth();
@@ -176,7 +181,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, axisOrder);
const renderable = getIsosurfaceRenderable(ctx, pyramidTex, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder, constantGroup);
ctx.state.currentRenderItemId = -1;
framebuffer.bind();
@@ -187,11 +192,16 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
]);
setRenderingDefaults(ctx);
gl.viewport(0, 0, width, height);
state.viewport(0, 0, width, height);
gl.clear(gl.COLOR_BUFFER_BIT);
renderable.render();
gl.finish();
if (isTimingMode) ctx.timer.markEnd('createIsosurfaceBuffers');
// printTextureImage(readTexture(ctx, vertexTexture, new Float32Array(width * height * 4)), { scale: 0.75 });
// printTextureImage(readTexture(ctx, groupTexture, new Uint8Array(width * height * 4)), { scale: 0.75 });
// printTextureImage(readTexture(ctx, normalTexture, new Float32Array(width * height * 4)), { scale: 0.75 });
return { vertexTexture, groupTexture, normalTexture, vertexCount: count };
}
@@ -207,21 +217,12 @@ 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, axisOrder: Vec3, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
// console.time('calcActiveVoxels');
export function extractIsosurface(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, gridTexScale: Vec2, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
if (isTimingMode) ctx.timer.mark('extractIsosurface');
const activeVoxelsTex = calcActiveVoxels(ctx, volumeData, gridDim, gridTexDim, isoValue, gridTexScale);
// ctx.waitForGpuCommandsCompleteSync();
// console.timeEnd('calcActiveVoxels');
// console.time('createHistogramPyramid');
const compacted = createHistogramPyramid(ctx, activeVoxelsTex, gridTexScale, gridTexDim);
// ctx.waitForGpuCommandsCompleteSync();
// console.timeEnd('createHistogramPyramid');
// console.time('createIsosurfaceBuffers');
const gv = createIsosurfaceBuffers(ctx, activeVoxelsTex, volumeData, compacted, gridDim, gridTexDim, transform, isoValue, invert, packedGroup, axisOrder, vertexTexture, groupTexture, normalTexture);
// ctx.waitForGpuCommandsCompleteSync();
// console.timeEnd('createIsosurfaceBuffers');
const gv = createIsosurfaceBuffers(ctx, activeVoxelsTex, volumeData, compacted, gridDim, gridTexDim, transform, isoValue, invert, packedGroup, axisOrder, constantGroup, vertexTexture, groupTexture, normalTexture);
if (isTimingMode) ctx.timer.markEnd('extractIsosurface');
return gv;
}

View File

@@ -75,9 +75,9 @@ export function getSharedCopyRenderable(ctx: WebGLContext, texture: Texture) {
const ReadTextureName = 'read-texture';
const ReadAlphaTextureName = 'read-alpha-texture';
export function readTexture(ctx: WebGLContext, texture: Texture) {
export function readTexture<T extends Uint8Array | Float32Array | Int32Array = Uint8Array>(ctx: WebGLContext, texture: Texture, array?: T) {
const { gl, resources } = ctx;
if (texture.type !== gl.UNSIGNED_BYTE) throw new Error('unsupported texture type');
if (!array && texture.type !== gl.UNSIGNED_BYTE) throw new Error('unsupported texture type');
if (!ctx.namedFramebuffers[ReadTextureName]) {
ctx.namedFramebuffers[ReadTextureName] = resources.framebuffer();
@@ -86,7 +86,7 @@ export function readTexture(ctx: WebGLContext, texture: Texture) {
const width = texture.getWidth();
const height = texture.getHeight();
const array = new Uint8Array(width * height * 4);
if (!array) array = new Uint8Array(width * height * 4) as T;
framebuffer.bind();
texture.attachFramebuffer(framebuffer, 0);
ctx.readPixels(0, 0, width, height, array);
@@ -125,8 +125,8 @@ export function readAlphaTexture(ctx: WebGLContext, texture: Texture) {
state.clearColor(0, 0, 0, 0);
state.blendFunc(gl.ONE, gl.ONE);
state.blendEquation(gl.FUNC_ADD);
gl.viewport(0, 0, width, height);
gl.scissor(0, 0, width, height);
state.viewport(0, 0, width, height);
state.scissor(0, 0, width, height);
gl.clear(gl.COLOR_BUFFER_BIT);
copy.render();

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2020 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>
*/
@@ -10,7 +10,6 @@ import { GraphicsRenderItem, ComputeRenderItem, GraphicsRenderVariant } from './
import { ValueCell } from '../mol-util';
import { idFactory } from '../mol-util/id-factory';
import { clamp } from '../mol-math/interpolate';
import { Textures } from './webgl/texture';
const getNextRenderableId = idFactory();
@@ -30,7 +29,7 @@ export interface Renderable<T extends RenderableValues> {
readonly values: T
readonly state: RenderableState
render: (variant: GraphicsRenderVariant, sharedTexturesList?: Textures) => void
render: (variant: GraphicsRenderVariant, sharedTexturesCount: number) => void
getProgram: (variant: GraphicsRenderVariant) => Program
update: () => void
dispose: () => void
@@ -43,11 +42,11 @@ export function createRenderable<T extends Values<RenderableSchema>>(renderItem:
values,
state,
render: (variant: GraphicsRenderVariant, sharedTexturesList?: Textures) => {
render: (variant: GraphicsRenderVariant, sharedTexturesCount: number) => {
if (values.uAlpha && values.alpha) {
ValueCell.updateIfChanged(values.uAlpha, clamp(values.alpha.ref.value * state.alphaFactor, 0, 1));
}
renderItem.render(variant, sharedTexturesList);
renderItem.render(variant, sharedTexturesCount);
},
getProgram: (variant: GraphicsRenderVariant) => renderItem.getProgram(variant),
update: () => renderItem.update(),
@@ -73,7 +72,7 @@ export function createComputeRenderable<T extends Values<RenderableSchema>>(rend
id: getNextRenderableId(),
values,
render: () => renderItem.render('compute'),
render: () => renderItem.render('compute', 0),
update: () => renderItem.update(),
dispose: () => renderItem.destroy()
};

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>
*/
@@ -23,11 +23,12 @@ export const CylindersSchema = {
elements: ElementsSpec('uint32'),
padding: ValueSpec('number'),
uDoubleSided: UniformSpec('b'),
uDoubleSided: UniformSpec('b', 'material'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
uBumpFrequency: UniformSpec('f'),
uBumpAmplitude: UniformSpec('f'),
dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']),
uBumpFrequency: UniformSpec('f', 'material'),
uBumpAmplitude: UniformSpec('f', 'material'),
};
export type CylindersSchema = typeof CylindersSchema
export type CylindersValues = Values<CylindersSchema>

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2020 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>
*/
@@ -26,7 +26,7 @@ export const DirectVolumeSchema = {
uTransform: UniformSpec('m4'),
uGridDim: UniformSpec('v3'),
tTransferTex: TextureSpec('image-uint8', 'alpha', 'ubyte', 'linear'),
uTransferScale: UniformSpec('f'),
uTransferScale: UniformSpec('f', 'material'),
dGridTexType: DefineSpec('string', ['2d', '3d']),
uGridTexDim: UniformSpec('v3'),

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -20,7 +20,7 @@ export const LinesSchema = {
aEnd: AttributeSpec('float32', 3, 0),
elements: ElementsSpec('uint32'),
dLineSizeAttenuation: DefineSpec('boolean'),
uDoubleSided: UniformSpec('b'),
uDoubleSided: UniformSpec('b', 'material'),
dFlipSided: DefineSpec('boolean'),
};
export type LinesSchema = typeof LinesSchema

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>
*/
@@ -17,13 +17,15 @@ export const MeshSchema = {
aPosition: AttributeSpec('float32', 3, 0),
aNormal: AttributeSpec('float32', 3, 0),
elements: ElementsSpec('uint32'),
dVaryingGroup: DefineSpec('boolean'),
dFlatShaded: DefineSpec('boolean'),
uDoubleSided: UniformSpec('b'),
uDoubleSided: UniformSpec('b', 'material'),
dFlipSided: DefineSpec('boolean'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
uBumpFrequency: UniformSpec('f'),
uBumpAmplitude: UniformSpec('f'),
dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']),
uBumpFrequency: UniformSpec('f', 'material'),
uBumpAmplitude: UniformSpec('f', 'material'),
meta: ValueSpec('unknown')
} as const;
export type MeshSchema = typeof MeshSchema

View File

@@ -1,7 +1,8 @@
/**
* 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 Gianluca Tomasello <giagitom@gmail.com>
*/
import { ValueCell } from '../../mol-util';
@@ -36,6 +37,7 @@ export function splitValues(schema: RenderableSchema, values: RenderableValues)
const attributeValues: AttributeValues = {};
const defineValues: DefineValues = {};
const textureValues: TextureValues = {};
const materialTextureValues: TextureValues = {};
const uniformValues: UniformValues = {};
const materialUniformValues: UniformValues = {};
const bufferedUniformValues: UniformValues = {};
@@ -44,7 +46,10 @@ export function splitValues(schema: RenderableSchema, values: RenderableValues)
if (spec.type === 'attribute') attributeValues[k] = values[k];
if (spec.type === 'define') defineValues[k] = values[k];
// check if k exists in values to exclude global textures
if (spec.type === 'texture' && values[k] !== undefined) textureValues[k] = values[k];
if (spec.type === 'texture' && values[k] !== undefined) {
if (spec.variant === 'material') materialTextureValues[k] = values[k];
else textureValues[k] = values[k];
}
// check if k exists in values to exclude global uniforms
if (spec.type === 'uniform' && values[k] !== undefined) {
if (spec.variant === 'material') materialUniformValues[k] = values[k];
@@ -52,7 +57,7 @@ export function splitValues(schema: RenderableSchema, values: RenderableValues)
else uniformValues[k] = values[k];
}
});
return { attributeValues, defineValues, textureValues, uniformValues, materialUniformValues, bufferedUniformValues };
return { attributeValues, defineValues, textureValues, materialTextureValues, uniformValues, materialUniformValues, bufferedUniformValues };
}
export type Versions<T extends RenderableValues> = { [k in keyof T]: number }
@@ -76,9 +81,9 @@ export function UniformSpec<K extends UniformKind>(kind: K, variant?: 'material'
return { type: 'uniform', kind, variant };
}
export type TextureSpec<K extends TextureKind> = { type: 'texture', kind: K, format: TextureFormat, dataType: TextureType, filter: TextureFilter }
export function TextureSpec<K extends TextureKind>(kind: K, format: TextureFormat, dataType: TextureType, filter: TextureFilter): TextureSpec<K> {
return { type: 'texture', kind, format, dataType, filter };
export type TextureSpec<K extends TextureKind> = { type: 'texture', kind: K, format: TextureFormat, dataType: TextureType, filter: TextureFilter, variant?: 'material' }
export function TextureSpec<K extends TextureKind>(kind: K, format: TextureFormat, dataType: TextureType, filter: TextureFilter, variant?: 'material'): TextureSpec<K> {
return { type: 'texture', kind, format, dataType, filter, variant };
}
export type ElementsSpec<K extends ElementsKind> = { type: 'elements', kind: K }
@@ -153,7 +158,7 @@ export const GlobalUniformSchema = {
uXrayEdgeFalloff: UniformSpec('f'),
uRenderWboit: UniformSpec('b'),
uRenderMask: UniformSpec('i'),
uMarkingDepthTest: UniformSpec('b'),
uMarkingType: UniformSpec('i'),
uPickType: UniformSpec('i'),
@@ -163,6 +168,11 @@ export type GlobalUniformValues = Values<GlobalUniformSchema>
export const GlobalTextureSchema = {
tDepth: TextureSpec('texture', 'depth', 'ushort', 'nearest'),
// dpoit
tDpoitDepth: TextureSpec('texture', 'rg', 'float', 'nearest'),
tDpoitFrontColor: TextureSpec('texture', 'rgba', 'float', 'nearest'),
tDpoitBackColor: TextureSpec('texture', 'rgba', 'float', 'nearest')
} as const;
export type GlobalTextureSchema = typeof GlobalTextureSchema
export type GlobalTextureValues = Values<GlobalTextureSchema>
@@ -194,7 +204,7 @@ export const SizeSchema = {
uSizeTexDim: UniformSpec('v2'),
tSize: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'),
dSizeType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'groupInstance']),
uSizeFactor: UniformSpec('f'),
uSizeFactor: UniformSpec('f', 'material'),
} as const;
export type SizeSchema = typeof SizeSchema
export type SizeValues = Values<SizeSchema>
@@ -205,6 +215,7 @@ export const MarkerSchema = {
tMarker: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
markerAverage: ValueSpec('number'),
markerStatus: ValueSpec('number'),
dMarkerType: DefineSpec('string', ['instance', 'groupInstance']),
} as const;
export type MarkerSchema = typeof MarkerSchema
export type MarkerValues = Values<MarkerSchema>
@@ -217,7 +228,7 @@ export const OverpaintSchema = {
uOverpaintGridDim: UniformSpec('v3'),
uOverpaintGridTransform: UniformSpec('v4'),
tOverpaintGrid: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
dOverpaintType: DefineSpec('string', ['groupInstance', 'volumeInstance']),
dOverpaintType: DefineSpec('string', ['instance', 'groupInstance', 'volumeInstance']),
} as const;
export type OverpaintSchema = typeof OverpaintSchema
export type OverpaintValues = Values<OverpaintSchema>
@@ -231,7 +242,7 @@ export const TransparencySchema = {
uTransparencyGridDim: UniformSpec('v3'),
uTransparencyGridTransform: UniformSpec('v4'),
tTransparencyGrid: TextureSpec('texture', 'alpha', 'ubyte', 'linear'),
dTransparencyType: DefineSpec('string', ['groupInstance', 'volumeInstance']),
dTransparencyType: DefineSpec('string', ['instance', 'groupInstance', 'volumeInstance'])
} as const;
export type TransparencySchema = typeof TransparencySchema
export type TransparencyValues = Values<TransparencySchema>
@@ -244,7 +255,7 @@ export const SubstanceSchema = {
uSubstanceGridDim: UniformSpec('v3'),
uSubstanceGridTransform: UniformSpec('v4'),
tSubstanceGrid: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
dSubstanceType: DefineSpec('string', ['groupInstance', 'volumeInstance']),
dSubstanceType: DefineSpec('string', ['instance', 'groupInstance', 'volumeInstance']),
} as const;
export type SubstanceSchema = typeof SubstanceSchema
export type SubstanceValues = Values<SubstanceSchema>
@@ -253,6 +264,7 @@ export const ClippingSchema = {
uClippingTexDim: UniformSpec('v2'),
tClipping: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
dClipping: DefineSpec('boolean'),
dClippingType: DefineSpec('string', ['instance', 'groupInstance']),
} as const;
export type ClippingSchema = typeof ClippingSchema
export type ClippingValues = Values<ClippingSchema>
@@ -268,14 +280,15 @@ export const BaseSchema = {
...ClippingSchema,
dLightCount: DefineSpec('number'),
dColorMarker: DefineSpec('boolean'),
dClipObjectCount: DefineSpec('number'),
dClipVariant: DefineSpec('string', ['instance', 'pixel']),
uClipObjectType: UniformSpec('i[]'),
uClipObjectInvert: UniformSpec('b[]'),
uClipObjectPosition: UniformSpec('v3[]'),
uClipObjectRotation: UniformSpec('v4[]'),
uClipObjectScale: UniformSpec('v3[]'),
uClipObjectType: UniformSpec('i[]', 'material'),
uClipObjectInvert: UniformSpec('b[]', 'material'),
uClipObjectPosition: UniformSpec('v3[]', 'material'),
uClipObjectRotation: UniformSpec('v4[]', 'material'),
uClipObjectScale: UniformSpec('v3[]', 'material'),
aInstance: AttributeSpec('float32', 1, 1),
/**
@@ -311,6 +324,8 @@ export const BaseSchema = {
extraTransform: ValueSpec('float32'),
/** denotes reflection in transform */
hasReflection: ValueSpec('boolean'),
/** use instance granularity for marker, transparency, clipping, overpaint, substance */
instanceGranularity: ValueSpec('boolean'),
/** bounding sphere taking aTransform into account and encompases all instances */
boundingSphere: ValueSpec('sphere'),
@@ -318,4 +333,4 @@ export const BaseSchema = {
invariantBoundingSphere: ValueSpec('sphere'),
} as const;
export type BaseSchema = typeof BaseSchema
export type BaseValues = Values<BaseSchema>
export type BaseValues = Values<BaseSchema>

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>
*/
@@ -20,11 +20,12 @@ export const SpheresSchema = {
elements: ElementsSpec('uint32'),
padding: ValueSpec('number'),
uDoubleSided: UniformSpec('b'),
uDoubleSided: UniformSpec('b', 'material'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
uBumpFrequency: UniformSpec('f'),
uBumpAmplitude: UniformSpec('f'),
dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']),
uBumpFrequency: UniformSpec('f', 'material'),
uBumpAmplitude: UniformSpec('f', 'material'),
};
export type SpheresSchema = typeof SpheresSchema
export type SpheresValues = Values<SpheresSchema>

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 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>
*/
@@ -24,13 +24,13 @@ export const TextSchema = {
tFont: TextureSpec('image-uint8', 'alpha', 'ubyte', 'linear'),
padding: ValueSpec('number'),
uBorderWidth: UniformSpec('f'),
uBorderColor: UniformSpec('v3'),
uOffsetX: UniformSpec('f'),
uOffsetY: UniformSpec('f'),
uOffsetZ: UniformSpec('f'),
uBackgroundColor: UniformSpec('v3'),
uBackgroundOpacity: UniformSpec('f'),
uBorderWidth: UniformSpec('f', 'material'),
uBorderColor: UniformSpec('v3', 'material'),
uOffsetX: UniformSpec('f', 'material'),
uOffsetY: UniformSpec('f', 'material'),
uOffsetZ: UniformSpec('f', 'material'),
uBackgroundColor: UniformSpec('v3', 'material'),
uBackgroundOpacity: UniformSpec('f', 'material'),
};
export type TextSchema = typeof TextSchema
export type TextValues = Values<TextSchema>

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>
*/
@@ -17,14 +17,15 @@ export const TextureMeshSchema = {
tPosition: TextureSpec('texture', 'rgb', 'float', 'nearest'),
tGroup: TextureSpec('texture', 'alpha', 'float', 'nearest'),
tNormal: TextureSpec('texture', 'rgb', 'float', 'nearest'),
dVaryingGroup: DefineSpec('boolean'),
dFlatShaded: DefineSpec('boolean'),
uDoubleSided: UniformSpec('b'),
uDoubleSided: UniformSpec('b', 'material'),
dFlipSided: DefineSpec('boolean'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
uBumpFrequency: UniformSpec('f'),
uBumpAmplitude: UniformSpec('f'),
dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']),
uBumpFrequency: UniformSpec('f', 'material'),
uBumpAmplitude: UniformSpec('f', 'material'),
meta: ValueSpec('unknown')
};
export type TextureMeshSchema = typeof TextureMeshSchema

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2020 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,6 +8,7 @@ import { Sphere3D } from '../../mol-math/geometry';
import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
import { BoundaryHelper } from '../../mol-math/geometry/boundary-helper';
import { TextureFilter } from '../webgl/texture';
import { arrayMinMax } from '../../mol-util/array';
export function calculateTextureInfo(n: number, itemSize: number) {
n = Math.max(n, 2); // observed issues with 1 pixel textures
@@ -42,7 +43,8 @@ export function createTextureImage<T extends Uint8Array | Float32Array>(n: numbe
const DefaultPrintImageOptions = {
scale: 1,
pixelated: false,
id: 'molstar.debug.image'
id: 'molstar.debug.image',
normalize: false,
};
export type PrintImageOptions = typeof DefaultPrintImageOptions
@@ -58,7 +60,17 @@ export function printTextureImage(textureImage: TextureImage<any>, options: Part
}
}
} else if (itemSize === 4) {
data.set(array);
if (options.normalize) {
const [min, max] = arrayMinMax(array);
for (let i = 0, il = width * height * 4; i < il; i += 4) {
data[i] = ((array[i] - min) / (max - min)) * 255;
data[i + 1] = ((array[i + 1] - min) / (max - min)) * 255;
data[i + 2] = ((array[i + 2] - min) / (max - min)) * 255;
data[i + 3] = 255;
}
} else {
data.set(array);
}
} else {
console.warn(`itemSize '${itemSize}' not supported`);
}

View File

@@ -2,6 +2,7 @@
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Gianluca Tomasello <giagitom@gmail.com>
*/
import { Viewport } from '../mol-canvas3d/camera/util';
@@ -19,6 +20,7 @@ import { degToRad } from '../mol-math/misc';
import { Texture, Textures } from './webgl/texture';
import { arrayMapUpsert } from '../mol-util/array';
import { clamp } from '../mol-math/interpolate';
import { isTimingMode } from '../mol-util/debug';
export interface RendererStats {
programCount: number
@@ -54,20 +56,24 @@ interface Renderer {
readonly props: Readonly<RendererProps>
clear: (toBackgroundColor: boolean, ignoreTransparentBackground?: boolean) => void
clearDepth: () => void
clearDepth: (packed?: boolean) => void
update: (camera: ICamera) => void
renderPick: (group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, depthTexture: Texture | null, pickType: PickType) => void
renderPick: (group: Scene.Group, camera: ICamera, variant: 'pick' | 'depth', depthTexture: Texture | null, pickType: PickType) => void
renderDepth: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderDepthOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderDepthTransparent: (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
renderBlended: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderBlended: (group: Scene, camera: ICamera) => void
renderBlendedOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderBlendedTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderBlendedVolumeOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderBlendedVolumeTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderBlendedVolume: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderWboitOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderWboitTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderDpoitOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderDpoitTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null, dpoitTextures: { depth: Texture, frontColor: Texture, backColor: Texture }) => void
renderDpoitVolume: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
setProps: (props: Partial<RendererProps>) => void
setViewport: (x: number, y: number, width: number, height: number) => void
@@ -87,6 +93,7 @@ export const RendererParams = {
interiorColorFlag: PD.Boolean(true, { label: 'Use Interior Color' }),
interiorColor: PD.Color(Color.fromNormalizedRgb(0.3, 0.3, 0.3)),
colorMarker: PD.Boolean(true, { description: 'Enable color marker' }),
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.3, { min: 0.0, max: 1.0, step: 0.1 }),
@@ -135,6 +142,18 @@ function getLight(props: RendererProps['light'], light?: Light): Light {
}
namespace Renderer {
const enum Flag {
None = 0,
BlendedFront = 1,
BlendedBack = 2,
}
const enum Mask {
All = 0,
Opaque = 1,
Transparent = 2,
}
export function create(ctx: WebGLContext, props: Partial<RendererProps> = {}): Renderer {
const { gl, state, stats } = ctx;
const p = PD.merge(RendererParams, PD.getDefaultValues(RendererParams), props);
@@ -146,7 +165,9 @@ namespace Renderer {
let transparentBackground = false;
const emptyDepthTexture = ctx.resources.texture('image-depth', 'depth', 'ushort', 'nearest');
const emptyDepthTexture = ctx.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
emptyDepthTexture.define(1, 1);
emptyDepthTexture.load({ array: new Uint8Array([255, 255, 255, 255]), width: 1, height: 1 });
const sharedTexturesList: Textures = [
['tDepth', emptyDepthTexture]
];
@@ -191,7 +212,7 @@ namespace Renderer {
uFogFar: ValueCell.create(10000),
uFogColor: ValueCell.create(bgColor),
uRenderWboit: ValueCell.create(false),
uRenderMask: ValueCell.create(0),
uMarkingDepthTest: ValueCell.create(false),
uPickType: ValueCell.create(PickType.None),
uMarkingType: ValueCell.create(MarkingType.None),
@@ -220,7 +241,7 @@ namespace Renderer {
let globalUniformsNeedUpdate = true;
const renderObject = (r: GraphicsRenderable, variant: GraphicsRenderVariant) => {
const renderObject = (r: GraphicsRenderable, variant: GraphicsRenderVariant, flag: Flag) => {
if (r.state.disposed || !r.state.visible || (!r.state.pickable && variant === 'pick')) {
return;
}
@@ -230,6 +251,10 @@ namespace Renderer {
ValueCell.update(r.values.dLightCount, light.count);
definesNeedUpdate = true;
}
if (r.values.dColorMarker.ref.value !== p.colorMarker) {
ValueCell.update(r.values.dColorMarker, p.colorMarker);
definesNeedUpdate = true;
}
if (definesNeedUpdate) r.update();
const program = r.getProgram(variant);
@@ -242,11 +267,12 @@ namespace Renderer {
if (globalUniformsNeedUpdate) {
// console.log('globalUniformsNeedUpdate')
program.setUniforms(globalUniformList);
program.bindTextures(sharedTexturesList, 0);
globalUniformsNeedUpdate = false;
}
if (r.values.dGeometryType.ref.value === 'directVolume') {
if (variant !== 'colorWboit' && variant !== 'colorBlended') {
if (variant !== 'colorDpoit' && variant !== 'colorWboit' && variant !== 'colorBlended') {
return; // only color supported
}
@@ -259,6 +285,24 @@ namespace Renderer {
state.disable(gl.DEPTH_TEST);
state.depthMask(false);
}
} else if (flag === Flag.BlendedFront) {
state.enable(gl.CULL_FACE);
if (r.values.dFlipSided?.ref.value) {
state.frontFace(gl.CW);
state.cullFace(gl.FRONT);
} else {
state.frontFace(gl.CCW);
state.cullFace(gl.BACK);
}
} else if (flag === Flag.BlendedBack) {
state.enable(gl.CULL_FACE);
if (r.values.dFlipSided?.ref.value) {
state.frontFace(gl.CW);
state.cullFace(gl.BACK);
} else {
state.frontFace(gl.CCW);
state.cullFace(gl.FRONT);
}
} else {
if (r.values.uDoubleSided) {
if (r.values.uDoubleSided.ref.value || r.values.hasReflection.ref.value) {
@@ -271,14 +315,9 @@ namespace Renderer {
state.disable(gl.CULL_FACE);
}
if (r.values.dFlipSided) {
if (r.values.dFlipSided.ref.value) {
state.frontFace(gl.CW);
state.cullFace(gl.FRONT);
} else {
state.frontFace(gl.CCW);
state.cullFace(gl.BACK);
}
if (r.values.dFlipSided?.ref.value) {
state.frontFace(gl.CW);
state.cullFace(gl.FRONT);
} else {
// webgl default
state.frontFace(gl.CCW);
@@ -286,7 +325,7 @@ namespace Renderer {
}
}
r.render(variant, sharedTexturesList);
r.render(variant, sharedTexturesList.length);
};
const update = (camera: ICamera) => {
@@ -308,7 +347,7 @@ namespace Renderer {
ValueCell.updateIfChanged(globalUniforms.uTransparentBackground, transparentBackground);
};
const updateInternal = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null, renderWboit: boolean, markingDepthTest: boolean) => {
const updateInternal = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null, renderMask: Mask, markingDepthTest: boolean) => {
arrayMapUpsert(sharedTexturesList, 'tDepth', depthTexture || emptyDepthTexture);
ValueCell.update(globalUniforms.uModel, group.view);
@@ -317,55 +356,96 @@ namespace Renderer {
ValueCell.update(globalUniforms.uModelViewProjection, Mat4.mul(modelViewProjection, modelView, camera.projection));
ValueCell.update(globalUniforms.uInvModelViewProjection, Mat4.invert(invModelViewProjection, modelViewProjection));
ValueCell.updateIfChanged(globalUniforms.uRenderWboit, renderWboit);
ValueCell.updateIfChanged(globalUniforms.uRenderMask, renderMask);
ValueCell.updateIfChanged(globalUniforms.uMarkingDepthTest, markingDepthTest);
state.enable(gl.SCISSOR_TEST);
state.colorMask(true, true, true, true);
const { x, y, width, height } = viewport;
gl.viewport(x, y, width, height);
gl.scissor(x, y, width, height);
state.viewport(x, y, width, height);
state.scissor(x, y, width, height);
globalUniformsNeedUpdate = true;
state.currentRenderItemId = -1;
};
const renderPick = (group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, depthTexture: Texture | null, pickType: PickType) => {
if (isTimingMode) ctx.timer.mark('Renderer.renderPick');
state.disable(gl.BLEND);
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
updateInternal(group, camera, depthTexture, false, false);
updateInternal(group, camera, depthTexture, Mask.All, false);
ValueCell.updateIfChanged(globalUniforms.uPickType, pickType);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
if (!renderables[i].state.colorOnly) {
renderObject(renderables[i], variant);
renderObject(renderables[i], variant, Flag.None);
}
}
if (isTimingMode) ctx.timer.markEnd('Renderer.renderPick');
};
const renderDepth = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
if (isTimingMode) ctx.timer.mark('Renderer.renderDepth');
state.disable(gl.BLEND);
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
updateInternal(group, camera, depthTexture, false, false);
updateInternal(group, camera, depthTexture, Mask.All, false);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
renderObject(renderables[i], 'depth');
renderObject(renderables[i], 'depth', Flag.None);
}
if (isTimingMode) ctx.timer.markEnd('Renderer.renderDepth');
};
const renderMarkingDepth = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
const renderDepthOpaque = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
if (isTimingMode) ctx.timer.mark('Renderer.renderDepthOpaque');
state.disable(gl.BLEND);
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
updateInternal(group, camera, depthTexture, false, false);
updateInternal(group, camera, depthTexture, Mask.Opaque, false);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i];
if (r.state.opaque && r.values.transparencyAverage.ref.value !== 1 && !r.values.dXrayShaded?.ref.value) {
renderObject(r, 'depth', Flag.None);
}
}
if (isTimingMode) ctx.timer.markEnd('Renderer.renderDepthOpaque');
};
const renderDepthTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
if (isTimingMode) ctx.timer.mark('Renderer.renderDepthTransparent');
state.disable(gl.BLEND);
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
updateInternal(group, camera, depthTexture, Mask.Transparent, false);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i];
if (!r.state.opaque || r.values.transparencyAverage.ref.value > 0 || r.values.dXrayShaded?.ref.value) {
renderObject(r, 'depth', Flag.None);
}
}
if (isTimingMode) ctx.timer.markEnd('Renderer.renderDepthTransparent');
};
const renderMarkingDepth = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
if (isTimingMode) ctx.timer.mark('Renderer.renderMarkingDepth');
state.disable(gl.BLEND);
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
updateInternal(group, camera, depthTexture, Mask.All, false);
ValueCell.updateIfChanged(globalUniforms.uMarkingType, MarkingType.Depth);
const { renderables } = group;
@@ -373,17 +453,19 @@ namespace Renderer {
const r = renderables[i];
if (r.values.markerAverage.ref.value !== 1) {
renderObject(renderables[i], 'marking');
renderObject(renderables[i], 'marking', Flag.None);
}
}
if (isTimingMode) ctx.timer.markEnd('Renderer.renderMarkingDepth');
};
const renderMarkingMask = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
if (isTimingMode) ctx.timer.mark('Renderer.renderMarkingMask');
state.disable(gl.BLEND);
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
updateInternal(group, camera, depthTexture, false, !!depthTexture);
updateInternal(group, camera, depthTexture, Mask.All, !!depthTexture);
ValueCell.updateIfChanged(globalUniforms.uMarkingType, MarkingType.Mask);
const { renderables } = group;
@@ -391,36 +473,46 @@ namespace Renderer {
const r = renderables[i];
if (r.values.markerAverage.ref.value > 0) {
renderObject(renderables[i], 'marking');
renderObject(renderables[i], 'marking', Flag.None);
}
}
if (isTimingMode) ctx.timer.markEnd('Renderer.renderMarkingMask');
};
const renderBlended = (scene: Scene, camera: ICamera) => {
if (scene.hasOpaque) {
renderBlendedOpaque(scene, camera, null);
}
if (scene.opacityAverage < 1) {
renderBlendedTransparent(scene, camera, null);
}
};
const renderBlended = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
renderBlendedOpaque(group, camera, depthTexture);
renderBlendedTransparent(group, camera, depthTexture);
};
const renderBlendedOpaque = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
if (isTimingMode) ctx.timer.mark('Renderer.renderBlendedOpaque');
state.disable(gl.BLEND);
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
updateInternal(group, camera, depthTexture, false, false);
updateInternal(group, camera, depthTexture, Mask.Opaque, false);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i];
if (r.state.opaque) {
renderObject(r, 'colorBlended');
renderObject(r, 'colorBlended', Flag.None);
} else if (r.values.uDoubleSided?.ref.value && r.values.dTransparentBackfaces?.ref.value === 'opaque') {
renderObject(r, 'colorBlended', Flag.BlendedBack);
}
}
if (isTimingMode) ctx.timer.markEnd('Renderer.renderBlendedOpaque');
};
const renderBlendedTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
if (isTimingMode) ctx.timer.mark('Renderer.renderBlendedTransparent');
state.enable(gl.DEPTH_TEST);
updateInternal(group, camera, depthTexture, false, false);
updateInternal(group, camera, depthTexture, Mask.Transparent, false);
const { renderables } = group;
@@ -435,63 +527,52 @@ namespace Renderer {
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i];
if (!r.state.opaque && r.state.writeDepth) {
renderObject(r, 'colorBlended');
renderObject(r, 'colorBlended', Flag.None);
}
}
state.depthMask(false);
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i];
if (!r.state.opaque && !r.state.writeDepth) {
renderObject(r, 'colorBlended');
if ((!r.state.opaque && !r.state.writeDepth) || r.values.transparencyAverage.ref.value > 0) {
if (r.values.uDoubleSided?.ref.value) {
// render frontfaces and backfaces separately to avoid artefacts
if (r.values.dTransparentBackfaces?.ref.value !== 'opaque') {
renderObject(r, 'colorBlended', Flag.BlendedBack);
}
renderObject(r, 'colorBlended', Flag.BlendedFront);
} else {
renderObject(r, 'colorBlended', Flag.None);
}
}
}
if (isTimingMode) ctx.timer.markEnd('Renderer.renderBlendedTransparent');
};
const renderBlendedVolumeOpaque = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
const renderBlendedVolume = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
if (isTimingMode) ctx.timer.mark('Renderer.renderBlendedVolume');
state.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
state.enable(gl.BLEND);
updateInternal(group, camera, depthTexture, false, false);
updateInternal(group, camera, depthTexture, Mask.Transparent, false);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i];
// 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.dXrayShaded?.ref.value) {
renderObject(r, 'colorBlended');
}
}
};
const renderBlendedVolumeTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
state.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
state.enable(gl.BLEND);
updateInternal(group, camera, depthTexture, false, false);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i];
// 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.dXrayShaded?.ref.value) {
renderObject(r, 'colorBlended');
if (r.values.dGeometryType.ref.value === 'directVolume') {
renderObject(r, 'colorBlended', Flag.None);
}
}
if (isTimingMode) ctx.timer.markEnd('Renderer.renderBlendedVolume');
};
const renderWboitOpaque = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
if (isTimingMode) ctx.timer.mark('Renderer.renderWboitOpaque');
state.disable(gl.BLEND);
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
updateInternal(group, camera, depthTexture, false, false);
updateInternal(group, camera, depthTexture, Mask.Opaque, false);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
@@ -500,14 +581,16 @@ 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.dGeometryType.ref.value !== 'directVolume' && r.values.dPointStyle?.ref.value !== 'fuzzy' && !r.values.dXrayShaded?.ref.value) {
renderObject(r, 'colorWboit');
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) || r.values.dTransparentBackfaces?.ref.value === 'opaque') {
renderObject(r, 'colorWboit', Flag.None);
}
}
if (isTimingMode) ctx.timer.markEnd('Renderer.renderWboitOpaque');
};
const renderWboitTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
updateInternal(group, camera, depthTexture, true, false);
if (isTimingMode) ctx.timer.mark('Renderer.renderWboitTransparent');
updateInternal(group, camera, depthTexture, Mask.Transparent, false);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
@@ -516,10 +599,76 @@ 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.dGeometryType.ref.value === 'directVolume' || r.values.dPointStyle?.ref.value === 'fuzzy' || !!r.values.uBackgroundColor || r.values.dXrayShaded?.ref.value) {
renderObject(r, 'colorWboit');
if (alpha < 1 || r.values.transparencyAverage.ref.value > 0 || r.values.dGeometryType.ref.value === 'directVolume' || r.values.dPointStyle?.ref.value === 'fuzzy' || r.values.dGeometryType.ref.value === 'text' || r.values.dXrayShaded?.ref.value) {
renderObject(r, 'colorWboit', Flag.None);
}
}
if (isTimingMode) ctx.timer.markEnd('Renderer.renderWboitTransparent');
};
const renderDpoitOpaque = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
if (isTimingMode) ctx.timer.mark('Renderer.renderDpoitOpaque');
state.disable(gl.BLEND);
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
updateInternal(group, camera, depthTexture, Mask.Opaque, false);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i];
// 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.dPointStyle?.ref.value !== 'fuzzy' && !r.values.dXrayShaded?.ref.value) || r.values.dTransparentBackfaces?.ref.value === 'opaque') {
renderObject(r, 'colorDpoit', Flag.None);
}
}
if (isTimingMode) ctx.timer.markEnd('Renderer.renderDpoitOpaque');
};
const renderDpoitTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null, dpoitTextures: { depth: Texture, frontColor: Texture, backColor: Texture }) => {
if (isTimingMode) ctx.timer.mark('Renderer.renderDpoitTransparent');
state.enable(gl.BLEND);
arrayMapUpsert(sharedTexturesList, 'tDpoitDepth', dpoitTextures.depth);
arrayMapUpsert(sharedTexturesList, 'tDpoitFrontColor', dpoitTextures.frontColor);
arrayMapUpsert(sharedTexturesList, 'tDpoitBackColor', dpoitTextures.backColor);
updateInternal(group, camera, depthTexture, Mask.Transparent, false);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i];
// 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.dPointStyle?.ref.value === 'fuzzy' || !!r.values.uBackgroundColor || r.values.dXrayShaded?.ref.value) {
renderObject(r, 'colorDpoit', Flag.None);
}
}
if (isTimingMode) ctx.timer.markEnd('Renderer.renderDpoitTransparent');
};
const renderDpoitVolume = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
if (isTimingMode) ctx.timer.mark('Renderer.renderDpoitVolume');
state.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
state.enable(gl.BLEND);
updateInternal(group, camera, depthTexture, Mask.Transparent, false);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i];
if (r.values.dGeometryType.ref.value === 'directVolume') {
renderObject(r, 'colorDpoit', Flag.None);
}
}
if (isTimingMode) ctx.timer.markEnd('Renderer.renderDpoitVolume');
};
return {
@@ -538,25 +687,36 @@ namespace Renderer {
}
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
},
clearDepth: () => {
clearDepth: (packed = false) => {
state.enable(gl.SCISSOR_TEST);
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
gl.clear(gl.DEPTH_BUFFER_BIT);
if (packed) {
state.colorMask(true, true, true, true);
state.clearColor(1, 1, 1, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
} else {
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
gl.clear(gl.DEPTH_BUFFER_BIT);
}
},
update,
renderPick,
renderDepth,
renderDepthOpaque,
renderDepthTransparent,
renderMarkingDepth,
renderMarkingMask,
renderBlended,
renderBlendedOpaque,
renderBlendedTransparent,
renderBlendedVolumeOpaque,
renderBlendedVolumeTransparent,
renderBlendedVolume,
renderWboitOpaque,
renderWboitTransparent,
renderDpoitOpaque,
renderDpoitTransparent,
renderDpoitVolume,
setProps: (props: Partial<RendererProps>) => {
if (props.backgroundColor !== undefined && props.backgroundColor !== p.backgroundColor) {
@@ -583,6 +743,9 @@ namespace Renderer {
ValueCell.update(globalUniforms.uInteriorColor, Color.toVec3Normalized(globalUniforms.uInteriorColor.ref.value, p.interiorColor));
}
if (props.colorMarker !== undefined && props.colorMarker !== p.colorMarker) {
p.colorMarker = props.colorMarker;
}
if (props.highlightColor !== undefined && props.highlightColor !== p.highlightColor) {
p.highlightColor = props.highlightColor;
ValueCell.update(globalUniforms.uHighlightColor, Color.toVec3Normalized(globalUniforms.uHighlightColor.ref.value, p.highlightColor));
@@ -627,8 +790,8 @@ namespace Renderer {
}
},
setViewport: (x: number, y: number, width: number, height: number) => {
gl.viewport(x, y, width, height);
gl.scissor(x, y, width, height);
state.viewport(x, y, width, height);
state.scissor(x, y, width, height);
if (x !== viewport.x || y !== viewport.y || width !== viewport.width || height !== viewport.height) {
Viewport.set(viewport, x, y, width, height);
ValueCell.update(globalUniforms.uViewport, Vec4.set(globalUniforms.uViewport.ref.value, x, y, width, height));
@@ -671,4 +834,4 @@ namespace Renderer {
}
}
export { Renderer };
export { Renderer };

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