mirror of
https://github.com/molstar/molstar.git
synced 2026-06-04 21:34:23 +08:00
Compare commits
195 Commits
v2.3.3
...
v3.0.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c57a4cdf6e | ||
|
|
18573e17f0 | ||
|
|
5bc7ffa8a7 | ||
|
|
430cc83259 | ||
|
|
3cd3afb775 | ||
|
|
93215b6beb | ||
|
|
e4f630dbef | ||
|
|
ccaf18af04 | ||
|
|
2b72098f95 | ||
|
|
b32546bea7 | ||
|
|
b9b0413e9f | ||
|
|
1d29b4627f | ||
|
|
bd44c76709 | ||
|
|
06b4761f2b | ||
|
|
daa3d1dbaa | ||
|
|
5490d5ceb5 | ||
|
|
cf3c1cfcce | ||
|
|
be2607ae84 | ||
|
|
e2966241e8 | ||
|
|
447792b1ef | ||
|
|
1cbcb5c530 | ||
|
|
f8d32d1d8d | ||
|
|
8c556c2849 | ||
|
|
4d0f0ceebf | ||
|
|
14abcddfcf | ||
|
|
704cc96a9f | ||
|
|
b51f610173 | ||
|
|
98050875c7 | ||
|
|
470280ea1a | ||
|
|
dafb5a8299 | ||
|
|
4a1af03744 | ||
|
|
bb176f1efb | ||
|
|
a53bcde973 | ||
|
|
1a8dc2c637 | ||
|
|
f96211ff91 | ||
|
|
77f9c02785 | ||
|
|
7910b65fdc | ||
|
|
eb4fc4588d | ||
|
|
5430674071 | ||
|
|
17e67e3b79 | ||
|
|
e87a0d72e4 | ||
|
|
67d3c65907 | ||
|
|
564a5486c9 | ||
|
|
9ce11c4c32 | ||
|
|
5e97b551a5 | ||
|
|
77536e75af | ||
|
|
6f12f714d2 | ||
|
|
1f67077400 | ||
|
|
d1c4cf69cb | ||
|
|
803c5eaa15 | ||
|
|
970fd5d9c3 | ||
|
|
7ccd4a1e0d | ||
|
|
3a6ab55266 | ||
|
|
eb41882c56 | ||
|
|
734851a810 | ||
|
|
6318717a15 | ||
|
|
d8498feaef | ||
|
|
aaec452bc2 | ||
|
|
bce959195a | ||
|
|
4287e09a9a | ||
|
|
fdc006f833 | ||
|
|
c704b7505c | ||
|
|
ceaf238322 | ||
|
|
b7224ce5c7 | ||
|
|
95654175fe | ||
|
|
de96244706 | ||
|
|
3104ee5742 | ||
|
|
73ac445a44 | ||
|
|
1a1d1d9d30 | ||
|
|
062aff76da | ||
|
|
7d0d24b66d | ||
|
|
6655672d11 | ||
|
|
6e573ae410 | ||
|
|
1c48c02473 | ||
|
|
78be3320ce | ||
|
|
c8018800cc | ||
|
|
bb795aca98 | ||
|
|
2cb1279f4c | ||
|
|
b876c6f618 | ||
|
|
3a7dfc055e | ||
|
|
928e521ac7 | ||
|
|
e5e9598e4b | ||
|
|
e6e1809592 | ||
|
|
812f97ddb7 | ||
|
|
c6b814b31b | ||
|
|
98566fa389 | ||
|
|
4318c89bdb | ||
|
|
b41a97ce6a | ||
|
|
862c384dc0 | ||
|
|
26cc7e94c2 | ||
|
|
c6fe6ddcba | ||
|
|
154984e74d | ||
|
|
72fcaf8321 | ||
|
|
0c14ca5888 | ||
|
|
a85ede5058 | ||
|
|
db49a16184 | ||
|
|
0704db2343 | ||
|
|
425dca4665 | ||
|
|
8d65ccabd2 | ||
|
|
cbd417ca13 | ||
|
|
1e4d1e45f9 | ||
|
|
1578211157 | ||
|
|
15932dc5df | ||
|
|
7db2205956 | ||
|
|
d87f0d236a | ||
|
|
16daca6008 | ||
|
|
a0d919c8db | ||
|
|
ffee2bf1c4 | ||
|
|
de77f6ac59 | ||
|
|
c8c2ebcd65 | ||
|
|
42796b984f | ||
|
|
746557bf52 | ||
|
|
a5020a9e96 | ||
|
|
2ebb0a35fd | ||
|
|
64aaa92d45 | ||
|
|
4baf391efe | ||
|
|
5afdcff6a5 | ||
|
|
339c397860 | ||
|
|
a58cbd31ef | ||
|
|
d232b01cf9 | ||
|
|
ec95270854 | ||
|
|
78cc0d960f | ||
|
|
b5ccdfdd53 | ||
|
|
d0c0d8e703 | ||
|
|
ebf64404be | ||
|
|
7f39cf0f37 | ||
|
|
e5dcc8e54f | ||
|
|
4592510a95 | ||
|
|
46d5442dc5 | ||
|
|
271cff4aba | ||
|
|
94fd5a97d6 | ||
|
|
28678e2f80 | ||
|
|
406307a432 | ||
|
|
56345b5096 | ||
|
|
3fcc42ee0e | ||
|
|
b903677f8a | ||
|
|
ef4b632a07 | ||
|
|
e9d485ca85 | ||
|
|
a149fa5929 | ||
|
|
bb3dde585b | ||
|
|
cd6bbeaa86 | ||
|
|
e3d24dae4b | ||
|
|
687a814a62 | ||
|
|
8f2e99dc51 | ||
|
|
568be030c3 | ||
|
|
97c3ab8b5a | ||
|
|
5db646d139 | ||
|
|
340f8f1af3 | ||
|
|
4484a4452c | ||
|
|
65b654a0a2 | ||
|
|
a8e0c13b0e | ||
|
|
41d67eb642 | ||
|
|
c76c8335d1 | ||
|
|
42528b7be5 | ||
|
|
b371f8c11c | ||
|
|
3d651b40f0 | ||
|
|
895a13fc0d | ||
|
|
c94acff82e | ||
|
|
2f9ac711d1 | ||
|
|
93b9953f6d | ||
|
|
dcaf6f8927 | ||
|
|
d96eb404e1 | ||
|
|
07322819f0 | ||
|
|
9be686686d | ||
|
|
3df539c9e1 | ||
|
|
e840059a38 | ||
|
|
1bd0339dec | ||
|
|
d0eaf2f71e | ||
|
|
254c9efbf5 | ||
|
|
73e9aed98c | ||
|
|
6e60d9713a | ||
|
|
ef0593b1e2 | ||
|
|
7831fa8b33 | ||
|
|
d4bb1a6e93 | ||
|
|
b06c134b61 | ||
|
|
3436d03468 | ||
|
|
58df6f3b85 | ||
|
|
ffaf008dce | ||
|
|
eb196a41b5 | ||
|
|
35baaaf594 | ||
|
|
0fc305aaea | ||
|
|
bf67546a61 | ||
|
|
80d54afdd0 | ||
|
|
2a74dfcf46 | ||
|
|
b0f447adde | ||
|
|
b8628ccff1 | ||
|
|
4c30057edf | ||
|
|
53a4826274 | ||
|
|
393fc99ed2 | ||
|
|
7c5dff1c8b | ||
|
|
ce3f13431d | ||
|
|
df54766ab2 | ||
|
|
94233fbcd9 | ||
|
|
d044496eaa | ||
|
|
ca825d720e |
18
.github/workflows/lint.yml
vendored
18
.github/workflows/lint.yml
vendored
@@ -1,18 +0,0 @@
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
eslint:
|
||||
name: eslint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: install node v12
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12
|
||||
- name: yarn install
|
||||
run: yarn install
|
||||
- name: eslint
|
||||
uses: icrawl/action-eslint@v1
|
||||
20
.github/workflows/node.yml
vendored
Normal file
20
.github/workflows/node.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 14
|
||||
- run: npm ci
|
||||
- run: sudo apt-get install xvfb
|
||||
- name: Lint
|
||||
run: npm run lint
|
||||
- name: Test
|
||||
run: xvfb-run --auto-servernum npm run jest
|
||||
- name: Build
|
||||
run: npm run build
|
||||
@@ -1 +1,5 @@
|
||||
tsconfig.commonjs.tsbuildinfo
|
||||
tests
|
||||
perf-tests
|
||||
_spec
|
||||
*.tsbuildinfo
|
||||
*.js.map
|
||||
110
CHANGELOG.md
110
CHANGELOG.md
@@ -6,6 +6,94 @@ Note that since we don't clearly distinguish between a public and private interf
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v3.0.0-dev.4] - 2021-12-14
|
||||
|
||||
- Add ``bumpiness`` (per-object and per-group), ``bumpFrequency`` & ``bumpAmplitude`` (per-object) render parameters (#299)
|
||||
- Change ``label`` representation defaults: Use text border instead of rectangle background
|
||||
- Add outline color option to renderer
|
||||
- Fix false positives in Model.isFromPdbArchive
|
||||
- Add drag and drop support for loading any file, including multiple at once
|
||||
- If there are session files (.molx or .molj) among the dropped files, only the first session will be loaded
|
||||
- Add drag and drop overlay
|
||||
- Safari 15.1 - 15.3 WebGL 2 support workaround
|
||||
- [Breaking] Move ``react`` and ``react-dom`` to ``peerDependencies``. This might break some builds.
|
||||
|
||||
## [v3.0.0-dev.3] - 2021-12-4
|
||||
|
||||
- Fix OBJ and USDZ export
|
||||
|
||||
## [v3.0.0-dev.2] - 2021-12-1
|
||||
|
||||
- Do not include tests and source maps in NPM package
|
||||
|
||||
## [v3.0.0-dev.0] - 2021-11-28
|
||||
|
||||
- Add multiple lights support (with color, intensity, and direction parameters)
|
||||
- [Breaking] Add per-object material rendering properties
|
||||
- ``SimpleSettingsParams.lighting.renderStyle`` and ``RendererParams.style`` were removed
|
||||
- Add substance theme with per-group material rendering properties
|
||||
- ``StructureComponentManager.Options`` state saving support
|
||||
- ``ParamDefinition.Group.presets`` support
|
||||
|
||||
## [v2.4.1] - 2021-11-28
|
||||
|
||||
- Fix: allow atoms in aromatic rings to do hydrogen bonds
|
||||
|
||||
## [v2.4.0] - 2021-11-25
|
||||
|
||||
- Fix secondary-structure property handling
|
||||
- StructureElement.Property was incorrectly resolving type & key
|
||||
- StructureSelectionQuery helpers 'helix' & 'beta' were not ensuring property availability
|
||||
- Re-enable VAO with better workaround (bind null elements buffer before deleting)
|
||||
- Add ``Representation.geometryVersion`` (increments whenever the geometry of any of its visuals changes)
|
||||
- Add support for grid-based smoothing of Overpaint and Transparency visual state for surfaces
|
||||
|
||||
## [v2.3.9] - 2021-11-20
|
||||
|
||||
- Workaround: switch off VAO support for now
|
||||
|
||||
## [v2.3.8] - 2021-11-20
|
||||
|
||||
- Fix double canvas context creation (in plugin context)
|
||||
- Fix unused vertex attribute handling (track which are used, disable the rest)
|
||||
- Workaround for VAO issue in Chrome 96 (can cause WebGL to crash on geometry updates)
|
||||
|
||||
## [v2.3.7] - 2021-11-15
|
||||
|
||||
- Added ``ViewerOptions.collapseRightPanel``
|
||||
- Added ``Viewer.loadTrajectory`` to support loading "composed" trajectories (e.g. from gro + xtc)
|
||||
- Fix: handle parent in Structure.remapModel
|
||||
- Add ``rounded`` and ``square`` helix profile options to Cartoon representation (in addition to the default ``elliptical``)
|
||||
|
||||
## [v2.3.6] - 2021-11-8
|
||||
|
||||
- Add additional measurement controls: orientation (box, axes, ellipsoid) & plane (best fit)
|
||||
- Improve aromatic bond visuals (add ``aromaticScale``, ``aromaticSpacing``, ``aromaticDashCount`` params)
|
||||
- [Breaking] Change ``adjustCylinderLength`` default to ``false`` (set to true for focus representation)
|
||||
- Fix marker highlight color overriding select color
|
||||
- CellPack extension update
|
||||
- add binary model support
|
||||
- add compartment (including membrane) geometry support
|
||||
- add latest mycoplasma model example
|
||||
- Prefer WebGL1 in Safari 15.1.
|
||||
|
||||
## [v2.3.5] - 2021-10-19
|
||||
|
||||
- Fix sequence viewer for PDB files with COMPND record and multichain entities.
|
||||
- Fix index pair bonds order assignment
|
||||
|
||||
## [v2.3.4] - 2021-10-12
|
||||
|
||||
- Fix pickScale not taken into account in line/point shader
|
||||
- Add pixel-scale, pick-scale & pick-padding GET params to Viewer app
|
||||
- Fix selecting bonds not adding their atoms in selection manager
|
||||
- Add ``preferAtoms`` option to SelectLoci/HighlightLoci behaviors
|
||||
- Make the implicit atoms of bond visuals pickable
|
||||
- Add ``preferAtomPixelPadding`` to Canvas3dInteractionHelper
|
||||
- Add points & crosses visuals to Line representation
|
||||
- Add ``pickPadding`` config option (look around in case target pixel is empty)
|
||||
- Add ``multipleBonds`` param to bond visuals with options: off, symmetric, offset
|
||||
- Fix ``argparse`` config in servers.
|
||||
|
||||
## [v2.3.3] - 2021-10-01
|
||||
|
||||
@@ -15,11 +103,10 @@ Note that since we don't clearly distinguish between a public and private interf
|
||||
|
||||
- Prefer WebGL1 on iOS devices until WebGL2 support has stabilized.
|
||||
|
||||
|
||||
## [v2.3.1] - 2021-09-28
|
||||
|
||||
- Add Charmm saccharide names
|
||||
- Treat missing occupancy column as occupany of 1
|
||||
- Treat missing occupancy column as occupancy of 1
|
||||
- Fix line shader not accounting for aspect ratio
|
||||
- [Breaking] Fix point repr & shader
|
||||
- Was unusable with ``wboit``
|
||||
@@ -137,29 +224,22 @@ Note that since we don't clearly distinguish between a public and private interf
|
||||
- Fixed Measurements UI labels (#166)
|
||||
|
||||
## [v2.0.3] - 2021-04-09
|
||||
### Added
|
||||
- Support for ``ColorTheme.palette`` designed for providing gradient-like coloring.
|
||||
|
||||
### Changed
|
||||
- Add support for ``ColorTheme.palette`` designed for providing gradient-like coloring.
|
||||
- [Breaking] The ``zip`` function is now asynchronous and expects a ``RuntimeContext``. Also added ``Zip()`` returning a ``Task``.
|
||||
- [Breaking] Add ``CubeGridFormat`` in ``alpha-orbitals`` extension.
|
||||
|
||||
## [v2.0.2] - 2021-03-29
|
||||
### Added
|
||||
- ``Canvas3D.getRenderObjects``.
|
||||
|
||||
- Add ``Canvas3D.getRenderObjects``.
|
||||
- [WIP] Animate state interpolating, including model trajectories
|
||||
|
||||
### Changed
|
||||
- Recognise MSE, SEP, TPO, PTR and PCA as non-standard amino-acids.
|
||||
|
||||
### Fixed
|
||||
- VolumeFromDensityServerCif transform label
|
||||
|
||||
- Fix VolumeFromDensityServerCif transform label
|
||||
|
||||
## [v2.0.1] - 2021-03-23
|
||||
### Fixed
|
||||
|
||||
- Exclude tsconfig.commonjs.tsbuildinfo from npm bundle
|
||||
|
||||
|
||||
## [v2.0.0] - 2021-03-23
|
||||
|
||||
Too many changes to list as this is the start of the changelog... Notably, default exports are now forbidden.
|
||||
|
||||
15
README.md
15
README.md
@@ -68,6 +68,17 @@ If working on just the viewer, ``npm run watch-viewer`` will provide shorter com
|
||||
|
||||
Debug/production mode in browsers can be turned on/off during runtime by calling ``setMolStarDebugMode(true/false, true/false)`` from the dev console.
|
||||
|
||||
### Cleaning and forcing a full rebuild
|
||||
npm run clean
|
||||
|
||||
Wipes the `build` and `lib` directories and `.tsbuildinfo` files.
|
||||
|
||||
npm run rebuild
|
||||
|
||||
Runs the cleanup script prior to building the project, forcing a full rebuild of the project.
|
||||
|
||||
Use these commands to resolve occassional build failures which may arise after some dependency updates. Once done, `npm run build` should work again. Note that full rebuilds take more time to complete.
|
||||
|
||||
### Build for production:
|
||||
NODE_ENV=production npm run build
|
||||
|
||||
@@ -122,9 +133,9 @@ and navigate to `build/viewer`
|
||||
|
||||
**Convert any CIF to BinaryCIF**
|
||||
|
||||
node lib/servers/model/preprocess -i file.cif -ob file.bcif
|
||||
node lib/commonjs/servers/model/preprocess -i file.cif -ob file.bcif
|
||||
|
||||
To see all available commands, use ``node lib/servers/model/preprocess -h``.
|
||||
To see all available commands, use ``node lib/commonjs/servers/model/preprocess -h``.
|
||||
|
||||
Or
|
||||
|
||||
|
||||
9934
package-lock.json
generated
9934
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
75
package.json
75
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "2.3.3",
|
||||
"version": "3.0.0-dev.4",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
@@ -16,6 +16,8 @@
|
||||
"test": "npm run lint && jest",
|
||||
"jest": "jest",
|
||||
"build": "npm run build-tsc && npm run build-extra && npm run build-webpack",
|
||||
"clean": "node ./scripts/clean.js",
|
||||
"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/",
|
||||
@@ -36,7 +38,7 @@
|
||||
"volume-server-test": "node lib/commonjs/servers/volume/server.js --idMap em 'test/${id}.mdb' --defaultPort 1336",
|
||||
"plugin-state": "node lib/commonjs/servers/plugin-state/index.js --working-folder ./build/state --port 1339",
|
||||
"preversion": "npm run test",
|
||||
"version": "npm run build",
|
||||
"version": "npm run rebuild && cpx .npmignore lib/",
|
||||
"postversion": "git push && git push --tags"
|
||||
},
|
||||
"files": [
|
||||
@@ -89,51 +91,51 @@
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/add": "^3.1.0",
|
||||
"@graphql-codegen/cli": "^2.2.0",
|
||||
"@graphql-codegen/cli": "^2.3.0",
|
||||
"@graphql-codegen/time": "^3.1.0",
|
||||
"@graphql-codegen/typescript": "^2.2.2",
|
||||
"@graphql-codegen/typescript": "^2.4.1",
|
||||
"@graphql-codegen/typescript-graphql-files-modules": "^2.1.0",
|
||||
"@graphql-codegen/typescript-graphql-request": "^4.1.4",
|
||||
"@graphql-codegen/typescript-operations": "^2.1.4",
|
||||
"@graphql-codegen/typescript-graphql-request": "^4.3.1",
|
||||
"@graphql-codegen/typescript-operations": "^2.2.1",
|
||||
"@types/cors": "^2.8.12",
|
||||
"@typescript-eslint/eslint-plugin": "^4.31.0",
|
||||
"@typescript-eslint/parser": "^4.31.0",
|
||||
"@types/gl": "^4.1.0",
|
||||
"@types/jest": "^27.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.5.0",
|
||||
"@typescript-eslint/parser": "^5.5.0",
|
||||
"benchmark": "^2.1.4",
|
||||
"concurrently": "^6.2.1",
|
||||
"cpx2": "^3.0.2",
|
||||
"concurrently": "^6.4.0",
|
||||
"cpx2": "^4.0.0",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"css-loader": "^6.2.0",
|
||||
"eslint": "^7.32.0",
|
||||
"css-loader": "^6.5.1",
|
||||
"eslint": "^8.3.0",
|
||||
"extra-watch-webpack-plugin": "^1.0.3",
|
||||
"file-loader": "^6.2.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
"graphql": "^15.5.3",
|
||||
"http-server": "^13.0.1",
|
||||
"jest": "^27.1.1",
|
||||
"mini-css-extract-plugin": "^2.3.0",
|
||||
"node-sass": "^6.0.1",
|
||||
"graphql": "^15.7.2",
|
||||
"http-server": "^14.0.0",
|
||||
"jest": "^27.3.1",
|
||||
"mini-css-extract-plugin": "^2.4.5",
|
||||
"path-browserify": "^1.0.1",
|
||||
"raw-loader": "^4.0.2",
|
||||
"sass-loader": "^12.1.0",
|
||||
"simple-git": "^2.45.1",
|
||||
"sass": "^1.43.5",
|
||||
"sass-loader": "^12.3.0",
|
||||
"simple-git": "^2.47.0",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"style-loader": "^3.2.1",
|
||||
"ts-jest": "^27.0.5",
|
||||
"typescript": "^4.4.3",
|
||||
"webpack": "^5.52.1",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-version-file-plugin": "^0.4.0"
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-jest": "^27.0.7",
|
||||
"typescript": "^4.5.2",
|
||||
"webpack": "^5.64.4",
|
||||
"webpack-cli": "^4.9.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/argparse": "^2.0.10",
|
||||
"@types/benchmark": "^2.1.1",
|
||||
"@types/compression": "1.7.2",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/node": "^16.9.1",
|
||||
"@types/node": "^16.11.10",
|
||||
"@types/node-fetch": "^2.5.12",
|
||||
"@types/react": "^17.0.20",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"@types/react": "^17.0.37",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"@types/swagger-ui-dist": "3.30.1",
|
||||
"argparse": "^2.0.1",
|
||||
"body-parser": "^1.19.0",
|
||||
@@ -141,15 +143,20 @@
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"h264-mp4-encoder": "^1.0.12",
|
||||
"immer": "^9.0.6",
|
||||
"immer": "^9.0.7",
|
||||
"immutable": "^3.8.2",
|
||||
"node-fetch": "^2.6.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"rxjs": "^7.3.0",
|
||||
"swagger-ui-dist": "^3.52.1",
|
||||
"rxjs": "^7.4.0",
|
||||
"swagger-ui-dist": "^4.1.1",
|
||||
"tslib": "^2.3.1",
|
||||
"util.promisify": "^1.1.1",
|
||||
"xhr2": "^0.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"gl": "^4.9.2"
|
||||
}
|
||||
}
|
||||
|
||||
41
scripts/clean.js
Normal file
41
scripts/clean.js
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Michal Malý <malym@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function removeDir(dirPath) {
|
||||
for (const ent of fs.readdirSync(dirPath)) {
|
||||
const entryPath = path.join(dirPath, ent);
|
||||
remove(entryPath);
|
||||
}
|
||||
|
||||
fs.rmdirSync(dirPath);
|
||||
}
|
||||
|
||||
function remove(entryPath) {
|
||||
const st = fs.statSync(entryPath);
|
||||
if (st.isDirectory())
|
||||
removeDir(entryPath);
|
||||
else
|
||||
fs.unlinkSync(entryPath);
|
||||
}
|
||||
|
||||
const toClean = [
|
||||
path.resolve(__dirname, '../build'),
|
||||
path.resolve(__dirname, '../lib'),
|
||||
path.resolve(__dirname, '../tsconfig.tsbuildinfo'),
|
||||
];
|
||||
|
||||
toClean.forEach(ph => {
|
||||
if (fs.existsSync(ph)) {
|
||||
try {
|
||||
remove(ph);
|
||||
} catch (err) {
|
||||
console.warn(`Cleanup failed: ${err}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -14,6 +14,9 @@ const buildDir = path.resolve(__dirname, '../build/');
|
||||
const deployDir = path.resolve(buildDir, 'deploy/');
|
||||
const localPath = path.resolve(deployDir, 'molstar.github.io/');
|
||||
|
||||
const analyticsTag = /<!-- __MOLSTAR_ANALYTICS__ -->/g;
|
||||
const analyticsCode = `<!-- Cloudflare Web Analytics --><script defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "c414cbae2d284ea995171a81e4a3e721"}'></script><!-- End Cloudflare Web Analytics -->`;
|
||||
|
||||
function log(command, stdout, stderr) {
|
||||
if (command) {
|
||||
console.log('\n###', command);
|
||||
@@ -22,11 +25,18 @@ function log(command, stdout, stderr) {
|
||||
}
|
||||
}
|
||||
|
||||
function addAnalytics(path) {
|
||||
const data = fs.readFileSync(path, 'utf8');
|
||||
const result = data.replace(analyticsTag, analyticsCode);
|
||||
fs.writeFileSync(path, result, 'utf8');
|
||||
}
|
||||
|
||||
function copyViewer() {
|
||||
console.log('\n###', 'copy viewer files');
|
||||
const viewerBuildPath = path.resolve(buildDir, '../build/viewer/');
|
||||
const viewerDeployPath = path.resolve(localPath, 'viewer/');
|
||||
fse.copySync(viewerBuildPath, viewerDeployPath, { overwrite: true });
|
||||
addAnalytics(path.resolve(viewerDeployPath, 'index.html'));
|
||||
}
|
||||
|
||||
if (!fs.existsSync(localPath)) {
|
||||
|
||||
@@ -21,12 +21,12 @@ import { PluginContext } from '../../mol-plugin/context';
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import { StateObjectRef } from '../../mol-state';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { Material } from '../../mol-util/material';
|
||||
|
||||
function shinyStyle(plugin: PluginContext) {
|
||||
return PluginCommands.Canvas3D.SetSettings(plugin, { settings: {
|
||||
renderer: {
|
||||
...plugin.canvas3d!.props.renderer,
|
||||
style: { name: 'plastic', params: {} },
|
||||
},
|
||||
postprocessing: {
|
||||
...plugin.canvas3d!.props.postprocessing,
|
||||
@@ -40,7 +40,6 @@ function occlusionStyle(plugin: PluginContext) {
|
||||
return PluginCommands.Canvas3D.SetSettings(plugin, { settings: {
|
||||
renderer: {
|
||||
...plugin.canvas3d!.props.renderer,
|
||||
style: { name: 'flat', params: {} }
|
||||
},
|
||||
postprocessing: {
|
||||
...plugin.canvas3d!.props.postprocessing,
|
||||
@@ -52,7 +51,8 @@ function occlusionStyle(plugin: PluginContext) {
|
||||
} },
|
||||
outline: { name: 'on', params: {
|
||||
scale: 1.0,
|
||||
threshold: 0.33
|
||||
threshold: 0.33,
|
||||
color: Color(0x0000),
|
||||
} }
|
||||
}
|
||||
} });
|
||||
@@ -77,7 +77,7 @@ const PresetParams = {
|
||||
...StructureRepresentationPresetProvider.CommonParams,
|
||||
};
|
||||
|
||||
|
||||
const CustomMaterial = Material({ roughness: 0.2, metalness: 0 });
|
||||
|
||||
export const StructurePreset = StructureRepresentationPresetProvider({
|
||||
id: 'preset-structure',
|
||||
@@ -94,8 +94,8 @@ export const StructurePreset = StructureRepresentationPresetProvider({
|
||||
|
||||
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
|
||||
const representations = {
|
||||
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, sizeFactor: 0.35 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
|
||||
polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { ...typeParams }, color: 'chain-id', colorParams: { palette: (plugin.customState as any).colorPalette } }, { tag: 'polymer' }),
|
||||
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, sizeFactor: 0.35 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
|
||||
polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { ...typeParams, material: CustomMaterial }, color: 'chain-id', colorParams: { palette: (plugin.customState as any).colorPalette } }, { tag: 'polymer' }),
|
||||
};
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
@@ -121,8 +121,8 @@ export const IllustrativePreset = StructureRepresentationPresetProvider({
|
||||
|
||||
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
|
||||
const representations = {
|
||||
ligand: builder.buildRepresentation(update, components.ligand, { type: 'spacefill', typeParams: { ...typeParams }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
|
||||
polymer: builder.buildRepresentation(update, components.polymer, { type: 'spacefill', typeParams: { ...typeParams }, color: 'illustrative', colorParams: { palette: (plugin.customState as any).colorPalette } }, { tag: 'polymer' }),
|
||||
ligand: builder.buildRepresentation(update, components.ligand, { type: 'spacefill', typeParams: { ...typeParams, ignoreLight: true }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
|
||||
polymer: builder.buildRepresentation(update, components.polymer, { type: 'spacefill', typeParams: { ...typeParams, ignoreLight: true }, color: 'illustrative', colorParams: { palette: (plugin.customState as any).colorPalette } }, { tag: 'polymer' }),
|
||||
};
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
@@ -149,8 +149,8 @@ const SurfacePreset = StructureRepresentationPresetProvider({
|
||||
|
||||
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
|
||||
const representations = {
|
||||
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, sizeFactor: 0.26 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
|
||||
polymer: builder.buildRepresentation(update, components.polymer, { type: 'molecular-surface', typeParams: { ...typeParams, quality: 'custom', resolution: 0.5, doubleSided: true }, color: 'partial-charge' }, { tag: 'polymer' }),
|
||||
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, sizeFactor: 0.26 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
|
||||
polymer: builder.buildRepresentation(update, components.polymer, { type: 'molecular-surface', typeParams: { ...typeParams, material: CustomMaterial, quality: 'custom', resolution: 0.5, doubleSided: true }, color: 'partial-charge' }, { tag: 'polymer' }),
|
||||
};
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
@@ -177,8 +177,8 @@ const PocketPreset = StructureRepresentationPresetProvider({
|
||||
|
||||
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
|
||||
const representations = {
|
||||
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, sizeFactor: 0.26 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
|
||||
surroundings: builder.buildRepresentation(update, components.surroundings, { type: 'molecular-surface', typeParams: { ...typeParams, includeParent: true, quality: 'custom', resolution: 0.2, doubleSided: true }, color: 'partial-charge' }, { tag: 'surroundings' }),
|
||||
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, sizeFactor: 0.26 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
|
||||
surroundings: builder.buildRepresentation(update, components.surroundings, { type: 'molecular-surface', typeParams: { ...typeParams, material: CustomMaterial, includeParent: true, quality: 'custom', resolution: 0.2, doubleSided: true }, color: 'partial-charge' }, { tag: 'surroundings' }),
|
||||
};
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
@@ -206,10 +206,10 @@ const InteractionsPreset = StructureRepresentationPresetProvider({
|
||||
|
||||
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
|
||||
const representations = {
|
||||
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, 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, 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 }, color: InteractionTypeColorThemeProvider }, { tag: 'interactions' }),
|
||||
label: builder.buildRepresentation(update, components.surroundings, { type: 'label', typeParams: { ...typeParams, background: false, borderWidth: 0.1 }, color: 'uniform', colorParams: { value: Color(0x000000) } }, { tag: 'label' }),
|
||||
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' }),
|
||||
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' }),
|
||||
};
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
emdbProvider: 'rcsb',
|
||||
});
|
||||
viewer.loadPdb('7bv2');
|
||||
viewer.loadEmdb('EMD-30210', { detail: 6 });
|
||||
viewer.loadEmdb('EMD-30210', { detail: 6 });
|
||||
|
||||
// viewer.loadAllModelsOrAssemblyFromUrl('https://cs.litemol.org/5ire/full', 'mmcif', false, { representationParams: { theme: { globalName: 'operator-name' } } })
|
||||
</script>
|
||||
|
||||
@@ -53,6 +53,9 @@
|
||||
var pdbProvider = getParam('pdb-provider', '[^&]+').trim().toLowerCase();
|
||||
var emdbProvider = getParam('emdb-provider', '[^&]+').trim().toLowerCase();
|
||||
var mapProvider = getParam('map-provider', '[^&]+').trim().toLowerCase();
|
||||
var pixelScale = getParam('pixel-scale', '[^&]+').trim();
|
||||
var pickScale = getParam('pick-scale', '[^&]+').trim();
|
||||
var pickPadding = getParam('pick-padding', '[^&]+').trim();
|
||||
var viewer = new molstar.Viewer('app', {
|
||||
layoutShowControls: !hideControls,
|
||||
viewportShowExpand: false,
|
||||
@@ -61,7 +64,10 @@
|
||||
emdbProvider: emdbProvider || 'pdbe',
|
||||
volumeStreamingServer: (mapProvider || 'pdbe') === 'rcsb'
|
||||
? 'https://maps.rcsb.org'
|
||||
: 'https://www.ebi.ac.uk/pdbe/densities'
|
||||
: 'https://www.ebi.ac.uk/pdbe/densities',
|
||||
pixelScale: parseFloat(pixelScale) || 1,
|
||||
pickScale: parseFloat(pickScale) || 0.25,
|
||||
pickPadding: isNaN(parseFloat(pickPadding)) ? 1 : parseFloat(pickPadding),
|
||||
});
|
||||
|
||||
var snapshotId = getParam('snapshot-id', '[^&]+').trim();
|
||||
@@ -85,5 +91,6 @@
|
||||
var emdb = getParam('emdb', '[^&]+').trim();
|
||||
if (emdb) viewer.loadEmdb(emdb);
|
||||
</script>
|
||||
<!-- __MOLSTAR_ANALYTICS__ -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
@@ -9,25 +9,28 @@ import { ANVILMembraneOrientation } from '../../extensions/anvil/behavior';
|
||||
import { CellPack } from '../../extensions/cellpack';
|
||||
import { DnatcoConfalPyramids } from '../../extensions/dnatco';
|
||||
import { G3DFormat, G3dProvider } from '../../extensions/g3d/format';
|
||||
import { Mp4Export } from '../../extensions/mp4-export';
|
||||
import { GeometryExport } from '../../extensions/geo-export';
|
||||
import { Mp4Export } from '../../extensions/mp4-export';
|
||||
import { PDBeStructureQualityReport } from '../../extensions/pdbe';
|
||||
import { RCSBAssemblySymmetry, RCSBValidationReport } from '../../extensions/rcsb';
|
||||
import { DownloadStructure, PdbDownloadProvider } from '../../mol-plugin-state/actions/structure';
|
||||
import { DownloadDensity } from '../../mol-plugin-state/actions/volume';
|
||||
import { PresetTrajectoryHierarchy } from '../../mol-plugin-state/builder/structure/hierarchy-preset';
|
||||
import { StructureRepresentationPresetProvider } from '../../mol-plugin-state/builder/structure/representation-preset';
|
||||
import { DataFormatProvider } from '../../mol-plugin-state/formats/provider';
|
||||
import { BuildInStructureFormat } from '../../mol-plugin-state/formats/structure';
|
||||
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
|
||||
import { BuildInVolumeFormat } from '../../mol-plugin-state/formats/volume';
|
||||
import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers/volume-representation-params';
|
||||
import { PluginStateObject } from '../../mol-plugin-state/objects';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { TrajectoryFromModelAndCoordinates } from '../../mol-plugin-state/transforms/model';
|
||||
import { createPlugin } from '../../mol-plugin-ui';
|
||||
import { PluginUIContext } from '../../mol-plugin-ui/context';
|
||||
import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout';
|
||||
import { DefaultPluginUISpec, PluginUISpec } from '../../mol-plugin-ui/spec';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { PluginConfig } from '../../mol-plugin/config';
|
||||
import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout';
|
||||
import { PluginSpec } from '../../mol-plugin/spec';
|
||||
import { PluginState } from '../../mol-plugin/state';
|
||||
import { StateObjectSelector } from '../../mol-state';
|
||||
@@ -71,9 +74,12 @@ const DefaultViewerOptions = {
|
||||
layoutShowLog: true,
|
||||
layoutShowLeftPanel: true,
|
||||
collapseLeftPanel: false,
|
||||
disableAntialiasing: false,
|
||||
pixelScale: 1,
|
||||
enableWboit: true,
|
||||
collapseRightPanel: false,
|
||||
disableAntialiasing: PluginConfig.General.DisableAntialiasing.defaultValue,
|
||||
pixelScale: PluginConfig.General.PixelScale.defaultValue,
|
||||
pickScale: PluginConfig.General.PickScale.defaultValue,
|
||||
pickPadding: PluginConfig.General.PickPadding.defaultValue,
|
||||
enableWboit: PluginConfig.General.EnableWboit.defaultValue,
|
||||
|
||||
viewportShowExpand: PluginConfig.Viewport.ShowExpand.defaultValue,
|
||||
viewportShowControls: PluginConfig.Viewport.ShowControls.defaultValue,
|
||||
@@ -112,7 +118,7 @@ export class Viewer {
|
||||
regionState: {
|
||||
bottom: 'full',
|
||||
left: o.collapseLeftPanel ? 'collapsed' : 'full',
|
||||
right: 'full',
|
||||
right: o.collapseRightPanel ? 'hidden' : 'full',
|
||||
top: 'full',
|
||||
}
|
||||
},
|
||||
@@ -130,6 +136,8 @@ export class Viewer {
|
||||
config: [
|
||||
[PluginConfig.General.DisableAntialiasing, o.disableAntialiasing],
|
||||
[PluginConfig.General.PixelScale, o.pixelScale],
|
||||
[PluginConfig.General.PickScale, o.pickScale],
|
||||
[PluginConfig.General.PickPadding, o.pickPadding],
|
||||
[PluginConfig.General.EnableWboit, o.enableWboit],
|
||||
[PluginConfig.Viewport.ShowExpand, o.viewportShowExpand],
|
||||
[PluginConfig.Viewport.ShowControls, o.viewportShowControls],
|
||||
@@ -324,6 +332,56 @@ export class Viewer {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @example
|
||||
* viewer.loadTrajectory({
|
||||
* model: { kind: 'model-url', url: 'villin.gro', format: 'gro' },
|
||||
* coordinates: { kind: 'coordinates-url', url: 'villin.xtc', format: 'xtc', isBinary: true },
|
||||
* preset: 'all-models' // or 'default'
|
||||
* });
|
||||
*/
|
||||
async loadTrajectory(params: LoadTrajectoryParams) {
|
||||
const plugin = this.plugin;
|
||||
|
||||
let model: StateObjectSelector, coords: StateObjectSelector;
|
||||
|
||||
if (params.model.kind === 'model-data' || params.model.kind === 'model-url') {
|
||||
const data = params.model.kind === 'model-data'
|
||||
? await plugin.builders.data.rawData({ data: params.model.data, label: params.modelLabel })
|
||||
: await plugin.builders.data.download({ url: params.model.url, isBinary: params.model.isBinary, label: params.modelLabel });
|
||||
|
||||
const trajectory = await plugin.builders.structure.parseTrajectory(data, params.model.format ?? 'mmcif');
|
||||
model = await plugin.builders.structure.createModel(trajectory);
|
||||
} else {
|
||||
const data = params.model.kind === 'topology-data'
|
||||
? await plugin.builders.data.rawData({ data: params.model.data, label: params.modelLabel })
|
||||
: await plugin.builders.data.download({ url: params.model.url, isBinary: params.model.isBinary, label: params.modelLabel });
|
||||
|
||||
const provider = plugin.dataFormats.get(params.model.format);
|
||||
model = await provider!.parse(plugin, data);
|
||||
}
|
||||
|
||||
{
|
||||
const data = params.coordinates.kind === 'coordinates-data'
|
||||
? await plugin.builders.data.rawData({ data: params.coordinates.data, label: params.coordinatesLabel })
|
||||
: await plugin.builders.data.download({ url: params.coordinates.url, isBinary: params.coordinates.isBinary, label: params.coordinatesLabel });
|
||||
|
||||
const provider = plugin.dataFormats.get(params.coordinates.format);
|
||||
coords = await provider!.parse(plugin, data);
|
||||
}
|
||||
|
||||
const trajectory = await plugin.build().toRoot()
|
||||
.apply(TrajectoryFromModelAndCoordinates, {
|
||||
modelRef: model.ref,
|
||||
coordinatesRef: coords.ref
|
||||
}, { dependsOn: [model.ref, coords.ref] })
|
||||
.commit();
|
||||
|
||||
const preset = await plugin.builders.structure.hierarchy.applyPreset(trajectory, params.preset ?? 'default');
|
||||
|
||||
return { model, coords, preset };
|
||||
}
|
||||
|
||||
handleResize() {
|
||||
this.plugin.layout.events.updated.next(void 0);
|
||||
}
|
||||
@@ -339,4 +397,16 @@ export interface VolumeIsovalueInfo {
|
||||
color: Color,
|
||||
alpha?: number,
|
||||
volumeIndex?: number
|
||||
}
|
||||
|
||||
export interface LoadTrajectoryParams {
|
||||
model: { kind: 'model-url', url: string, format?: BuiltInTrajectoryFormat /* mmcif */, isBinary?: boolean }
|
||||
| { kind: 'model-data', data: string | number[] | ArrayBuffer | Uint8Array, format?: BuiltInTrajectoryFormat /* mmcif */ }
|
||||
| { kind: 'topology-url', url: string, format: BuildInStructureFormat, isBinary?: boolean }
|
||||
| { kind: 'topology-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuildInStructureFormat },
|
||||
modelLabel?: string,
|
||||
coordinates: { kind: 'coordinates-url', url: string, format: BuildInStructureFormat, isBinary?: boolean }
|
||||
| { kind: 'coordinates-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuildInStructureFormat },
|
||||
coordinatesLabel?: string,
|
||||
preset?: keyof PresetTrajectoryHierarchy
|
||||
}
|
||||
@@ -84,6 +84,7 @@ export function getFieldType(type: string, description: string, values?: string[
|
||||
case 'DateTime':
|
||||
case 'Tag':
|
||||
case 'Implied':
|
||||
case 'Word':
|
||||
return wrapContainer('str', ',', description, container);
|
||||
case 'Real':
|
||||
return wrapContainer('float', ',', description, container);
|
||||
|
||||
@@ -11,6 +11,7 @@ import { PluginUIContext } from '../../mol-plugin-ui/context';
|
||||
import { DefaultPluginUISpec } from '../../mol-plugin-ui/spec';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import './index.html';
|
||||
require('mol-plugin-ui/skin/light.scss');
|
||||
|
||||
@@ -26,7 +27,7 @@ const Canvas3DPresets = {
|
||||
},
|
||||
postprocessing: {
|
||||
occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15 } },
|
||||
outline: { name: 'on', params: { scale: 1, threshold: 0.1 } }
|
||||
outline: { name: 'on', params: { scale: 1, threshold: 0.1, color: Color(0x000000) } }
|
||||
},
|
||||
renderer: {
|
||||
style: { name: 'flat', params: {} }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Ludovic Autin <autin@scripps.edu>
|
||||
* @author Ludovic Autin <ludovic.autin@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
|
||||
@@ -13,16 +13,27 @@ export interface CellPack {
|
||||
|
||||
export interface CellPacking {
|
||||
name: string,
|
||||
location: 'surface' | 'interior' | 'cytoplasme',
|
||||
location: 'surface' | 'interior' | 'cytoplasme'
|
||||
ingredients: Packing['ingredients']
|
||||
compartment?: CellCompartment
|
||||
}
|
||||
|
||||
//
|
||||
export interface CellCompartment {
|
||||
filename?: string
|
||||
geom_type?: 'raw' | 'file' | 'sphere' | 'mb' | 'None'
|
||||
compartment_primitives?: CompartmentPrimitives
|
||||
}
|
||||
|
||||
export interface Cell {
|
||||
recipe: Recipe
|
||||
options?: RecipeOptions
|
||||
cytoplasme?: Packing
|
||||
compartments?: { [key: string]: Compartment }
|
||||
mapping_ids?: { [key: number]: [number, string] }
|
||||
}
|
||||
|
||||
export interface RecipeOptions {
|
||||
resultfile?: string
|
||||
}
|
||||
|
||||
export interface Recipe {
|
||||
@@ -35,8 +46,29 @@ export interface Recipe {
|
||||
export interface Compartment {
|
||||
surface?: Packing
|
||||
interior?: Packing
|
||||
geom?: unknown
|
||||
geom_type?: 'raw' | 'file' | 'sphere' | 'mb' | 'None'
|
||||
mb?: CompartmentPrimitives
|
||||
}
|
||||
|
||||
// Primitives discribing a compartment
|
||||
export const enum CompartmentPrimitiveType {
|
||||
MetaBall = 0,
|
||||
Sphere = 1,
|
||||
Cube = 2,
|
||||
Cylinder = 3,
|
||||
Cone = 4,
|
||||
Plane = 5,
|
||||
None = 6
|
||||
}
|
||||
|
||||
export interface CompartmentPrimitives{
|
||||
positions?: number[];
|
||||
radii?: number[];
|
||||
types?: CompartmentPrimitiveType[];
|
||||
}
|
||||
|
||||
|
||||
export interface Packing {
|
||||
ingredients: { [key: string]: Ingredient }
|
||||
}
|
||||
@@ -64,11 +96,13 @@ export interface Ingredient {
|
||||
[curveX: string]: unknown;
|
||||
/** the orientation in the membrane */
|
||||
principalAxis?: Vec3;
|
||||
principalVector?: Vec3;
|
||||
/** offset along membrane */
|
||||
offset?: Vec3;
|
||||
ingtype?: string;
|
||||
color?: Vec3;
|
||||
confidence?: number;
|
||||
Type?: string;
|
||||
}
|
||||
|
||||
export interface IngredientSource {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
* Copyright (c) 2019-2021 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>
|
||||
*/
|
||||
|
||||
import { StateAction, StateBuilder, StateTransformer, State } from '../../mol-state';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { PluginStateObject as PSO } from '../../mol-plugin-state/objects';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Ingredient, IngredientSource, CellPacking } from './data';
|
||||
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';
|
||||
@@ -17,7 +18,7 @@ import { Mat4, Vec3, Quat } from '../../mol-math/linear-algebra';
|
||||
import { SymmetryOperator } from '../../mol-math/geometry';
|
||||
import { Task, RuntimeContext } from '../../mol-task';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { ParseCellPack, StructureFromCellpack, DefaultCellPackBaseUrl, StructureFromAssemblies } from './state';
|
||||
import { ParseCellPack, StructureFromCellpack, DefaultCellPackBaseUrl, StructureFromAssemblies, CreateCompartmentSphere } from './state';
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import { getMatFromResamplePoints } from './curve';
|
||||
import { compile } from '../../mol-script/runtime/query/compiler';
|
||||
@@ -28,8 +29,9 @@ 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 { readFromFile } from '../../mol-util/data-source';
|
||||
import { objectForEach } from '../../mol-util/object';
|
||||
import { readFromFile } from '../../mol-util/data-source';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
|
||||
function getCellPackModelUrl(fileName: string, baseUrl: string) {
|
||||
return `${baseUrl}/results/${fileName}`;
|
||||
@@ -41,10 +43,14 @@ class TrajectoryCache {
|
||||
get(id: string) { return this.map.get(id); }
|
||||
}
|
||||
|
||||
async function getModel(plugin: PluginContext, id: string, ingredient: Ingredient, baseUrl: string, trajCache: TrajectoryCache, file?: Asset.File) {
|
||||
async function getModel(plugin: PluginContext, id: string, ingredient: Ingredient,
|
||||
baseUrl: string, trajCache: TrajectoryCache, location: string,
|
||||
file?: Asset.File
|
||||
) {
|
||||
const assetManager = plugin.managers.asset;
|
||||
const modelIndex = (ingredient.source.model) ? parseInt(ingredient.source.model) : 0;
|
||||
const surface = (ingredient.ingtype) ? (ingredient.ingtype === 'transmembrane') : false;
|
||||
let surface = (ingredient.ingtype) ? (ingredient.ingtype === 'transmembrane') : false;
|
||||
if (location === 'surface') surface = true;
|
||||
let trajectory = trajCache.get(id);
|
||||
const assets: Asset.Wrapper[] = [];
|
||||
if (!trajectory) {
|
||||
@@ -72,6 +78,7 @@ async function getModel(plugin: PluginContext, id: string, ingredient: Ingredien
|
||||
try {
|
||||
const data = await getFromOPM(plugin, id, assetManager);
|
||||
assets.push(data.asset);
|
||||
data.pdb.id! = id.toUpperCase();
|
||||
trajectory = await plugin.runTask(trajectoryFromPDB(data.pdb));
|
||||
} catch (e) {
|
||||
// fallback to getFromPdb
|
||||
@@ -100,7 +107,7 @@ async function getModel(plugin: PluginContext, id: string, ingredient: Ingredien
|
||||
return { model, assets };
|
||||
}
|
||||
|
||||
async function getStructure(plugin: PluginContext, model: Model, source: IngredientSource, props: { assembly?: string } = {}) {
|
||||
async function getStructure(plugin: PluginContext, model: Model, source: Ingredient, props: { assembly?: string } = {}) {
|
||||
let structure = Structure.ofModel(model);
|
||||
const { assembly } = props;
|
||||
|
||||
@@ -108,11 +115,12 @@ async function getStructure(plugin: PluginContext, model: Model, source: Ingredi
|
||||
structure = await plugin.runTask(StructureSymmetry.buildAssembly(structure, assembly));
|
||||
}
|
||||
let query;
|
||||
if (source.selection) {
|
||||
const asymIds: string[] = source.selection.replace(' ', '').replace(':', '').split('or');
|
||||
if (source.source.selection) {
|
||||
const sel = source.source.selection;
|
||||
// selection can have the model ID as well. remove it
|
||||
const asymIds: string[] = sel.replace(/ /g, '').replace(/:/g, '').split('or').slice(1);
|
||||
query = MS.struct.modifier.union([
|
||||
MS.struct.generator.atomGroups({
|
||||
'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
|
||||
'chain-test': MS.core.set.has([MS.set(...asymIds), MS.ammp('auth_asym_id')])
|
||||
})
|
||||
]);
|
||||
@@ -123,11 +131,11 @@ async function getStructure(plugin: PluginContext, model: Model, source: Ingredi
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
const compiled = compile<StructureSelection>(query);
|
||||
const result = compiled(new QueryContext(structure));
|
||||
structure = StructureSelection.unionStructure(result);
|
||||
|
||||
// change here if possible the label ?
|
||||
// structure.label = source.name;
|
||||
return structure;
|
||||
}
|
||||
|
||||
@@ -141,9 +149,9 @@ 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 q: Quat = Quat.create(-rot[0], rot[1], rot[2], -rot[3]);
|
||||
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q);
|
||||
const p: Vec3 = Vec3.create(trans[0], trans[1], trans[2]);
|
||||
const p: Vec3 = Vec3.create(-trans[0], trans[1], trans[2]);
|
||||
Mat4.setTranslation(m, p);
|
||||
return m;
|
||||
}
|
||||
@@ -168,7 +176,7 @@ function getCurveTransforms(ingredient: Ingredient) {
|
||||
for (let i = 0; i < n; ++i) {
|
||||
const cname = `curve${i}`;
|
||||
if (!(cname in ingredient)) {
|
||||
// console.warn(`Expected '${cname}' in ingredient`)
|
||||
console.warn(`Expected '${cname}' in ingredient`);
|
||||
continue;
|
||||
}
|
||||
const _points = ingredient[cname] as Vec3[];
|
||||
@@ -179,7 +187,7 @@ function getCurveTransforms(ingredient: Ingredient) {
|
||||
// test for resampling
|
||||
const distance: number = Vec3.distance(_points[0], _points[1]);
|
||||
if (distance >= segmentLength + 2.0) {
|
||||
console.info(distance);
|
||||
// console.info(distance);
|
||||
resampling = true;
|
||||
}
|
||||
const points = new Float32Array(_points.length * 3);
|
||||
@@ -190,8 +198,8 @@ function getCurveTransforms(ingredient: Ingredient) {
|
||||
return instances;
|
||||
}
|
||||
|
||||
function getAssembly(transforms: Mat4[], structure: Structure) {
|
||||
const builder = Structure.Builder();
|
||||
function getAssembly(name: string, transforms: Mat4[], structure: Structure) {
|
||||
const builder = Structure.Builder({ label: name });
|
||||
const { units } = structure;
|
||||
|
||||
for (let i = 0, il = transforms.length; i < il; ++i) {
|
||||
@@ -307,13 +315,13 @@ async function getCurve(plugin: PluginContext, name: string, ingredient: Ingredi
|
||||
});
|
||||
|
||||
const curveModel = await plugin.runTask(curveModelTask);
|
||||
return getStructure(plugin, curveModel, ingredient.source);
|
||||
// ingredient.source.selection = undefined;
|
||||
return getStructure(plugin, curveModel, ingredient);
|
||||
}
|
||||
|
||||
async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredient, baseUrl: string, ingredientFiles: IngredientFiles, trajCache: TrajectoryCache) {
|
||||
async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredient, baseUrl: string, ingredientFiles: IngredientFiles, trajCache: TrajectoryCache, location: 'surface' | 'interior' | 'cytoplasme') {
|
||||
const { name, source, results, nbCurve } = ingredient;
|
||||
if (source.pdb === 'None') return;
|
||||
|
||||
const file = ingredientFiles[source.pdb];
|
||||
if (!file) {
|
||||
// TODO can these be added to the library?
|
||||
@@ -325,13 +333,13 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
|
||||
}
|
||||
|
||||
// model id in case structure is NMR
|
||||
const { model, assets } = await getModel(plugin, source.pdb || name, ingredient, baseUrl, trajCache, file);
|
||||
const { model, assets } = await getModel(plugin, source.pdb || name, ingredient, baseUrl, trajCache, location, file);
|
||||
if (!model) return;
|
||||
|
||||
let structure: Structure;
|
||||
if (nbCurve) {
|
||||
structure = await getCurve(plugin, name, ingredient, getCurveTransforms(ingredient), model);
|
||||
} else {
|
||||
if ((!results || results.length === 0)) return;
|
||||
let bu: string|undefined = source.bu ? source.bu : undefined;
|
||||
if (bu) {
|
||||
if (bu === 'AU') {
|
||||
@@ -340,10 +348,11 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
|
||||
bu = bu.slice(2);
|
||||
}
|
||||
}
|
||||
structure = await getStructure(plugin, model, source, { assembly: bu });
|
||||
structure = await getStructure(plugin, model, ingredient, { assembly: bu });
|
||||
// transform with offset and pcp
|
||||
let legacy: boolean = true;
|
||||
if (ingredient.offset || ingredient.principalAxis) {
|
||||
const pcp = ingredient.principalVector ? ingredient.principalVector : ingredient.principalAxis;
|
||||
if (pcp) {
|
||||
legacy = false;
|
||||
const structureMean = getStructureMean(structure);
|
||||
Vec3.negate(structureMean, structureMean);
|
||||
@@ -351,38 +360,44 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
|
||||
Mat4.setTranslation(m1, structureMean);
|
||||
structure = Structure.transform(structure, m1);
|
||||
if (ingredient.offset) {
|
||||
if (!Vec3.exactEquals(ingredient.offset, Vec3.zero())) {
|
||||
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 (location !== 'surface') {
|
||||
Vec3.negate(o, o);
|
||||
}
|
||||
const m: Mat4 = Mat4.identity();
|
||||
Mat4.setTranslation(m, ingredient.offset);
|
||||
Mat4.setTranslation(m, o);
|
||||
structure = Structure.transform(structure, m);
|
||||
}
|
||||
}
|
||||
if (ingredient.principalAxis) {
|
||||
if (!Vec3.exactEquals(ingredient.principalAxis, Vec3.unitZ)) {
|
||||
if (pcp) {
|
||||
const p: Vec3 = Vec3.create(pcp[0], pcp[1], pcp[2]);
|
||||
if (!Vec3.exactEquals(p, Vec3.unitZ)) {
|
||||
const q: Quat = Quat.identity();
|
||||
Quat.rotationTo(q, ingredient.principalAxis, Vec3.unitZ);
|
||||
Quat.rotationTo(q, p, Vec3.unitZ);
|
||||
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q);
|
||||
structure = Structure.transform(structure, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
structure = getAssembly(getResultTransforms(results, legacy), structure);
|
||||
|
||||
structure = getAssembly(name, getResultTransforms(results, legacy), structure);
|
||||
}
|
||||
|
||||
return { structure, assets };
|
||||
}
|
||||
|
||||
|
||||
export function createStructureFromCellPack(plugin: PluginContext, packing: CellPacking, baseUrl: string, ingredientFiles: IngredientFiles) {
|
||||
return Task.create('Create Packing Structure', async ctx => {
|
||||
const { ingredients, name } = packing;
|
||||
const { ingredients, location, name } = packing;
|
||||
const assets: Asset.Wrapper[] = [];
|
||||
const trajCache = new TrajectoryCache();
|
||||
const structures: Structure[] = [];
|
||||
const colors: Color[] = [];
|
||||
let skipColors: boolean = false;
|
||||
for (const iName in ingredients) {
|
||||
if (ctx.shouldUpdate) await ctx.update(iName);
|
||||
const ingredientStructure = await getIngredientStructure(plugin, ingredients[iName], baseUrl, ingredientFiles, trajCache);
|
||||
const ingredientStructure = await getIngredientStructure(plugin, ingredients[iName], baseUrl, ingredientFiles, trajCache, location);
|
||||
if (ingredientStructure) {
|
||||
structures.push(ingredientStructure.structure);
|
||||
assets.push(...ingredientStructure.assets);
|
||||
@@ -390,7 +405,7 @@ export function createStructureFromCellPack(plugin: PluginContext, packing: Cell
|
||||
if (c) {
|
||||
colors.push(Color.fromNormalizedRgb(c[0], c[1], c[2]));
|
||||
} else {
|
||||
skipColors = true;
|
||||
colors.push(Color.fromNormalizedRgb(1, 0, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -414,21 +429,20 @@ export function createStructureFromCellPack(plugin: PluginContext, packing: Cell
|
||||
}
|
||||
|
||||
if (ctx.shouldUpdate) await ctx.update(`${name} - structure`);
|
||||
const structure = Structure.create(units);
|
||||
const structure = Structure.create(units, { label: name + '.' + location });
|
||||
for (let i = 0, il = structure.models.length; i < il; ++i) {
|
||||
Model.TrajectoryInfo.set(structure.models[i], { size: il, index: i });
|
||||
}
|
||||
return { structure, assets, colors: skipColors ? undefined : colors };
|
||||
return { structure, assets, colors: colors };
|
||||
});
|
||||
}
|
||||
|
||||
async function handleHivRna(plugin: PluginContext, packings: CellPacking[], baseUrl: string) {
|
||||
for (let i = 0, il = packings.length; i < il; ++i) {
|
||||
if (packings[i].name === 'HIV1_capsid_3j3q_PackInner_0_1_0') {
|
||||
if (packings[i].name === 'HIV1_capsid_3j3q_PackInner_0_1_0' || packings[i].name === 'HIV_capsid') {
|
||||
const url = Asset.getUrlAsset(plugin.managers.asset, `${baseUrl}/extras/rna_allpoints.json`);
|
||||
const json = await plugin.runTask(plugin.managers.asset.resolve(url, 'json', false));
|
||||
const points = json.data.points as number[];
|
||||
|
||||
const curve0: Vec3[] = [];
|
||||
for (let j = 0, jl = points.length; j < jl; j += 3) {
|
||||
curve0.push(Vec3.fromArray(Vec3(), points, j));
|
||||
@@ -465,7 +479,8 @@ async function loadMembrane(plugin: PluginContext, name: string, state: State, p
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let legacy_membrane: boolean = false; // temporary variable until all membrane are converted to the new correct cif format
|
||||
let geometry_membrane: boolean = false; // membrane can be a mesh geometry
|
||||
let b = state.build().toRoot();
|
||||
if (file) {
|
||||
if (file.name.endsWith('.cif')) {
|
||||
@@ -474,27 +489,82 @@ async function loadMembrane(plugin: PluginContext, name: string, state: State, p
|
||||
b = b.apply(StateTransforms.Data.ReadFile, { file, isBinary: true, label: file.name }, { state: { isGhost: true } });
|
||||
}
|
||||
} else {
|
||||
const url = Asset.getUrlAsset(plugin.managers.asset, `${params.baseUrl}/membranes/${name}.bcif`);
|
||||
b = b.apply(StateTransforms.Data.Download, { url, isBinary: true, label: name }, { state: { isGhost: true } });
|
||||
if (name.toLowerCase().endsWith('.bcif')) {
|
||||
const url = Asset.getUrlAsset(plugin.managers.asset, `${params.baseUrl}/membranes/${name}`);
|
||||
b = b.apply(StateTransforms.Data.Download, { url, isBinary: true, label: name }, { state: { isGhost: true } });
|
||||
} else if (name.toLowerCase().endsWith('.cif')) {
|
||||
const url = Asset.getUrlAsset(plugin.managers.asset, `${params.baseUrl}/membranes/${name}`);
|
||||
b = b.apply(StateTransforms.Data.Download, { url, isBinary: false, label: name }, { state: { isGhost: true } });
|
||||
} else if (name.toLowerCase().endsWith('.ply')) {
|
||||
const url = Asset.getUrlAsset(plugin.managers.asset, `${params.baseUrl}/geometries/${name}`);
|
||||
b = b.apply(StateTransforms.Data.Download, { url, isBinary: false, label: name }, { state: { isGhost: true } });
|
||||
geometry_membrane = true;
|
||||
} else {
|
||||
const url = Asset.getUrlAsset(plugin.managers.asset, `${params.baseUrl}/membranes/${name}.bcif`);
|
||||
b = b.apply(StateTransforms.Data.Download, { url, isBinary: true, label: name }, { state: { isGhost: true } });
|
||||
legacy_membrane = true;
|
||||
}
|
||||
}
|
||||
|
||||
const membrane = await b.apply(StateTransforms.Data.ParseCif, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.TrajectoryFromMmCif, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.ModelFromTrajectory, undefined, { state: { isGhost: true } })
|
||||
.apply(StructureFromAssemblies, undefined, { state: { isGhost: true } })
|
||||
.commit({ revertOnError: true });
|
||||
|
||||
const membraneParams = {
|
||||
representation: params.preset.representation,
|
||||
const props = {
|
||||
type: {
|
||||
name: 'assembly' as const,
|
||||
params: { id: '1' }
|
||||
}
|
||||
};
|
||||
if (legacy_membrane) {
|
||||
// old membrane
|
||||
const membrane = await b.apply(StateTransforms.Data.ParseCif, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.TrajectoryFromMmCif, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.ModelFromTrajectory, undefined, { state: { isGhost: true } })
|
||||
.apply(StructureFromAssemblies, undefined, { state: { isGhost: true } })
|
||||
.commit({ revertOnError: true });
|
||||
const membraneParams = {
|
||||
representation: params.preset.representation,
|
||||
};
|
||||
await CellpackMembranePreset.apply(membrane, membraneParams, plugin);
|
||||
} else if (geometry_membrane) {
|
||||
await b.apply(StateTransforms.Data.ParsePly, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.ShapeFromPly)
|
||||
.apply(StateTransforms.Representation.ShapeRepresentation3D, { xrayShaded: true,
|
||||
doubleSided: true, coloring: { name: 'uniform', params: { color: ColorNames.orange } } })
|
||||
.commit({ revertOnError: true });
|
||||
} else {
|
||||
const membrane = await b.apply(StateTransforms.Data.ParseCif, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.TrajectoryFromMmCif, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.ModelFromTrajectory, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.StructureFromModel, props, { state: { isGhost: true } })
|
||||
.commit({ revertOnError: true });
|
||||
const membraneParams = {
|
||||
representation: params.preset.representation,
|
||||
};
|
||||
await CellpackMembranePreset.apply(membrane, membraneParams, plugin);
|
||||
}
|
||||
}
|
||||
|
||||
await CellpackMembranePreset.apply(membrane, membraneParams, plugin);
|
||||
async function handleMembraneSpheres(state: State, primitives: CompartmentPrimitives) {
|
||||
const nSpheres = primitives.positions!.length / 3;
|
||||
// console.log('ok mb ', nSpheres);
|
||||
// TODO : take in account the type of the primitives.
|
||||
for (let j = 0; j < nSpheres; j++) {
|
||||
await state.build()
|
||||
.toRoot()
|
||||
.apply(CreateCompartmentSphere, {
|
||||
center: Vec3.create(
|
||||
primitives.positions![j * 3 + 0],
|
||||
primitives.positions![j * 3 + 1],
|
||||
primitives.positions![j * 3 + 2]
|
||||
),
|
||||
radius: primitives!.radii![j]
|
||||
})
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
|
||||
async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, state: State, params: LoadCellPackModelParams) {
|
||||
const ingredientFiles = params.ingredients || [];
|
||||
|
||||
let cellPackJson: StateBuilder.To<PSO.Format.Json, StateTransformer<PSO.Data.String, PSO.Format.Json>>;
|
||||
let resultsFile: Asset.File | null = params.results;
|
||||
if (params.source.name === 'id') {
|
||||
const url = Asset.getUrlAsset(plugin.managers.asset, getCellPackModelUrl(params.source.params, params.baseUrl));
|
||||
cellPackJson = state.build().toRoot()
|
||||
@@ -506,29 +576,36 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
|
||||
return;
|
||||
}
|
||||
|
||||
let jsonFile: Asset.File;
|
||||
let modelFile: Asset.File;
|
||||
if (file.name.toLowerCase().endsWith('.zip')) {
|
||||
const data = await readFromFile(file.file, 'zip').runInContext(runtime);
|
||||
jsonFile = Asset.File(new File([data['model.json']], 'model.json'));
|
||||
if (data['model.json']) {
|
||||
modelFile = Asset.File(new File([data['model.json']], 'model.json'));
|
||||
} else {
|
||||
throw new Error('model.json missing from zip file');
|
||||
}
|
||||
if (data['results.bin']) {
|
||||
resultsFile = Asset.File(new File([data['results.bin']], 'results.bin'));
|
||||
}
|
||||
objectForEach(data, (v, k) => {
|
||||
if (k === 'model.json') return;
|
||||
if (k === 'results.bin') return;
|
||||
ingredientFiles.push(Asset.File(new File([v], k)));
|
||||
});
|
||||
} else {
|
||||
jsonFile = file;
|
||||
modelFile = file;
|
||||
}
|
||||
|
||||
cellPackJson = state.build().toRoot()
|
||||
.apply(StateTransforms.Data.ReadFile, { file: jsonFile, isBinary: false, label: jsonFile.name }, { state: { isGhost: true } });
|
||||
.apply(StateTransforms.Data.ReadFile, { file: modelFile, isBinary: false, label: modelFile.name }, { state: { isGhost: true } });
|
||||
}
|
||||
|
||||
const cellPackBuilder = cellPackJson
|
||||
.apply(StateTransforms.Data.ParseJson, undefined, { state: { isGhost: true } })
|
||||
.apply(ParseCellPack);
|
||||
.apply(ParseCellPack, { resultsFile, baseUrl: params.baseUrl });
|
||||
|
||||
const cellPackObject = await state.updateTree(cellPackBuilder).runInContext(runtime);
|
||||
const { packings } = cellPackObject.obj!.data;
|
||||
|
||||
const { packings } = cellPackObject.obj!.data;
|
||||
await handleHivRna(plugin, packings, params.baseUrl);
|
||||
|
||||
for (let i = 0, il = packings.length; i < il; ++i) {
|
||||
@@ -544,8 +621,30 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
|
||||
representation: params.preset.representation,
|
||||
};
|
||||
await CellpackPackingPreset.apply(packing, packingParams, plugin);
|
||||
if (packings[i].location === 'surface' && params.membrane) {
|
||||
await loadMembrane(plugin, packings[i].name, state, params);
|
||||
if (packings[i].compartment) {
|
||||
if (params.membrane === 'lipids') {
|
||||
if (packings[i].compartment!.geom_type) {
|
||||
if (packings[i].compartment!.geom_type === 'file') {
|
||||
// TODO: load mesh files or vertex,faces data
|
||||
await loadMembrane(plugin, packings[i].compartment!.filename!, state, params);
|
||||
} else if (packings[i].compartment!.compartment_primitives) {
|
||||
await handleMembraneSpheres(state, packings[i].compartment!.compartment_primitives!);
|
||||
}
|
||||
} else {
|
||||
// try loading membrane from repo as a bcif file or from the given list of files.
|
||||
if (params.membrane === 'lipids') {
|
||||
await loadMembrane(plugin, packings[i].name, state, params);
|
||||
}
|
||||
}
|
||||
} else if (params.membrane === 'geometry') {
|
||||
if (packings[i].compartment!.compartment_primitives) {
|
||||
await handleMembraneSpheres(state, packings[i].compartment!.compartment_primitives!);
|
||||
} else if (packings[i].compartment!.geom_type === 'file') {
|
||||
if (packings[i].compartment!.filename!.toLowerCase().endsWith('.ply')) {
|
||||
await loadMembrane(plugin, packings[i].compartment!.filename!, state, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -555,19 +654,19 @@ const LoadCellPackModelParams = {
|
||||
'id': PD.Select('InfluenzaModel2.json', [
|
||||
['blood_hiv_immature_inside.json', 'Blood HIV immature'],
|
||||
['HIV_immature_model.json', 'HIV immature'],
|
||||
['BloodHIV1.0_mixed_fixed_nc1.cpr', 'Blood HIV'],
|
||||
['HIV-1_0.1.6-8_mixed_radii_pdb.cpr', 'HIV'],
|
||||
['Blood_HIV.json', 'Blood HIV'],
|
||||
['HIV-1_0.1.6-8_mixed_radii_pdb.json', 'HIV'],
|
||||
['influenza_model1.json', 'Influenza envelope'],
|
||||
['InfluenzaModel2.json', 'Influenza Complete'],
|
||||
['InfluenzaModel2.json', 'Influenza complete'],
|
||||
['ExosomeModel.json', 'Exosome Model'],
|
||||
['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma simple'],
|
||||
['MycoplasmaModel.json', 'Mycoplasma WholeCell model'],
|
||||
['MycoplasmaGenitalium.json', 'Mycoplasma Genitalium curated model'],
|
||||
] as const, { description: 'Download the model definition with `id` from the server at `baseUrl.`' }),
|
||||
'file': PD.File({ accept: '.json,.cpr,.zip', description: 'Open model definition from .json/.cpr file or open .zip file containing model definition plus ingredients.' }),
|
||||
'file': PD.File({ accept: '.json,.cpr,.zip', description: 'Open model definition from .json/.cpr file or open .zip file containing model definition plus ingredients.', label: 'Recipe file' }),
|
||||
}, { options: [['id', 'Id'], ['file', 'File']] }),
|
||||
baseUrl: PD.Text(DefaultCellPackBaseUrl),
|
||||
membrane: PD.Boolean(true),
|
||||
ingredients: PD.FileList({ accept: '.cif,.bcif,.pdb', label: 'Ingredients' }),
|
||||
results: PD.File({ accept: '.bin', description: 'open results file in binary format from cellpackgpu for the specified recipe', label: 'Results file' }),
|
||||
membrane: PD.Select('lipids', PD.arrayToOptions(['lipids', 'geometry', 'none'])),
|
||||
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']))
|
||||
@@ -581,4 +680,4 @@ export const LoadCellPackModel = StateAction.build({
|
||||
from: PSO.Root
|
||||
})(({ state, params }, ctx: PluginContext) => Task.create('CellPack Loader', async taskCtx => {
|
||||
await loadPackings(ctx, taskCtx, state, params);
|
||||
}));
|
||||
}));
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2021 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>
|
||||
*/
|
||||
|
||||
import { StateObjectRef } from '../../mol-state';
|
||||
@@ -9,8 +10,6 @@ import { StructureRepresentationPresetProvider, presetStaticComponent } from '..
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { CellPackGenerateColorThemeProvider } from './color/generate';
|
||||
import { CellPackInfoProvider } from './property';
|
||||
import { CellPackProvidedColorThemeProvider } from './color/provided';
|
||||
|
||||
export const CellpackPackingPresetParams = {
|
||||
traceOnly: PD.Boolean(true),
|
||||
@@ -42,8 +41,8 @@ export const CellpackPackingPreset = StructureRepresentationPresetProvider({
|
||||
Object.assign(reprProps, { sizeFactor: 2 });
|
||||
}
|
||||
|
||||
const info = structureCell.obj?.data && CellPackInfoProvider.get(structureCell.obj?.data).value;
|
||||
const color = info?.colors ? CellPackProvidedColorThemeProvider.name : CellPackGenerateColorThemeProvider.name;
|
||||
// default is generated
|
||||
const color = CellPackGenerateColorThemeProvider.name;
|
||||
|
||||
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, {});
|
||||
const representations = {
|
||||
@@ -92,4 +91,4 @@ export const CellpackMembranePreset = StructureRepresentationPresetProvider({
|
||||
|
||||
return { components, representations };
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -34,4 +34,4 @@ export const CellPackInfoProvider: CustomStructureProperty.Provider<typeof CellP
|
||||
value: { ...CellPackInfoParams.info.defaultValue, ...props.info }
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
70
src/extensions/cellpack/representation.ts
Normal file
70
src/extensions/cellpack/representation.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2021 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>
|
||||
*/
|
||||
|
||||
import { ShapeRepresentation } from '../../mol-repr/shape/representation';
|
||||
import { Shape } from '../../mol-model/shape';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { RuntimeContext } from '../../mol-task';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
|
||||
import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
|
||||
// import { Polyhedron, DefaultPolyhedronProps } from '../../mol-geo/primitive/polyhedron';
|
||||
// import { Icosahedron } from '../../mol-geo/primitive/icosahedron';
|
||||
import { Sphere } from '../../mol-geo/primitive/sphere';
|
||||
import { Mat4, Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { RepresentationParamsGetter, Representation, RepresentationContext } from '../../mol-repr/representation';
|
||||
|
||||
|
||||
interface MembraneSphereData {
|
||||
radius: number
|
||||
center: Vec3
|
||||
}
|
||||
|
||||
|
||||
const MembraneSphereParams = {
|
||||
...Mesh.Params,
|
||||
cellColor: PD.Color(ColorNames.orange),
|
||||
cellScale: PD.Numeric(2, { min: 0.1, max: 5, step: 0.1 }),
|
||||
radius: PD.Numeric(2, { min: 0.1, max: 5, step: 0.1 }),
|
||||
center: PD.Vec3(Vec3.create(0, 0, 0)),
|
||||
quality: { ...Mesh.Params.quality, isEssential: false },
|
||||
};
|
||||
|
||||
type MeshParams = typeof MembraneSphereParams
|
||||
|
||||
const MembraneSphereVisuals = {
|
||||
'mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<MembraneSphereData, MeshParams>) => ShapeRepresentation(getMBShape, Mesh.Utils),
|
||||
};
|
||||
|
||||
export const MBParams = {
|
||||
...MembraneSphereParams
|
||||
};
|
||||
export type MBParams = typeof MBParams
|
||||
export type UnitcellProps = PD.Values<MBParams>
|
||||
|
||||
function getMBMesh(data: MembraneSphereData, props: UnitcellProps, mesh?: Mesh) {
|
||||
const state = MeshBuilder.createState(256, 128, mesh);
|
||||
const radius = props.radius;
|
||||
const asphere = Sphere(3);
|
||||
const trans: Mat4 = Mat4.identity();
|
||||
Mat4.fromScaling(trans, Vec3.create(radius, radius, radius));
|
||||
state.currentGroup = 1;
|
||||
MeshBuilder.addPrimitive(state, trans, asphere);
|
||||
const m = MeshBuilder.getMesh(state);
|
||||
return m;
|
||||
}
|
||||
|
||||
function getMBShape(ctx: RuntimeContext, data: MembraneSphereData, props: UnitcellProps, shape?: Shape<Mesh>) {
|
||||
const geo = getMBMesh(data, props, shape && shape.geometry);
|
||||
const label = 'mb';
|
||||
return Shape.create(label, data, geo, () => props.cellColor, () => 1, () => label);
|
||||
}
|
||||
|
||||
export type MBRepresentation = Representation<MembraneSphereData, MBParams>
|
||||
export function MBRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<MembraneSphereData, MBParams>): MBRepresentation {
|
||||
return Representation.createMulti('MB', ctx, getParams, Representation.StateBuilder, MembraneSphereVisuals as unknown as Representation.Def<MembraneSphereData, MBParams>);
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2021 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>
|
||||
*/
|
||||
|
||||
import { PluginStateObject as PSO, PluginStateTransform } from '../../mol-plugin-state/objects';
|
||||
@@ -15,9 +16,13 @@ import { PluginContext } from '../../mol-plugin/context';
|
||||
import { CellPackInfoProvider } from './property';
|
||||
import { Structure, StructureSymmetry, Unit, Model } from '../../mol-model/structure';
|
||||
import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
|
||||
import { Vec3, Quat } from '../../mol-math/linear-algebra';
|
||||
import { StateTransformer } from '../../mol-state';
|
||||
import { MBRepresentation, MBParams } from './representation';
|
||||
import { IsNativeEndianLittle, flipByteOrder } from '../../mol-io/common/binary';
|
||||
import { getFloatValue } from './util';
|
||||
|
||||
export const DefaultCellPackBaseUrl = 'https://mesoscope.scripps.edu/data/cellPACK_data/cellPACK_database_1.1.0/';
|
||||
|
||||
export const DefaultCellPackBaseUrl = 'https://raw.githubusercontent.com/mesoscope/cellPACK_data/master/cellPACK_database_1.1.0';
|
||||
export class CellPack extends PSO.Create<_CellPack>({ name: 'CellPack', typeClass: 'Object' }) { }
|
||||
|
||||
export { ParseCellPack };
|
||||
@@ -26,26 +31,173 @@ const ParseCellPack = PluginStateTransform.BuiltIn({
|
||||
name: 'parse-cellpack',
|
||||
display: { name: 'Parse CellPack', description: 'Parse CellPack from JSON data' },
|
||||
from: PSO.Format.Json,
|
||||
to: CellPack
|
||||
to: CellPack,
|
||||
params: a => {
|
||||
return {
|
||||
resultsFile: PD.File({ accept: '.bin' }),
|
||||
baseUrl: PD.Text(DefaultCellPackBaseUrl)
|
||||
};
|
||||
}
|
||||
})({
|
||||
apply({ a }) {
|
||||
apply({ a, params, cache }, plugin: PluginContext) {
|
||||
return Task.create('Parse CellPack', async ctx => {
|
||||
const cell = a.data as Cell;
|
||||
|
||||
let counter_id = 0;
|
||||
let fiber_counter_id = 0;
|
||||
let comp_counter = 0;
|
||||
const packings: CellPacking[] = [];
|
||||
const { compartments, cytoplasme } = cell;
|
||||
if (!cell.mapping_ids) cell.mapping_ids = {};
|
||||
if (cytoplasme) {
|
||||
packings.push({ name: 'Cytoplasme', location: 'cytoplasme', ingredients: cytoplasme.ingredients });
|
||||
for (const iName in cytoplasme.ingredients) {
|
||||
if (cytoplasme.ingredients[iName].ingtype === 'fiber') {
|
||||
cell.mapping_ids[-(fiber_counter_id + 1)] = [comp_counter, iName];
|
||||
if (!cytoplasme.ingredients[iName].nbCurve) cytoplasme.ingredients[iName].nbCurve = 0;
|
||||
fiber_counter_id++;
|
||||
} else {
|
||||
cell.mapping_ids[counter_id] = [comp_counter, iName];
|
||||
if (!cytoplasme.ingredients[iName].results) { cytoplasme.ingredients[iName].results = []; }
|
||||
counter_id++;
|
||||
}
|
||||
}
|
||||
comp_counter++;
|
||||
}
|
||||
if (compartments) {
|
||||
for (const name in compartments) {
|
||||
const { surface, interior } = compartments[name];
|
||||
if (surface) packings.push({ name, location: 'surface', ingredients: surface.ingredients });
|
||||
if (interior) packings.push({ name, location: 'interior', ingredients: interior.ingredients });
|
||||
let filename = '';
|
||||
if (compartments[name].geom_type === 'file') {
|
||||
filename = (compartments[name].geom) ? compartments[name].geom as string : '';
|
||||
}
|
||||
const compartment = { filename: filename, geom_type: compartments[name].geom_type, compartment_primitives: compartments[name].mb };
|
||||
if (surface) {
|
||||
packings.push({ name, location: 'surface', ingredients: surface.ingredients, compartment: compartment });
|
||||
for (const iName in surface.ingredients) {
|
||||
if (surface.ingredients[iName].ingtype === 'fiber') {
|
||||
cell.mapping_ids[-(fiber_counter_id + 1)] = [comp_counter, iName];
|
||||
if (!surface.ingredients[iName].nbCurve) surface.ingredients[iName].nbCurve = 0;
|
||||
fiber_counter_id++;
|
||||
} else {
|
||||
cell.mapping_ids[counter_id] = [comp_counter, iName];
|
||||
if (!surface.ingredients[iName].results) { surface.ingredients[iName].results = []; }
|
||||
counter_id++;
|
||||
}
|
||||
}
|
||||
comp_counter++;
|
||||
}
|
||||
if (interior) {
|
||||
if (!surface) packings.push({ name, location: 'interior', ingredients: interior.ingredients, compartment: compartment });
|
||||
else packings.push({ name, location: 'interior', ingredients: interior.ingredients });
|
||||
for (const iName in interior.ingredients) {
|
||||
if (interior.ingredients[iName].ingtype === 'fiber') {
|
||||
cell.mapping_ids[-(fiber_counter_id + 1)] = [comp_counter, iName];
|
||||
if (!interior.ingredients[iName].nbCurve) interior.ingredients[iName].nbCurve = 0;
|
||||
fiber_counter_id++;
|
||||
} else {
|
||||
cell.mapping_ids[counter_id] = [comp_counter, iName];
|
||||
if (!interior.ingredients[iName].results) { interior.ingredients[iName].results = []; }
|
||||
counter_id++;
|
||||
}
|
||||
}
|
||||
comp_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cytoplasme) packings.push({ name: 'Cytoplasme', location: 'cytoplasme', ingredients: cytoplasme.ingredients });
|
||||
const { options } = cell;
|
||||
let resultsAsset: Asset.Wrapper<'binary'> | undefined;
|
||||
if (params.resultsFile) {
|
||||
resultsAsset = await plugin.runTask(plugin.managers.asset.resolve(params.resultsFile, 'binary', true));
|
||||
} else if (options?.resultfile) {
|
||||
const url = `${params.baseUrl}/results/${options.resultfile}`;
|
||||
resultsAsset = await plugin.runTask(plugin.managers.asset.resolve(Asset.getUrlAsset(plugin.managers.asset, url), 'binary', true));
|
||||
}
|
||||
if (resultsAsset) {
|
||||
(cache as any).asset = resultsAsset;
|
||||
const results = resultsAsset.data;
|
||||
// flip the byte order if needed
|
||||
const buffer = IsNativeEndianLittle ? results.buffer : flipByteOrder(results, 4);
|
||||
const numbers = new DataView(buffer);
|
||||
const ninst = getFloatValue(numbers, 0);
|
||||
const npoints = getFloatValue(numbers, 4);
|
||||
const ncurve = getFloatValue(numbers, 8);
|
||||
|
||||
let offset = 12;
|
||||
|
||||
if (ninst !== 0) {
|
||||
const pos = new Float32Array(buffer, offset, ninst * 4);
|
||||
offset += ninst * 4 * 4;
|
||||
const quat = new Float32Array(buffer, offset, ninst * 4);
|
||||
offset += ninst * 4 * 4;
|
||||
|
||||
for (let i = 0; i < ninst; i++) {
|
||||
const x: number = pos[i * 4 + 0];
|
||||
const y: number = pos[i * 4 + 1];
|
||||
const z: number = pos[i * 4 + 2];
|
||||
const ingr_id = pos[i * 4 + 3] as number;
|
||||
const pid = cell.mapping_ids![ingr_id];
|
||||
if (!packings[pid[0]].ingredients[pid[1]].results) {
|
||||
packings[pid[0]].ingredients[pid[1]].results = [];
|
||||
}
|
||||
packings[pid[0]].ingredients[pid[1]].results.push([Vec3.create(x, y, z),
|
||||
Quat.create(quat[i * 4 + 0], quat[i * 4 + 1], quat[i * 4 + 2], quat[i * 4 + 3])]);
|
||||
}
|
||||
}
|
||||
|
||||
if (npoints !== 0) {
|
||||
const ctr_pos = new Float32Array(buffer, offset, npoints * 4);
|
||||
offset += npoints * 4 * 4;
|
||||
offset += npoints * 4 * 4;
|
||||
const ctr_info = new Float32Array(buffer, offset, npoints * 4);
|
||||
offset += npoints * 4 * 4;
|
||||
const curve_ids = new Float32Array(buffer, offset, ncurve * 4);
|
||||
offset += ncurve * 4 * 4;
|
||||
|
||||
let counter = 0;
|
||||
let ctr_points: Vec3[] = [];
|
||||
let prev_ctype = 0;
|
||||
let prev_cid = 0;
|
||||
|
||||
for (let i = 0; i < npoints; i++) {
|
||||
const x: number = -ctr_pos[i * 4 + 0];
|
||||
const y: number = ctr_pos[i * 4 + 1];
|
||||
const z: number = ctr_pos[i * 4 + 2];
|
||||
const cid: number = ctr_info[i * 4 + 0]; // curve id
|
||||
const ctype: number = curve_ids[cid * 4 + 0]; // curve type
|
||||
// cid 148 165 -1 0
|
||||
// console.log("cid ",cid,ctype,prev_cid,prev_ctype);//165,148
|
||||
if (prev_ctype !== ctype) {
|
||||
const pid = cell.mapping_ids![-prev_ctype - 1];
|
||||
const cname = `curve${counter}`;
|
||||
packings[pid[0]].ingredients[pid[1]].nbCurve = counter + 1;
|
||||
packings[pid[0]].ingredients[pid[1]][cname] = ctr_points;
|
||||
ctr_points = [];
|
||||
counter = 0;
|
||||
} else if (prev_cid !== cid) {
|
||||
ctr_points = [];
|
||||
const pid = cell.mapping_ids![-prev_ctype - 1];
|
||||
const cname = `curve${counter}`;
|
||||
packings[pid[0]].ingredients[pid[1]][cname] = ctr_points;
|
||||
counter += 1;
|
||||
}
|
||||
ctr_points.push(Vec3.create(x, y, z));
|
||||
prev_ctype = ctype;
|
||||
prev_cid = cid;
|
||||
}
|
||||
|
||||
// do the last one
|
||||
const pid = cell.mapping_ids![-prev_ctype - 1];
|
||||
const cname = `curve${counter}`;
|
||||
packings[pid[0]].ingredients[pid[1]].nbCurve = counter + 1;
|
||||
packings[pid[0]].ingredients[pid[1]][cname] = ctr_points;
|
||||
}
|
||||
}
|
||||
return new CellPack({ cell, packings });
|
||||
});
|
||||
}
|
||||
},
|
||||
dispose({ cache }) {
|
||||
((cache as any)?.asset as Asset.Wrapper | undefined)?.dispose();
|
||||
},
|
||||
});
|
||||
|
||||
export { StructureFromCellpack };
|
||||
@@ -77,9 +229,8 @@ const StructureFromCellpack = PluginStateTransform.BuiltIn({
|
||||
await CellPackInfoProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, structure, {
|
||||
info: { packingsCount: a.data.packings.length, packingIndex: params.packing, colors }
|
||||
});
|
||||
|
||||
(cache as any).assets = assets;
|
||||
return new PSO.Molecule.Structure(structure, { label: packing.name });
|
||||
return new PSO.Molecule.Structure(structure, { label: packing.name + '.' + packing.location });
|
||||
});
|
||||
},
|
||||
dispose({ b, cache }) {
|
||||
@@ -125,7 +276,7 @@ const StructureFromAssemblies = PluginStateTransform.BuiltIn({
|
||||
const s = await StructureSymmetry.buildAssembly(initial_structure, a.id).runInContext(ctx);
|
||||
structures.push(s);
|
||||
}
|
||||
const builder = Structure.Builder();
|
||||
const builder = Structure.Builder({ label: 'Membrane' });
|
||||
let offsetInvariantId = 0;
|
||||
for (const s of structures) {
|
||||
let maxInvariantId = 0;
|
||||
@@ -148,3 +299,28 @@ const StructureFromAssemblies = PluginStateTransform.BuiltIn({
|
||||
b?.data.customPropertyDescriptors.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
const CreateTransformer = StateTransformer.builderFactory('cellPACK');
|
||||
export const CreateCompartmentSphere = CreateTransformer({
|
||||
name: 'create-compartment-sphere',
|
||||
display: 'CompartmentSphere',
|
||||
from: PSO.Root, // or whatever data source
|
||||
to: PSO.Shape.Representation3D,
|
||||
params: {
|
||||
center: PD.Vec3(Vec3()),
|
||||
radius: PD.Numeric(1),
|
||||
label: PD.Text(`Compartment Sphere`)
|
||||
}
|
||||
})({
|
||||
canAutoUpdate({ oldParams, newParams }) {
|
||||
return true;
|
||||
},
|
||||
apply({ a, params }, plugin: PluginContext) {
|
||||
return Task.create('Compartment Sphere', async ctx => {
|
||||
const data = params;
|
||||
const repr = MBRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => (MBParams));
|
||||
await repr.createOrUpdate({ ...params, quality: 'custom', xrayShaded: true, doubleSided: true }, data).runInContext(ctx);
|
||||
return new PSO.Shape.Representation3D({ repr, sourceData: a }, { label: data.label });
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2021 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>
|
||||
*/
|
||||
|
||||
import { CIF } from '../../mol-io/reader/cif';
|
||||
@@ -37,7 +38,7 @@ async function downloadPDB(plugin: PluginContext, url: string, id: string, asset
|
||||
}
|
||||
|
||||
export async function getFromPdb(plugin: PluginContext, pdbId: string, assetManager: AssetManager) {
|
||||
const { cif, asset } = await downloadCif(plugin, `https://models.rcsb.org/${pdbId.toUpperCase()}.bcif`, true, assetManager);
|
||||
const { cif, asset } = await downloadCif(plugin, `https://models.rcsb.org/${pdbId}.bcif`, true, assetManager);
|
||||
return { mmcif: cif.blocks[0], asset };
|
||||
}
|
||||
|
||||
@@ -74,4 +75,35 @@ export function getStructureMean(structure: Structure) {
|
||||
}
|
||||
const { elementCount } = structure;
|
||||
return Vec3.create(xSum / elementCount, ySum / elementCount, zSum / elementCount);
|
||||
}
|
||||
|
||||
export function getFloatValue(value: DataView, offset: number) {
|
||||
// if the last byte is a negative value (MSB is 1), the final
|
||||
// float should be too
|
||||
const negative = value.getInt8(offset + 2) >>> 31;
|
||||
|
||||
// this is how the bytes are arranged in the byte array/DataView
|
||||
// buffer
|
||||
const [b0, b1, b2, exponent] = [
|
||||
// get first three bytes as unsigned since we only care
|
||||
// about the last 8 bits of 32-bit js number returned by
|
||||
// getUint8().
|
||||
// Should be the same as: getInt8(offset) & -1 >>> 24
|
||||
value.getUint8(offset),
|
||||
value.getUint8(offset + 1),
|
||||
value.getUint8(offset + 2),
|
||||
|
||||
// get the last byte, which is the exponent, as a signed int
|
||||
// since it's already correct
|
||||
value.getInt8(offset + 3)
|
||||
];
|
||||
|
||||
let mantissa = b0 | (b1 << 8) | (b2 << 16);
|
||||
if (negative) {
|
||||
// need to set the most significant 8 bits to 1's since a js
|
||||
// number is 32 bits but our mantissa is only 24.
|
||||
mantissa |= 255 << 24;
|
||||
}
|
||||
|
||||
return mantissa * Math.pow(10, exponent);
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
|
||||
*/
|
||||
|
||||
import { getStyle } from '../../mol-gl/renderer';
|
||||
import { Box3D } from '../../mol-math/geometry';
|
||||
import { PluginComponent } from '../../mol-plugin-state/component';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
@@ -46,13 +45,12 @@ export class GeometryControls extends PluginComponent {
|
||||
const renderObjects = this.plugin.canvas3d?.getRenderObjects()!;
|
||||
const filename = this.getFilename();
|
||||
|
||||
const style = getStyle(this.plugin.canvas3d?.props.renderer.style!);
|
||||
const boundingSphere = this.plugin.canvas3d?.boundingSphereVisible!;
|
||||
const boundingBox = Box3D.fromSphere3D(Box3D(), boundingSphere);
|
||||
let renderObjectExporter: GlbExporter | ObjExporter | StlExporter | UsdzExporter;
|
||||
switch (this.behaviors.params.value.format) {
|
||||
case 'glb':
|
||||
renderObjectExporter = new GlbExporter(style, boundingBox);
|
||||
renderObjectExporter = new GlbExporter(boundingBox);
|
||||
break;
|
||||
case 'obj':
|
||||
renderObjectExporter = new ObjExporter(filename, boundingBox);
|
||||
@@ -61,7 +59,7 @@ export class GeometryControls extends PluginComponent {
|
||||
renderObjectExporter = new StlExporter(boundingBox);
|
||||
break;
|
||||
case 'usdz':
|
||||
renderObjectExporter = new UsdzExporter(style, boundingBox, boundingSphere.radius);
|
||||
renderObjectExporter = new UsdzExporter(boundingBox, boundingSphere.radius);
|
||||
break;
|
||||
default: throw new Error('Unsupported format.');
|
||||
}
|
||||
@@ -77,7 +75,7 @@ export class GeometryControls extends PluginComponent {
|
||||
filename: filename + '.' + renderObjectExporter.fileExtension
|
||||
};
|
||||
} catch (e) {
|
||||
this.plugin.log.error('' + e);
|
||||
this.plugin.log.error('Error during geometry export');
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { BaseValues } from '../../mol-gl/renderable/schema';
|
||||
import { Style } from '../../mol-gl/renderer';
|
||||
import { asciiWrite } from '../../mol-io/common/ascii';
|
||||
import { IsNativeEndianLittle, flipByteOrder } from '../../mol-io/common/binary';
|
||||
import { Box3D } from '../../mol-math/geometry';
|
||||
@@ -15,7 +14,7 @@ import { RuntimeContext } from '../../mol-task';
|
||||
import { Color } from '../../mol-util/color/color';
|
||||
import { fillSerial } from '../../mol-util/array';
|
||||
import { NumberArray } from '../../mol-util/type-helpers';
|
||||
import { MeshExporter, AddMeshInput } from './mesh-exporter';
|
||||
import { MeshExporter, AddMeshInput, MeshGeoData } from './mesh-exporter';
|
||||
|
||||
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
|
||||
const v3fromArray = Vec3.fromArray;
|
||||
@@ -30,6 +29,12 @@ const FLOAT = 5126;
|
||||
const ARRAY_BUFFER = 34962;
|
||||
const ELEMENT_ARRAY_BUFFER = 34963;
|
||||
|
||||
const GLTF_MAGIC_BYTE = 0x46546C67;
|
||||
const JSON_CHUNK_TYPE = 0x4E4F534A;
|
||||
const BIN_CHUNK_TYPE = 0x004E4942;
|
||||
const JSON_PAD_CHAR = 0x20;
|
||||
const BIN_PAD_CHAR = 0x00;
|
||||
|
||||
export type GlbData = {
|
||||
glb: Uint8Array
|
||||
}
|
||||
@@ -38,6 +43,8 @@ export class GlbExporter extends MeshExporter<GlbData> {
|
||||
readonly fileExtension = 'glb';
|
||||
private nodes: Record<string, any>[] = [];
|
||||
private meshes: Record<string, any>[] = [];
|
||||
private materials: Record<string, any>[] = [];
|
||||
private materialMap = new Map<string, number>();
|
||||
private accessors: Record<string, any>[] = [];
|
||||
private bufferViews: Record<string, any>[] = [];
|
||||
private binaryBuffer: ArrayBuffer[] = [];
|
||||
@@ -126,23 +133,17 @@ export class GlbExporter extends MeshExporter<GlbData> {
|
||||
};
|
||||
}
|
||||
|
||||
private addColorBuffer(values: BaseValues, groups: Float32Array | Uint8Array, vertexCount: number, instanceIndex: number, isGeoTexture: boolean, interpolatedColors: Uint8Array | undefined) {
|
||||
const groupCount = values.uGroupCount.ref.value;
|
||||
private addColorBuffer(geoData: MeshGeoData, interpolatedColors: Uint8Array | undefined, interpolatedOverpaint: Uint8Array | undefined, interpolatedTransparency: Uint8Array | undefined) {
|
||||
const { values, vertexCount } = geoData;
|
||||
const uAlpha = values.uAlpha.ref.value;
|
||||
const dTransparency = values.dTransparency.ref.value;
|
||||
const tTransparency = values.tTransparency.ref.value;
|
||||
|
||||
const colorArray = new Uint8Array(vertexCount * 4);
|
||||
|
||||
for (let i = 0; i < vertexCount; ++i) {
|
||||
let color = GlbExporter.getColor(values, groups, vertexCount, instanceIndex, isGeoTexture, interpolatedColors, i);
|
||||
let color = GlbExporter.getColor(i, geoData, interpolatedColors, interpolatedOverpaint);
|
||||
|
||||
let alpha = uAlpha;
|
||||
if (dTransparency) {
|
||||
const group = isGeoTexture ? GlbExporter.getGroup(groups, i) : groups[i];
|
||||
const transparency = tTransparency.array[instanceIndex * groupCount + group] / 255;
|
||||
alpha *= 1 - transparency;
|
||||
}
|
||||
const transparency = GlbExporter.getTransparency(i, geoData, interpolatedTransparency);
|
||||
const alpha = uAlpha * (1 - transparency);
|
||||
|
||||
color = Color.sRGBToLinear(color);
|
||||
Color.toArray(color, colorArray, i * 4);
|
||||
@@ -157,20 +158,53 @@ export class GlbExporter extends MeshExporter<GlbData> {
|
||||
return this.addBuffer(colorBuffer, UNSIGNED_BYTE, 'VEC4', vertexCount, ARRAY_BUFFER, undefined, undefined, true);
|
||||
}
|
||||
|
||||
private addMaterial(metalness: number, roughness: number) {
|
||||
const hash = `${metalness}|${roughness}`;
|
||||
if (!this.materialMap.has(hash)) {
|
||||
this.materialMap.set(hash, this.materials.length);
|
||||
this.materials.push({
|
||||
pbrMetallicRoughness: {
|
||||
baseColorFactor: [1, 1, 1, 1],
|
||||
metallicFactor: metalness,
|
||||
roughnessFactor: roughness
|
||||
}
|
||||
});
|
||||
}
|
||||
return this.materialMap.get(hash)!;
|
||||
}
|
||||
|
||||
protected async addMeshWithColors(input: AddMeshInput) {
|
||||
const { mesh, values, isGeoTexture, webgl, ctx } = input;
|
||||
|
||||
const t = Mat4();
|
||||
|
||||
const colorType = values.dColorType.ref.value;
|
||||
const overpaintType = values.dOverpaintType.ref.value;
|
||||
const transparencyType = values.dTransparencyType.ref.value;
|
||||
const dTransparency = values.dTransparency.ref.value;
|
||||
const aTransform = values.aTransform.ref.value;
|
||||
const instanceCount = values.uInstanceCount.ref.value;
|
||||
const metalness = values.uMetalness.ref.value;
|
||||
const roughness = values.uRoughness.ref.value;
|
||||
|
||||
const material = this.addMaterial(metalness, roughness);
|
||||
|
||||
let interpolatedColors: Uint8Array | undefined;
|
||||
if (colorType === 'volume' || colorType === 'volumeInstance') {
|
||||
const stride = isGeoTexture ? 4 : 3;
|
||||
interpolatedColors = GlbExporter.getInterpolatedColors(mesh!.vertices, mesh!.vertexCount, values, stride, colorType, webgl!);
|
||||
interpolatedColors = GlbExporter.getInterpolatedColors(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType });
|
||||
}
|
||||
|
||||
let interpolatedOverpaint: Uint8Array | undefined;
|
||||
if (overpaintType === 'volumeInstance') {
|
||||
const stride = isGeoTexture ? 4 : 3;
|
||||
interpolatedOverpaint = GlbExporter.getInterpolatedOverpaint(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType: overpaintType });
|
||||
}
|
||||
|
||||
let interpolatedTransparency: Uint8Array | undefined;
|
||||
if (transparencyType === 'volumeInstance') {
|
||||
const stride = isGeoTexture ? 4 : 3;
|
||||
interpolatedTransparency = GlbExporter.getInterpolatedTransparency(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType: transparencyType });
|
||||
}
|
||||
|
||||
// instancing
|
||||
@@ -201,7 +235,7 @@ export class GlbExporter extends MeshExporter<GlbData> {
|
||||
|
||||
// create a color buffer if needed
|
||||
if (instanceIndex === 0 || !sameColorBuffer) {
|
||||
colorAccessorIndex = this.addColorBuffer(values, groups, vertexCount, instanceIndex, isGeoTexture, interpolatedColors);
|
||||
colorAccessorIndex = this.addColorBuffer({ values, groups, vertexCount, instanceIndex, isGeoTexture }, interpolatedColors, interpolatedOverpaint, interpolatedTransparency);
|
||||
}
|
||||
|
||||
// glTF mesh
|
||||
@@ -214,7 +248,7 @@ export class GlbExporter extends MeshExporter<GlbData> {
|
||||
COLOR_0: colorAccessorIndex!
|
||||
},
|
||||
indices: indexAccessorIndex,
|
||||
material: 0
|
||||
material
|
||||
}]
|
||||
});
|
||||
}
|
||||
@@ -248,13 +282,7 @@ export class GlbExporter extends MeshExporter<GlbData> {
|
||||
}],
|
||||
bufferViews: this.bufferViews,
|
||||
accessors: this.accessors,
|
||||
materials: [{
|
||||
pbrMetallicRoughness: {
|
||||
baseColorFactor: [1, 1, 1, 1],
|
||||
metallicFactor: this.style.metalness,
|
||||
roughnessFactor: this.style.roughness
|
||||
}
|
||||
}]
|
||||
materials: this.materials
|
||||
};
|
||||
|
||||
const createChunk = (chunkType: number, data: ArrayBuffer[], byteLength: number, padChar: number): [ArrayBuffer[], number] => {
|
||||
@@ -279,13 +307,13 @@ export class GlbExporter extends MeshExporter<GlbData> {
|
||||
const jsonBuffer = new Uint8Array(jsonString.length);
|
||||
asciiWrite(jsonBuffer, jsonString);
|
||||
|
||||
const [jsonChunk, jsonChunkLength] = createChunk(0x4E4F534A, [jsonBuffer.buffer], jsonBuffer.length, 0x20);
|
||||
const [binaryChunk, binaryChunkLength] = createChunk(0x004E4942, this.binaryBuffer, binaryBufferLength, 0x00);
|
||||
const [jsonChunk, jsonChunkLength] = createChunk(JSON_CHUNK_TYPE, [jsonBuffer.buffer], jsonBuffer.length, JSON_PAD_CHAR);
|
||||
const [binaryChunk, binaryChunkLength] = createChunk(BIN_CHUNK_TYPE, this.binaryBuffer, binaryBufferLength, BIN_PAD_CHAR);
|
||||
|
||||
const glbBufferLength = 12 + jsonChunkLength + binaryChunkLength;
|
||||
const header = new ArrayBuffer(12);
|
||||
const headerDataView = new DataView(header);
|
||||
headerDataView.setUint32(0, 0x46546C67, true); // magic number "glTF"
|
||||
headerDataView.setUint32(0, GLTF_MAGIC_BYTE, true); // magic number "glTF"
|
||||
headerDataView.setUint32(4, 2, true); // version
|
||||
headerDataView.setUint32(8, glbBufferLength, true); // length
|
||||
const glbBuffer = [header, ...jsonChunk, ...binaryChunk];
|
||||
@@ -303,7 +331,7 @@ export class GlbExporter extends MeshExporter<GlbData> {
|
||||
return new Blob([(await this.getData()).glb], { type: 'model/gltf-binary' });
|
||||
}
|
||||
|
||||
constructor(private style: Style, boundingBox: Box3D) {
|
||||
constructor(boundingBox: Box3D) {
|
||||
super();
|
||||
const tmpV = Vec3();
|
||||
Vec3.add(tmpV, boundingBox.min, boundingBox.max);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { sort, arraySwap } from '../../mol-data/util';
|
||||
@@ -26,6 +27,7 @@ import { RuntimeContext } from '../../mol-task';
|
||||
import { Color } from '../../mol-util/color/color';
|
||||
import { decodeFloatRGB } from '../../mol-util/float-packing';
|
||||
import { RenderObjectExporter, RenderObjectExportData } from './render-object-exporter';
|
||||
import { readAlphaTexture, readTexture } from '../../mol-gl/compute/util';
|
||||
|
||||
const GeoExportName = 'geo-export';
|
||||
|
||||
@@ -48,6 +50,14 @@ export interface AddMeshInput {
|
||||
ctx: RuntimeContext
|
||||
}
|
||||
|
||||
export type MeshGeoData = {
|
||||
values: BaseValues,
|
||||
groups: Float32Array | Uint8Array,
|
||||
vertexCount: number,
|
||||
instanceIndex: number,
|
||||
isGeoTexture: boolean
|
||||
}
|
||||
|
||||
export abstract class MeshExporter<D extends RenderObjectExportData> implements RenderObjectExporter<D> {
|
||||
abstract readonly fileExtension: string;
|
||||
|
||||
@@ -90,26 +100,43 @@ export abstract class MeshExporter<D extends RenderObjectExportData> implements
|
||||
return decodeFloatRGB(r, g, b);
|
||||
}
|
||||
|
||||
protected static getInterpolatedColors(vertices: Float32Array, vertexCount: number, values: BaseValues, stride: number, colorType: 'volume' | 'volumeInstance', webgl: WebGLContext) {
|
||||
protected static getInterpolatedColors(webgl: WebGLContext, input: { vertices: Float32Array, vertexCount: number, values: BaseValues, stride: 3 | 4, colorType: 'volume' | 'volumeInstance' }) {
|
||||
const { values, vertexCount, vertices, colorType, stride } = input;
|
||||
const colorGridTransform = values.uColorGridTransform.ref.value;
|
||||
const colorGridDim = values.uColorGridDim.ref.value;
|
||||
const colorTexDim = values.uColorTexDim.ref.value;
|
||||
const aTransform = values.aTransform.ref.value;
|
||||
const instanceCount = values.uInstanceCount.ref.value;
|
||||
|
||||
if (!webgl.namedFramebuffers[GeoExportName]) {
|
||||
webgl.namedFramebuffers[GeoExportName] = webgl.resources.framebuffer();
|
||||
}
|
||||
const framebuffer = webgl.namedFramebuffers[GeoExportName];
|
||||
const colorGrid = readTexture(webgl, values.tColorGrid.ref.value).array;
|
||||
const interpolated = getTrilinearlyInterpolated({ vertexCount, instanceCount, transformBuffer: aTransform, positionBuffer: vertices, colorType, grid: colorGrid, gridDim: colorGridDim, gridTexDim: colorTexDim, gridTransform: colorGridTransform, vertexStride: stride, colorStride: 4, outputStride: 3 });
|
||||
return interpolated.array;
|
||||
}
|
||||
|
||||
const [width, height] = colorTexDim;
|
||||
const colorGrid = new Uint8Array(width * height * 4);
|
||||
protected static getInterpolatedOverpaint(webgl: WebGLContext, input: { vertices: Float32Array, vertexCount: number, values: BaseValues, stride: 3 | 4, colorType: 'volumeInstance' }) {
|
||||
const { values, vertexCount, vertices, colorType, stride } = input;
|
||||
const overpaintGridTransform = values.uOverpaintGridTransform.ref.value;
|
||||
const overpaintGridDim = values.uOverpaintGridDim.ref.value;
|
||||
const overpaintTexDim = values.uOverpaintTexDim.ref.value;
|
||||
const aTransform = values.aTransform.ref.value;
|
||||
const instanceCount = values.uInstanceCount.ref.value;
|
||||
|
||||
framebuffer.bind();
|
||||
values.tColorGrid.ref.value.attachFramebuffer(framebuffer, 0);
|
||||
webgl.readPixels(0, 0, width, height, colorGrid);
|
||||
const overpaintGrid = readTexture(webgl, values.tOverpaintGrid.ref.value).array;
|
||||
const interpolated = getTrilinearlyInterpolated({ vertexCount, instanceCount, transformBuffer: aTransform, positionBuffer: vertices, colorType, grid: overpaintGrid, gridDim: overpaintGridDim, gridTexDim: overpaintTexDim, gridTransform: overpaintGridTransform, vertexStride: stride, colorStride: 4, outputStride: 4 });
|
||||
return interpolated.array;
|
||||
}
|
||||
|
||||
protected static getInterpolatedTransparency(webgl: WebGLContext, input: { vertices: Float32Array, vertexCount: number, values: BaseValues, stride: 3 | 4, colorType: 'volumeInstance' }) {
|
||||
const { values, vertexCount, vertices, colorType, stride } = input;
|
||||
const transparencyGridTransform = values.uTransparencyGridTransform.ref.value;
|
||||
const transparencyGridDim = values.uTransparencyGridDim.ref.value;
|
||||
const transparencyTexDim = values.uTransparencyTexDim.ref.value;
|
||||
const aTransform = values.aTransform.ref.value;
|
||||
const instanceCount = values.uInstanceCount.ref.value;
|
||||
|
||||
const transparencyGrid = readAlphaTexture(webgl, values.tTransparencyGrid.ref.value).array;
|
||||
const interpolated = getTrilinearlyInterpolated({ vertexCount, instanceCount, transformBuffer: aTransform, positionBuffer: vertices, colorType, grid: transparencyGrid, gridDim: transparencyGridDim, gridTexDim: transparencyTexDim, gridTransform: transparencyGridTransform, vertexStride: stride, colorStride: 4, outputStride: 1, itemOffset: 3 });
|
||||
|
||||
const interpolated = getTrilinearlyInterpolated({ vertexCount, instanceCount, transformBuffer: aTransform, positionBuffer: vertices, colorType, grid: colorGrid, gridDim: colorGridDim, gridTexDim: colorTexDim, gridTransform: colorGridTransform, vertexStride: stride, colorStride: 4 });
|
||||
return interpolated.array;
|
||||
}
|
||||
|
||||
@@ -194,11 +221,13 @@ export abstract class MeshExporter<D extends RenderObjectExportData> implements
|
||||
}
|
||||
}
|
||||
|
||||
protected static getColor(values: BaseValues, groups: Float32Array | Uint8Array, vertexCount: number, instanceIndex: number, isGeoTexture: boolean, interpolatedColors: Uint8Array | undefined, vertexIndex: number): Color {
|
||||
protected static getColor(vertexIndex: number, geoData: MeshGeoData, interpolatedColors?: Uint8Array, interpolatedOverpaint?: Uint8Array): Color {
|
||||
const { values, instanceIndex, isGeoTexture, groups, vertexCount } = geoData;
|
||||
const groupCount = values.uGroupCount.ref.value;
|
||||
const colorType = values.dColorType.ref.value;
|
||||
const uColor = values.uColor.ref.value;
|
||||
const tColor = values.tColor.ref.value.array;
|
||||
const overpaintType = values.dOverpaintType.ref.value;
|
||||
const dOverpaint = values.dOverpaint.ref.value;
|
||||
const tOverpaint = values.tOverpaint.ref.value.array;
|
||||
|
||||
@@ -236,15 +265,70 @@ export abstract class MeshExporter<D extends RenderObjectExportData> implements
|
||||
}
|
||||
|
||||
if (dOverpaint) {
|
||||
const group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex];
|
||||
const overpaintColor = Color.fromArray(tOverpaint, (instanceIndex * groupCount + group) * 4);
|
||||
const overpaintAlpha = tOverpaint[(instanceIndex * groupCount + group) * 4 + 3] / 255;
|
||||
let overpaintColor: Color;
|
||||
let overpaintAlpha: number;
|
||||
switch (overpaintType) {
|
||||
case 'groupInstance': {
|
||||
const group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex];
|
||||
const idx = (instanceIndex * groupCount + group) * 4;
|
||||
overpaintColor = Color.fromArray(tOverpaint, idx);
|
||||
overpaintAlpha = tOverpaint[idx + 3] / 255;
|
||||
break;
|
||||
}
|
||||
case 'vertexInstance': {
|
||||
const idx = (instanceIndex * vertexCount + vertexIndex) * 4;
|
||||
overpaintColor = Color.fromArray(tOverpaint, idx);
|
||||
overpaintAlpha = tOverpaint[idx + 3] / 255;
|
||||
break;
|
||||
}
|
||||
case 'volumeInstance': {
|
||||
const idx = (instanceIndex * vertexCount + vertexIndex) * 4;
|
||||
overpaintColor = Color.fromArray(interpolatedOverpaint!, idx);
|
||||
overpaintAlpha = interpolatedOverpaint![idx + 3] / 255;
|
||||
break;
|
||||
}
|
||||
default: throw new Error('Unsupported overpaint type.');
|
||||
}
|
||||
// interpolate twice to avoid darkening due to empty overpaint
|
||||
overpaintColor = Color.interpolate(color, overpaintColor, overpaintAlpha);
|
||||
color = Color.interpolate(color, overpaintColor, overpaintAlpha);
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
protected static getTransparency(vertexIndex: number, geoData: MeshGeoData, interpolatedTransparency?: Uint8Array): number {
|
||||
const { values, instanceIndex, isGeoTexture, groups, vertexCount } = geoData;
|
||||
const groupCount = values.uGroupCount.ref.value;
|
||||
const dTransparency = values.dTransparency.ref.value;
|
||||
const tTransparency = values.tTransparency.ref.value.array;
|
||||
const transparencyType = values.dTransparencyType.ref.value;
|
||||
|
||||
let transparency: number = 0;
|
||||
if (dTransparency) {
|
||||
switch (transparencyType) {
|
||||
case 'groupInstance': {
|
||||
const group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex];
|
||||
const idx = (instanceIndex * groupCount + group);
|
||||
transparency = tTransparency[idx] / 255;
|
||||
break;
|
||||
}
|
||||
case 'vertexInstance': {
|
||||
const idx = (instanceIndex * vertexCount + vertexIndex);
|
||||
transparency = tTransparency[idx] / 255;
|
||||
break;
|
||||
}
|
||||
case 'volumeInstance': {
|
||||
const idx = (instanceIndex * vertexCount + vertexIndex);
|
||||
transparency = interpolatedTransparency![idx] / 255;
|
||||
break;
|
||||
}
|
||||
default: throw new Error('Unsupported transparency type.');
|
||||
}
|
||||
}
|
||||
return transparency;
|
||||
}
|
||||
|
||||
protected abstract addMeshWithColors(input: AddMeshInput): void;
|
||||
|
||||
private async addMesh(values: MeshValues, webgl: WebGLContext, ctx: RuntimeContext) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { asciiWrite } from '../../mol-io/common/ascii';
|
||||
@@ -47,7 +48,7 @@ export class ObjExporter extends MeshExporter<ObjData> {
|
||||
StringBuilder.newline(this.obj);
|
||||
if (!this.materialSet.has(material)) {
|
||||
this.materialSet.add(material);
|
||||
const [r, g, b] = Color.toRgbNormalized(color);
|
||||
const [r, g, b] = Color.toRgbNormalized(color).map(v => Math.round(v * 1000) / 1000);
|
||||
const mtl = this.mtl;
|
||||
StringBuilder.writeSafe(mtl, `newmtl ${material}\n`);
|
||||
StringBuilder.writeSafe(mtl, 'illum 2\n'); // illumination model
|
||||
@@ -77,18 +78,27 @@ export class ObjExporter extends MeshExporter<ObjData> {
|
||||
const tmpV = Vec3();
|
||||
const stride = isGeoTexture ? 4 : 3;
|
||||
|
||||
const groupCount = values.uGroupCount.ref.value;
|
||||
const colorType = values.dColorType.ref.value;
|
||||
const overpaintType = values.dOverpaintType.ref.value;
|
||||
const transparencyType = values.dTransparencyType.ref.value;
|
||||
const uAlpha = values.uAlpha.ref.value;
|
||||
const dTransparency = values.dTransparency.ref.value;
|
||||
const tTransparency = values.tTransparency.ref.value;
|
||||
const aTransform = values.aTransform.ref.value;
|
||||
const instanceCount = values.uInstanceCount.ref.value;
|
||||
|
||||
let interpolatedColors: Uint8Array | undefined;
|
||||
if (colorType === 'volume' || colorType === 'volumeInstance') {
|
||||
interpolatedColors = ObjExporter.getInterpolatedColors(mesh!.vertices, mesh!.vertexCount, values, stride, colorType, webgl!);
|
||||
ObjExporter.quantizeColors(interpolatedColors, mesh!.vertexCount);
|
||||
interpolatedColors = ObjExporter.getInterpolatedColors(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType });
|
||||
}
|
||||
|
||||
let interpolatedOverpaint: Uint8Array | undefined;
|
||||
if (overpaintType === 'volumeInstance') {
|
||||
interpolatedOverpaint = ObjExporter.getInterpolatedOverpaint(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType: overpaintType });
|
||||
}
|
||||
|
||||
let interpolatedTransparency: Uint8Array | undefined;
|
||||
if (transparencyType === 'volumeInstance') {
|
||||
const stride = isGeoTexture ? 4 : 3;
|
||||
interpolatedTransparency = ObjExporter.getInterpolatedTransparency(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType: transparencyType });
|
||||
}
|
||||
|
||||
await ctx.update({ isIndeterminate: false, current: 0, max: instanceCount });
|
||||
@@ -126,17 +136,23 @@ export class ObjExporter extends MeshExporter<ObjData> {
|
||||
StringBuilder.newline(obj);
|
||||
}
|
||||
|
||||
// face
|
||||
const geoData = { values, groups, vertexCount, instanceIndex, isGeoTexture };
|
||||
|
||||
// color
|
||||
const quantizedColors = new Uint8Array(drawCount * 3);
|
||||
for (let i = 0; i < drawCount; i += 3) {
|
||||
const v = isGeoTexture ? i : indices![i];
|
||||
const color = ObjExporter.getColor(values, groups, vertexCount, instanceIndex, isGeoTexture, interpolatedColors, v);
|
||||
const color = ObjExporter.getColor(v, geoData, interpolatedColors, interpolatedOverpaint);
|
||||
Color.toArray(color, quantizedColors, i);
|
||||
}
|
||||
ObjExporter.quantizeColors(quantizedColors, vertexCount);
|
||||
|
||||
let alpha = uAlpha;
|
||||
if (dTransparency) {
|
||||
const group = isGeoTexture ? ObjExporter.getGroup(groups, i) : groups[indices![i]];
|
||||
const transparency = tTransparency.array[instanceIndex * groupCount + group] / 255;
|
||||
alpha *= 1 - transparency;
|
||||
}
|
||||
// face
|
||||
for (let i = 0; i < drawCount; i += 3) {
|
||||
const color = Color.fromArray(quantizedColors, i);
|
||||
|
||||
const transparency = ObjExporter.getTransparency(i, geoData, interpolatedTransparency);
|
||||
const alpha = Math.round(uAlpha * (1 - transparency) * 10) / 10; // quantized
|
||||
|
||||
this.updateMaterial(color, alpha);
|
||||
|
||||
|
||||
@@ -79,10 +79,10 @@ export class GeometryExporterUI extends CollapsableControls<{}, State> {
|
||||
try {
|
||||
this.setState({ busy: true });
|
||||
const data = await this.controls.exportGeometry();
|
||||
this.setState({ busy: false });
|
||||
|
||||
download(data.blob, data.filename);
|
||||
} catch {
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
this.setState({ busy: false });
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,6 @@ export class GeometryExporterUI extends CollapsableControls<{}, State> {
|
||||
try {
|
||||
this.setState({ busy: true });
|
||||
const data = await this.controls.exportGeometry();
|
||||
this.setState({ busy: false });
|
||||
const a = document.createElement('a');
|
||||
a.rel = 'ar';
|
||||
a.href = URL.createObjectURL(data.blob);
|
||||
@@ -100,7 +99,9 @@ export class GeometryExporterUI extends CollapsableControls<{}, State> {
|
||||
a.appendChild(document.createElement('img'));
|
||||
setTimeout(() => URL.revokeObjectURL(a.href), 4E4); // 40s
|
||||
setTimeout(() => a.dispatchEvent(new MouseEvent('click')));
|
||||
} catch {
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
this.setState({ busy: false });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Style } from '../../mol-gl/renderer';
|
||||
import { asciiWrite } from '../../mol-io/common/ascii';
|
||||
import { Box3D } from '../../mol-math/geometry';
|
||||
import { Vec3, Mat3, Mat4 } from '../../mol-math/linear-algebra';
|
||||
@@ -31,18 +31,17 @@ export class UsdzExporter extends MeshExporter<UsdzData> {
|
||||
readonly fileExtension = 'usdz';
|
||||
private meshes: string[] = [];
|
||||
private materials: string[] = [];
|
||||
private materialSet = new Set<number>();
|
||||
private materialMap = new Map<string, number>();
|
||||
private centerTransform: Mat4;
|
||||
|
||||
private static getMaterialKey(color: Color, alpha: number) {
|
||||
return color * 256 + Math.round(alpha * 255);
|
||||
}
|
||||
private addMaterial(color: Color, alpha: number, metalness: number, roughness: number): number {
|
||||
const hash = `${color}|${alpha}|${metalness}|${roughness}`;
|
||||
if (this.materialMap.has(hash)) return this.materialMap.get(hash)!;
|
||||
|
||||
private addMaterial(color: Color, alpha: number) {
|
||||
const materialKey = UsdzExporter.getMaterialKey(color, alpha);
|
||||
if (this.materialSet.has(materialKey)) return;
|
||||
this.materialSet.add(materialKey);
|
||||
const [r, g, b] = Color.toRgbNormalized(color);
|
||||
const materialKey = this.materialMap.size;
|
||||
this.materialMap.set(hash, materialKey);
|
||||
|
||||
const [r, g, b] = Color.toRgbNormalized(color).map(v => Math.round(v * 1000) / 1000);
|
||||
this.materials.push(`
|
||||
def Material "material${materialKey}"
|
||||
{
|
||||
@@ -52,12 +51,13 @@ def Material "material${materialKey}"
|
||||
uniform token info:id = "UsdPreviewSurface"
|
||||
color3f inputs:diffuseColor = (${r},${g},${b})
|
||||
float inputs:opacity = ${alpha}
|
||||
float inputs:metallic = ${this.style.metalness}
|
||||
float inputs:roughness = ${this.style.roughness}
|
||||
float inputs:metallic = ${metalness}
|
||||
float inputs:roughness = ${roughness}
|
||||
token outputs:surface
|
||||
}
|
||||
}
|
||||
`);
|
||||
return materialKey;
|
||||
}
|
||||
|
||||
protected async addMeshWithColors(input: AddMeshInput) {
|
||||
@@ -68,18 +68,30 @@ def Material "material${materialKey}"
|
||||
const tmpV = Vec3();
|
||||
const stride = isGeoTexture ? 4 : 3;
|
||||
|
||||
const groupCount = values.uGroupCount.ref.value;
|
||||
const colorType = values.dColorType.ref.value;
|
||||
const overpaintType = values.dOverpaintType.ref.value;
|
||||
const transparencyType = values.dTransparencyType.ref.value;
|
||||
const uAlpha = values.uAlpha.ref.value;
|
||||
const dTransparency = values.dTransparency.ref.value;
|
||||
const tTransparency = values.tTransparency.ref.value;
|
||||
const aTransform = values.aTransform.ref.value;
|
||||
const instanceCount = values.uInstanceCount.ref.value;
|
||||
const metalness = values.uMetalness.ref.value;
|
||||
const roughness = values.uRoughness.ref.value;
|
||||
|
||||
let interpolatedColors: Uint8Array | undefined;
|
||||
if (colorType === 'volume' || colorType === 'volumeInstance') {
|
||||
interpolatedColors = UsdzExporter.getInterpolatedColors(mesh!.vertices, mesh!.vertexCount, values, stride, colorType, webgl!);
|
||||
UsdzExporter.quantizeColors(interpolatedColors, mesh!.vertexCount);
|
||||
interpolatedColors = UsdzExporter.getInterpolatedColors(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType });
|
||||
}
|
||||
|
||||
let interpolatedOverpaint: Uint8Array | undefined;
|
||||
if (overpaintType === 'volumeInstance') {
|
||||
const stride = isGeoTexture ? 4 : 3;
|
||||
interpolatedOverpaint = UsdzExporter.getInterpolatedOverpaint(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType: overpaintType });
|
||||
}
|
||||
|
||||
let interpolatedTransparency: Uint8Array | undefined;
|
||||
if (transparencyType === 'volumeInstance') {
|
||||
const stride = isGeoTexture ? 4 : 3;
|
||||
interpolatedTransparency = UsdzExporter.getInterpolatedTransparency(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType: transparencyType });
|
||||
}
|
||||
|
||||
await ctx.update({ isIndeterminate: false, current: 0, max: instanceCount });
|
||||
@@ -121,6 +133,8 @@ def Material "material${materialKey}"
|
||||
StringBuilder.writeSafe(normalBuilder, ')');
|
||||
}
|
||||
|
||||
const geoData = { values, groups, vertexCount, instanceIndex, isGeoTexture };
|
||||
|
||||
// face
|
||||
for (let i = 0; i < drawCount; ++i) {
|
||||
const v = isGeoTexture ? i : indices![i];
|
||||
@@ -129,21 +143,23 @@ def Material "material${materialKey}"
|
||||
}
|
||||
|
||||
// color
|
||||
const faceIndicesByMaterial = new Map<number, number[]>();
|
||||
const quantizedColors = new Uint8Array(drawCount * 3);
|
||||
for (let i = 0; i < drawCount; i += 3) {
|
||||
const v = isGeoTexture ? i : indices![i];
|
||||
const color = UsdzExporter.getColor(values, groups, vertexCount, instanceIndex, isGeoTexture, interpolatedColors, v);
|
||||
const color = UsdzExporter.getColor(v, geoData, interpolatedColors, interpolatedOverpaint);
|
||||
Color.toArray(color, quantizedColors, i);
|
||||
}
|
||||
UsdzExporter.quantizeColors(quantizedColors, vertexCount);
|
||||
|
||||
let alpha = uAlpha;
|
||||
if (dTransparency) {
|
||||
const group = isGeoTexture ? UsdzExporter.getGroup(groups, i) : groups[indices![i]];
|
||||
const transparency = tTransparency.array[instanceIndex * groupCount + group] / 255;
|
||||
alpha *= 1 - transparency;
|
||||
}
|
||||
// material
|
||||
const faceIndicesByMaterial = new Map<number, number[]>();
|
||||
for (let i = 0; i < drawCount; i += 3) {
|
||||
const color = Color.fromArray(quantizedColors, i);
|
||||
|
||||
this.addMaterial(color, alpha);
|
||||
const transparency = UsdzExporter.getTransparency(i, geoData, interpolatedTransparency);
|
||||
const alpha = Math.round(uAlpha * (1 - transparency) * 10) / 10; // quantized
|
||||
|
||||
const materialKey = UsdzExporter.getMaterialKey(color, alpha);
|
||||
const materialKey = this.addMaterial(color, alpha, metalness, roughness);
|
||||
let faceIndices = faceIndicesByMaterial.get(materialKey);
|
||||
if (faceIndices === undefined) {
|
||||
faceIndices = [];
|
||||
@@ -215,7 +231,7 @@ def Mesh "mesh${this.meshes.length}"
|
||||
return new Blob([usdz], { type: 'model/vnd.usdz+zip' });
|
||||
}
|
||||
|
||||
constructor(private style: Style, boundingBox: Box3D, radius: number) {
|
||||
constructor(boundingBox: Box3D, radius: number) {
|
||||
super();
|
||||
const t = Mat4();
|
||||
// scale the model so that it fits within 1 meter
|
||||
|
||||
@@ -73,7 +73,7 @@ export class Mp4Controls extends PluginComponent {
|
||||
const filename = anim.anim.display.name.toLowerCase().replace(/\s/g, '-').replace(/[^a-z0-9_\-]/g, '');
|
||||
return { movie, filename: `${this.plugin.helpers.viewportScreenshot?.getFilename('')}_${filename}.mp4` };
|
||||
} catch (e) {
|
||||
this.plugin.log.error('' + e);
|
||||
this.plugin.log.error('Error during animation export');
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -115,7 +115,8 @@ export class Mp4EncoderUI extends CollapsableControls<{}, State> {
|
||||
this.setState({ busy: true });
|
||||
const data = await this.controls.render();
|
||||
this.setState({ busy: false, data });
|
||||
} catch {
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.setState({ busy: false });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/* eslint-disable */
|
||||
export type Maybe<T> = T | null;
|
||||
export type InputMaybe<T> = Maybe<T>;
|
||||
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
|
||||
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
|
||||
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
|
||||
// Generated on 2021-09-12T13:31:40-07:00
|
||||
// Generated on 2021-11-25T14:34:23-08:00
|
||||
|
||||
/** All built-in and custom scalars, mapped to their actual values */
|
||||
export type Scalars = {
|
||||
@@ -6769,7 +6770,7 @@ export type Query = {
|
||||
|
||||
/** Query root */
|
||||
export type QueryAssembliesArgs = {
|
||||
assembly_ids: ReadonlyArray<Maybe<Scalars['String']>>;
|
||||
assembly_ids: ReadonlyArray<InputMaybe<Scalars['String']>>;
|
||||
};
|
||||
|
||||
|
||||
@@ -6802,7 +6803,7 @@ export type QueryBranched_Entity_InstanceArgs = {
|
||||
|
||||
/** Query root */
|
||||
export type QueryBranched_Entity_InstancesArgs = {
|
||||
instance_ids: ReadonlyArray<Maybe<Scalars['String']>>;
|
||||
instance_ids: ReadonlyArray<InputMaybe<Scalars['String']>>;
|
||||
};
|
||||
|
||||
|
||||
@@ -6814,7 +6815,7 @@ export type QueryChem_CompArgs = {
|
||||
|
||||
/** Query root */
|
||||
export type QueryChem_CompsArgs = {
|
||||
comp_ids: ReadonlyArray<Maybe<Scalars['String']>>;
|
||||
comp_ids: ReadonlyArray<InputMaybe<Scalars['String']>>;
|
||||
};
|
||||
|
||||
|
||||
@@ -6852,7 +6853,7 @@ export type QueryNonpolymer_Entity_InstanceArgs = {
|
||||
|
||||
/** Query root */
|
||||
export type QueryNonpolymer_Entity_InstancesArgs = {
|
||||
instance_ids: ReadonlyArray<Maybe<Scalars['String']>>;
|
||||
instance_ids: ReadonlyArray<InputMaybe<Scalars['String']>>;
|
||||
};
|
||||
|
||||
|
||||
@@ -6878,7 +6879,7 @@ export type QueryPolymer_Entity_InstanceArgs = {
|
||||
|
||||
/** Query root */
|
||||
export type QueryPolymer_Entity_InstancesArgs = {
|
||||
instance_ids: ReadonlyArray<Maybe<Scalars['String']>>;
|
||||
instance_ids: ReadonlyArray<InputMaybe<Scalars['String']>>;
|
||||
};
|
||||
|
||||
|
||||
@@ -13144,4 +13145,4 @@ export type AssemblySymmetryQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type AssemblySymmetryQuery = { readonly assembly?: Maybe<{ readonly rcsb_struct_symmetry?: Maybe<ReadonlyArray<Maybe<{ readonly kind: string, readonly oligomeric_state: string, readonly stoichiometry: ReadonlyArray<Maybe<string>>, readonly symbol: string, readonly type: string, readonly clusters: ReadonlyArray<Maybe<{ readonly avg_rmsd?: Maybe<number>, readonly members: ReadonlyArray<Maybe<{ readonly asym_id: string, readonly pdbx_struct_oper_list_ids?: Maybe<ReadonlyArray<Maybe<string>>> }>> }>>, readonly rotation_axes?: Maybe<ReadonlyArray<Maybe<{ readonly order?: Maybe<number>, readonly start: ReadonlyArray<Maybe<number>>, readonly end: ReadonlyArray<Maybe<number>> }>>> }>>> }> };
|
||||
export type AssemblySymmetryQuery = { readonly assembly?: { readonly rcsb_struct_symmetry?: ReadonlyArray<{ readonly kind: string, readonly oligomeric_state: string, readonly stoichiometry: ReadonlyArray<string | null | undefined>, readonly symbol: string, readonly type: string, readonly clusters: ReadonlyArray<{ readonly avg_rmsd?: number | null | undefined, readonly members: ReadonlyArray<{ readonly asym_id: string, readonly pdbx_struct_oper_list_ids?: ReadonlyArray<string | null | undefined> | null | undefined } | null | undefined> } | null | undefined>, readonly rotation_axes?: ReadonlyArray<{ readonly order?: number | null | undefined, readonly start: ReadonlyArray<number | null | undefined>, readonly end: ReadonlyArray<number | null | undefined> } | null | undefined> | null | undefined } | null | undefined> | null | undefined } | null | undefined };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
@@ -27,6 +27,10 @@ interface ICamera {
|
||||
readonly fogNear: number,
|
||||
}
|
||||
|
||||
const tmpPos1 = Vec3();
|
||||
const tmpPos2 = Vec3();
|
||||
const tmpClip = Vec4();
|
||||
|
||||
class Camera implements ICamera {
|
||||
readonly view: Mat4 = Mat4.identity();
|
||||
readonly projection: Mat4 = Mat4.identity();
|
||||
@@ -155,14 +159,32 @@ class Camera implements ICamera {
|
||||
}
|
||||
}
|
||||
|
||||
/** Transform point into 2D window coordinates. */
|
||||
project(out: Vec4, point: Vec3) {
|
||||
return cameraProject(out, point, this.viewport, this.projectionView);
|
||||
}
|
||||
|
||||
unproject(out: Vec3, point: Vec3) {
|
||||
/**
|
||||
* Transform point from screen space to 3D coordinates.
|
||||
* The point must have `x` and `y` set to 2D window coordinates
|
||||
* and `z` between 0 (near) and 1 (far); the optional `w` is not used.
|
||||
*/
|
||||
unproject(out: Vec3, point: Vec3 | Vec4) {
|
||||
return cameraUnproject(out, point, this.viewport, this.inverseProjectionView);
|
||||
}
|
||||
|
||||
/** World space pixel size at given `point` */
|
||||
getPixelSize(point: Vec3) {
|
||||
// project -> unproject of `point` does not exactly return the same
|
||||
// to get a sufficiently accurate measure we unproject the original
|
||||
// clip position in addition to the one shifted bey one pixel
|
||||
this.project(tmpClip, point);
|
||||
this.unproject(tmpPos1, tmpClip);
|
||||
tmpClip[0] += 1;
|
||||
this.unproject(tmpPos2, tmpClip);
|
||||
return Vec3.distance(tmpPos1, tmpPos2);
|
||||
}
|
||||
|
||||
constructor(state?: Partial<Camera.Snapshot>, viewport = Viewport.create(0, 0, 128, 128), props: Partial<{ pixelScale: number }> = {}) {
|
||||
this.viewport = viewport;
|
||||
this.pixelScale = props.pixelScale || 1;
|
||||
@@ -178,7 +200,7 @@ namespace Camera {
|
||||
/**
|
||||
* Sets an offseted view in a larger frustum. This is useful for
|
||||
* - multi-window or multi-monitor/multi-machine setups
|
||||
* - jittering the camera position for
|
||||
* - jittering the camera position for sampling
|
||||
*/
|
||||
export interface ViewOffset {
|
||||
enabled: boolean,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -55,14 +55,11 @@ namespace Viewport {
|
||||
|
||||
//
|
||||
|
||||
const NEAR_RANGE = 0;
|
||||
const FAR_RANGE = 1;
|
||||
|
||||
const tmpVec4 = Vec4();
|
||||
|
||||
/** Transform point into 2D window coordinates. */
|
||||
export function cameraProject(out: Vec4, point: Vec3, viewport: Viewport, projectionView: Mat4) {
|
||||
const { x: vX, y: vY, width: vWidth, height: vHeight } = viewport;
|
||||
const { x, y, width, height } = viewport;
|
||||
|
||||
// clip space -> NDC -> window coordinates, implicit 1.0 for w component
|
||||
Vec4.set(tmpVec4, point[0], point[1], point[2], 1.0);
|
||||
@@ -78,27 +75,28 @@ export function cameraProject(out: Vec4, point: Vec3, viewport: Viewport, projec
|
||||
tmpVec4[2] /= w;
|
||||
}
|
||||
|
||||
// transform into window coordinates, set fourth component is (1/clip.w) as in gl_FragCoord.w
|
||||
out[0] = vX + vWidth / 2 * tmpVec4[0] + (0 + vWidth / 2);
|
||||
out[1] = vY + vHeight / 2 * tmpVec4[1] + (0 + vHeight / 2);
|
||||
out[2] = (FAR_RANGE - NEAR_RANGE) / 2 * tmpVec4[2] + (FAR_RANGE + NEAR_RANGE) / 2;
|
||||
// transform into window coordinates, set fourth component to 1 / clip.w as in gl_FragCoord.w
|
||||
out[0] = (tmpVec4[0] + 1) * width * 0.5 + x;
|
||||
out[1] = (1 - tmpVec4[1]) * height * 0.5 + y; // flip Y
|
||||
out[2] = (tmpVec4[2] + 1) * 0.5;
|
||||
out[3] = w === 0 ? 0 : 1 / w;
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform point from screen space to 3D coordinates.
|
||||
* The point must have x and y set to 2D window coordinates and z between 0 (near) and 1 (far).
|
||||
* The point must have `x` and `y` set to 2D window coordinates
|
||||
* and `z` between 0 (near) and 1 (far); the optional `w` is not used.
|
||||
*/
|
||||
export function cameraUnproject(out: Vec3, point: Vec3, viewport: Viewport, inverseProjectionView: Mat4) {
|
||||
const { x: vX, y: vY, width: vWidth, height: vHeight } = viewport;
|
||||
export function cameraUnproject(out: Vec3, point: Vec3 | Vec4, viewport: Viewport, inverseProjectionView: Mat4) {
|
||||
const { x, y, width, height } = viewport;
|
||||
|
||||
const x = point[0] - vX;
|
||||
const y = (vHeight - point[1] - 1) - vY;
|
||||
const z = point[2];
|
||||
const px = point[0] - x;
|
||||
const py = (height - point[1] - 1) - y;
|
||||
const pz = point[2];
|
||||
|
||||
out[0] = (2 * x) / vWidth - 1;
|
||||
out[1] = (2 * y) / vHeight - 1;
|
||||
out[2] = 2 * z - 1;
|
||||
out[0] = (2 * px) / width - 1;
|
||||
out[1] = (2 * py) / height - 1;
|
||||
out[2] = 2 * pz - 1;
|
||||
return Vec3.transformMat4(out, out, inverseProjectionView);
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import { Camera } from './camera';
|
||||
import { ParamDefinition as PD } from '../mol-util/param-definition';
|
||||
import { DebugHelperParams } from './helper/bounding-sphere-helper';
|
||||
import { SetUtils } from '../mol-util/set';
|
||||
import { Canvas3dInteractionHelper } from './helper/interaction-events';
|
||||
import { Canvas3dInteractionHelper, Canvas3dInteractionHelperParams } from './helper/interaction-events';
|
||||
import { PostprocessingParams } from './passes/postprocessing';
|
||||
import { MultiSampleHelper, MultiSampleParams, MultiSamplePass } from './passes/multi-sample';
|
||||
import { PickData } from './passes/pick';
|
||||
@@ -84,6 +84,7 @@ export const Canvas3DParams = {
|
||||
marking: PD.Group(MarkingParams),
|
||||
renderer: PD.Group(RendererParams),
|
||||
trackball: PD.Group(TrackballControlsParams),
|
||||
interaction: PD.Group(Canvas3dInteractionHelperParams),
|
||||
debug: PD.Group(DebugHelperParams),
|
||||
handle: PD.Group(HandleHelperParams),
|
||||
};
|
||||
@@ -115,6 +116,8 @@ namespace Canvas3DContext {
|
||||
preserveDrawingBuffer: true,
|
||||
pixelScale: 1,
|
||||
pickScale: 0.25,
|
||||
/** extra pixels to around target to check in case target is empty */
|
||||
pickPadding: 1,
|
||||
enableWboit: true,
|
||||
preferWebGl1: false
|
||||
};
|
||||
@@ -307,8 +310,8 @@ namespace Canvas3D {
|
||||
const renderer = Renderer.create(webgl, p.renderer);
|
||||
const helper = new Helper(webgl, scene, p);
|
||||
|
||||
const pickHelper = new PickHelper(webgl, renderer, scene, helper, passes.pick, { x, y, width, height });
|
||||
const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input, camera);
|
||||
const pickHelper = new PickHelper(webgl, renderer, scene, helper, passes.pick, { x, y, width, height }, attribs.pickPadding);
|
||||
const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input, camera, p.interaction);
|
||||
const multiSampleHelper = new MultiSampleHelper(passes.multiSample);
|
||||
|
||||
let cameraResetRequested = false;
|
||||
@@ -644,6 +647,7 @@ namespace Canvas3D {
|
||||
multiSample: { ...p.multiSample },
|
||||
renderer: { ...renderer.props },
|
||||
trackball: { ...controls.props },
|
||||
interaction: { ...interactionHelper.props },
|
||||
debug: { ...helper.debug.props },
|
||||
handle: { ...helper.handle.props },
|
||||
};
|
||||
@@ -780,6 +784,7 @@ namespace Canvas3D {
|
||||
if (props.multiSample) Object.assign(p.multiSample, props.multiSample);
|
||||
if (props.renderer) renderer.setProps(props.renderer);
|
||||
if (props.trackball) controls.setProps(props.trackball);
|
||||
if (props.interaction) interactionHelper.setProps(props.interaction);
|
||||
if (props.debug) helper.debug.setProps(props.debug);
|
||||
if (props.handle) helper.handle.setProps(props.handle);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
@@ -11,6 +11,8 @@ import { InputObserver, ModifiersKeys, ButtonsType } from '../../mol-util/input/
|
||||
import { RxEventHelper } from '../../mol-util/rx-event-helper';
|
||||
import { Vec2, Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { Camera } from '../camera';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Bond } from '../../mol-model/structure';
|
||||
|
||||
type Canvas3D = import('../canvas3d').Canvas3D
|
||||
type HoverEvent = import('../canvas3d').Canvas3D.HoverEvent
|
||||
@@ -19,6 +21,17 @@ type ClickEvent = import('../canvas3d').Canvas3D.ClickEvent
|
||||
|
||||
const enum InputEvent { Move, Click, Drag }
|
||||
|
||||
const tmpPosA = Vec3();
|
||||
const tmpPos = Vec3();
|
||||
const tmpNorm = Vec3();
|
||||
|
||||
export const Canvas3dInteractionHelperParams = {
|
||||
maxFps: PD.Numeric(30, { min: 10, max: 60, step: 10 }),
|
||||
preferAtomPixelPadding: PD.Numeric(3, { min: 0, max: 20, step: 1 }, { description: 'Number of extra pixels at which to prefer atoms over bonds.' }),
|
||||
};
|
||||
export type Canvas3dInteractionHelperParams = typeof Canvas3dInteractionHelperParams
|
||||
export type Canvas3dInteractionHelperProps = PD.Values<Canvas3dInteractionHelperParams>
|
||||
|
||||
export class Canvas3dInteractionHelper {
|
||||
private ev = RxEventHelper.create();
|
||||
|
||||
@@ -48,6 +61,12 @@ export class Canvas3dInteractionHelper {
|
||||
private button: ButtonsType.Flag = ButtonsType.create(0);
|
||||
private modifiers: ModifiersKeys = ModifiersKeys.None;
|
||||
|
||||
readonly props: Canvas3dInteractionHelperProps;
|
||||
|
||||
setProps(props: Partial<Canvas3dInteractionHelperProps>) {
|
||||
Object.assign(this.props, props);
|
||||
}
|
||||
|
||||
private identify(e: InputEvent, t: number) {
|
||||
const xyChanged = this.startX !== this.endX || this.startY !== this.endY;
|
||||
|
||||
@@ -70,7 +89,7 @@ export class Canvas3dInteractionHelper {
|
||||
}
|
||||
|
||||
if (e === InputEvent.Click) {
|
||||
const loci = this.getLoci(this.id);
|
||||
const loci = this.getLoci(this.id, this.position);
|
||||
this.events.click.next({ current: loci, buttons: this.buttons, button: this.button, modifiers: this.modifiers, page: Vec2.create(this.endX, this.endY), position: this.position });
|
||||
this.prevLoci = loci;
|
||||
return;
|
||||
@@ -78,13 +97,13 @@ export class Canvas3dInteractionHelper {
|
||||
|
||||
if (!this.inside || this.currentIdentifyT !== t || !xyChanged || this.outsideViewport(this.endX, this.endY)) return;
|
||||
|
||||
const loci = this.getLoci(this.id);
|
||||
const loci = this.getLoci(this.id, this.position);
|
||||
this.events.hover.next({ current: loci, buttons: this.buttons, button: this.button, modifiers: this.modifiers, page: Vec2.create(this.endX, this.endY), position: this.position });
|
||||
this.prevLoci = loci;
|
||||
}
|
||||
|
||||
tick(t: number) {
|
||||
if (this.inside && t - this.prevT > 1000 / this.maxFps) {
|
||||
if (this.inside && t - this.prevT > 1000 / this.props.maxFps) {
|
||||
this.prevT = t;
|
||||
this.currentIdentifyT = t;
|
||||
this.identify(this.isInteracting ? InputEvent.Drag : InputEvent.Move, t);
|
||||
@@ -144,11 +163,34 @@ export class Canvas3dInteractionHelper {
|
||||
);
|
||||
}
|
||||
|
||||
private getLoci(pickingId: PickingId | undefined, position: Vec3 | undefined) {
|
||||
const { repr, loci } = this.lociGetter(pickingId);
|
||||
if (position && repr && Bond.isLoci(loci) && loci.bonds.length === 2) {
|
||||
const { aUnit, aIndex } = loci.bonds[0];
|
||||
aUnit.conformation.position(aUnit.elements[aIndex], tmpPosA);
|
||||
Vec3.sub(tmpNorm, this.camera.state.position, this.camera.state.target);
|
||||
Vec3.projectPointOnPlane(tmpPos, position, tmpNorm, tmpPosA);
|
||||
const pixelSize = this.camera.getPixelSize(tmpPos);
|
||||
let radius = repr.theme.size.size(loci.bonds[0]) * (repr.props.sizeFactor ?? 1);
|
||||
if (repr.props.lineSizeAttenuation === false) {
|
||||
// divide by two to get radius
|
||||
radius *= pixelSize / 2;
|
||||
}
|
||||
radius += this.props.preferAtomPixelPadding * pixelSize;
|
||||
if (Vec3.distance(tmpPos, tmpPosA) < radius) {
|
||||
return { repr, loci: Bond.toFirstStructureElementLoci(loci) };
|
||||
}
|
||||
}
|
||||
return { repr, loci };
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.ev.dispose();
|
||||
}
|
||||
|
||||
constructor(private canvasIdentify: Canvas3D['identify'], private getLoci: Canvas3D['getLoci'], private input: InputObserver, private camera: Camera, private maxFps: number = 30) {
|
||||
constructor(private canvasIdentify: Canvas3D['identify'], private lociGetter: Canvas3D['getLoci'], private input: InputObserver, private camera: Camera, props: Partial<Canvas3dInteractionHelperProps> = {}) {
|
||||
this.props = { ...PD.getDefaultValues(Canvas3dInteractionHelperParams), ...props };
|
||||
|
||||
input.drag.subscribe(({ x, y, buttons, button, modifiers }) => {
|
||||
this.isInteracting = true;
|
||||
// console.log('drag');
|
||||
|
||||
@@ -22,11 +22,11 @@ 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 { copy_frag } from '../../mol-gl/shader/copy.frag';
|
||||
import { StereoCamera } from '../camera/stereo';
|
||||
import { WboitPass } from './wboit';
|
||||
import { AntialiasingPass, PostprocessingPass, PostprocessingProps } from './postprocessing';
|
||||
import { MarkingPass, MarkingProps } from './marking';
|
||||
import { CopyRenderable, createCopyRenderable } from '../../mol-gl/compute/util';
|
||||
|
||||
const DepthMergeSchema = {
|
||||
...QuadSchema,
|
||||
@@ -53,27 +53,6 @@ function getDepthMergeRenderable(ctx: WebGLContext, depthTexturePrimitives: Text
|
||||
return createComputeRenderable(renderItem, values);
|
||||
}
|
||||
|
||||
const CopySchema = {
|
||||
...QuadSchema,
|
||||
tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
uTexSize: UniformSpec('v2'),
|
||||
};
|
||||
const CopyShaderCode = ShaderCode('copy', quad_vert, copy_frag);
|
||||
type CopyRenderable = ComputeRenderable<Values<typeof CopySchema>>
|
||||
|
||||
function getCopyRenderable(ctx: WebGLContext, colorTexture: Texture): CopyRenderable {
|
||||
const values: Values<typeof CopySchema> = {
|
||||
...QuadValues,
|
||||
tColor: ValueCell.create(colorTexture),
|
||||
uTexSize: ValueCell.create(Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())),
|
||||
};
|
||||
|
||||
const schema = { ...CopySchema };
|
||||
const renderItem = createComputeRenderItem(ctx, 'triangles', CopyShaderCode, schema, values);
|
||||
|
||||
return createComputeRenderable(renderItem, values);
|
||||
}
|
||||
|
||||
export class DrawPass {
|
||||
private readonly drawTarget: RenderTarget
|
||||
|
||||
@@ -128,8 +107,8 @@ export class DrawPass {
|
||||
this.postprocessing = new PostprocessingPass(webgl, this);
|
||||
this.antialiasing = new AntialiasingPass(webgl, this);
|
||||
|
||||
this.copyFboTarget = getCopyRenderable(webgl, this.colorTarget.texture);
|
||||
this.copyFboPostprocessing = getCopyRenderable(webgl, this.postprocessing.target.texture);
|
||||
this.copyFboTarget = createCopyRenderable(webgl, this.colorTarget.texture);
|
||||
this.copyFboPostprocessing = createCopyRenderable(webgl, this.postprocessing.target.texture);
|
||||
}
|
||||
|
||||
reset() {
|
||||
@@ -362,6 +341,7 @@ export class DrawPass {
|
||||
render(renderer: Renderer, camera: Camera | StereoCamera, scene: Scene, helper: Helper, toDrawingBuffer: boolean, transparentBackground: boolean, postprocessingProps: PostprocessingProps, markingProps: MarkingProps) {
|
||||
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, transparentBackground, postprocessingProps, markingProps);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -11,6 +11,7 @@ import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { GraphicsRenderVariant } from '../../mol-gl/webgl/render-item';
|
||||
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { spiral2d } from '../../mol-math/misc';
|
||||
import { decodeFloatRGB, unpackRGBAToDepth } from '../../mol-util/float-packing';
|
||||
import { Camera, ICamera } from '../camera';
|
||||
import { StereoCamera } from '../camera/stereo';
|
||||
@@ -88,6 +89,7 @@ export class PickPass {
|
||||
|
||||
this.groupPickTarget.bind();
|
||||
this.renderVariant(renderer, camera, scene, helper, 'pickGroup');
|
||||
// printTexture(this.webgl, this.groupPickTarget.texture, { id: 'group' })
|
||||
|
||||
this.depthPickTarget.bind();
|
||||
this.renderVariant(renderer, camera, scene, helper, 'depth');
|
||||
@@ -111,6 +113,8 @@ export class PickHelper {
|
||||
private pickHeight: number
|
||||
private halfPickWidth: number
|
||||
|
||||
private spiral: [number, number][]
|
||||
|
||||
private setupBuffers() {
|
||||
const bufferSize = this.pickWidth * this.pickHeight * 4;
|
||||
if (!this.objectBuffer || this.objectBuffer.length !== bufferSize) {
|
||||
@@ -138,6 +142,8 @@ export class PickHelper {
|
||||
|
||||
this.setupBuffers();
|
||||
}
|
||||
|
||||
this.spiral = spiral2d(Math.round(this.pickScale * this.pickPadding));
|
||||
}
|
||||
|
||||
private syncBuffers() {
|
||||
@@ -177,6 +183,7 @@ export class PickHelper {
|
||||
|
||||
renderer.setTransparentBackground(false);
|
||||
renderer.setDrawingBufferSize(this.pickPass.objectPickTarget.getWidth(), this.pickPass.objectPickTarget.getHeight());
|
||||
renderer.setPixelRatio(this.pickScale);
|
||||
|
||||
if (StereoCamera.is(camera)) {
|
||||
renderer.setViewport(pickX, pickY, halfPickWidth, pickHeight);
|
||||
@@ -192,7 +199,7 @@ export class PickHelper {
|
||||
this.dirty = false;
|
||||
}
|
||||
|
||||
identify(x: number, y: number, camera: Camera | StereoCamera): PickData | undefined {
|
||||
private identifyInternal(x: number, y: number, camera: Camera | StereoCamera): PickData | undefined {
|
||||
const { webgl, pickScale } = this;
|
||||
if (webgl.isContextLost) return;
|
||||
|
||||
@@ -251,7 +258,14 @@ export class PickHelper {
|
||||
return { id: { objectId, instanceId, groupId }, position };
|
||||
}
|
||||
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private helper: Helper, private pickPass: PickPass, viewport: Viewport) {
|
||||
identify(x: number, y: number, camera: Camera | StereoCamera): PickData | undefined {
|
||||
for (const d of this.spiral) {
|
||||
const pickData = this.identifyInternal(x + d[0], y + d[1], camera);
|
||||
if (pickData) return pickData;
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private helper: Helper, private pickPass: PickPass, viewport: Viewport, readonly pickPadding = 1) {
|
||||
this.setViewport(viewport.x, viewport.y, viewport.width, viewport.height);
|
||||
}
|
||||
}
|
||||
@@ -193,6 +193,7 @@ const PostprocessingSchema = {
|
||||
uFogNear: UniformSpec('f'),
|
||||
uFogFar: UniformSpec('f'),
|
||||
uFogColor: UniformSpec('v3'),
|
||||
uOutlineColor: UniformSpec('v3'),
|
||||
uTransparentBackground: UniformSpec('b'),
|
||||
|
||||
uMaxPossibleViewZDiff: UniformSpec('f'),
|
||||
@@ -220,6 +221,7 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d
|
||||
uFogNear: ValueCell.create(10000),
|
||||
uFogFar: ValueCell.create(10000),
|
||||
uFogColor: ValueCell.create(Vec3.create(1, 1, 1)),
|
||||
uOutlineColor: ValueCell.create(Vec3.create(0, 0, 0)),
|
||||
uTransparentBackground: ValueCell.create(false),
|
||||
|
||||
uMaxPossibleViewZDiff: ValueCell.create(0.5),
|
||||
@@ -252,6 +254,7 @@ export const PostprocessingParams = {
|
||||
on: PD.Group({
|
||||
scale: PD.Numeric(1, { min: 1, max: 5, step: 1 }),
|
||||
threshold: PD.Numeric(0.33, { min: 0.01, max: 1, step: 0.01 }),
|
||||
color: PD.Color(Color(0x000000)),
|
||||
}),
|
||||
off: PD.Group({})
|
||||
}, { cycle: true, description: 'Draw outline around 3D objects' }),
|
||||
@@ -446,6 +449,8 @@ export class PostprocessingPass {
|
||||
ValueCell.updateIfChanged(this.outlinesRenderable.values.uFar, camera.far);
|
||||
ValueCell.updateIfChanged(this.outlinesRenderable.values.uMaxPossibleViewZDiff, maxPossibleViewZDiff);
|
||||
|
||||
ValueCell.update(this.renderable.values.uOutlineColor, Color.toVec3Normalized(this.renderable.values.uOutlineColor.ref.value, props.outline.params.color));
|
||||
|
||||
ValueCell.updateIfChanged(this.renderable.values.uMaxPossibleViewZDiff, maxPossibleViewZDiff);
|
||||
if (this.renderable.values.dOutlineScale.ref.value !== outlineScale) { needsUpdateMain = true; }
|
||||
ValueCell.updateIfChanged(this.renderable.values.dOutlineScale, outlineScale);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -15,6 +15,8 @@ import { ColorNames } from '../../mol-util/color/names';
|
||||
import { NullLocation } from '../../mol-model/location';
|
||||
import { UniformColorTheme } from '../../mol-theme/color/uniform';
|
||||
import { UniformSizeTheme } from '../../mol-theme/size/uniform';
|
||||
import { smoothstep } from '../../mol-math/interpolate';
|
||||
import { Material } from '../../mol-util/material';
|
||||
|
||||
export const VisualQualityInfo = {
|
||||
'custom': {},
|
||||
@@ -33,19 +35,55 @@ export const VisualQualityOptions = PD.arrayToOptions(VisualQualityNames);
|
||||
|
||||
//
|
||||
|
||||
export namespace BaseGeometry {
|
||||
export const Params = {
|
||||
alpha: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }, { label: 'Opacity', isEssential: true, description: 'How opaque/transparent the representation is rendered.' }),
|
||||
quality: PD.Select<VisualQuality>('auto', VisualQualityOptions, { isEssential: true, description: 'Visual/rendering quality of the representation.' }),
|
||||
};
|
||||
export type Params = typeof Params
|
||||
export const ColorSmoothingParams = {
|
||||
smoothColors: PD.MappedStatic('auto', {
|
||||
auto: PD.Group({}),
|
||||
on: PD.Group({
|
||||
resolutionFactor: PD.Numeric(2, { min: 0.5, max: 6, step: 0.1 }),
|
||||
sampleStride: PD.Numeric(3, { min: 1, max: 12, step: 1 }),
|
||||
}),
|
||||
off: PD.Group({})
|
||||
}),
|
||||
};
|
||||
export type ColorSmoothingParams = typeof ColorSmoothingParams
|
||||
|
||||
export function hasColorSmoothingProp(props: PD.Values<any>): props is PD.Values<ColorSmoothingParams> {
|
||||
return !!props.smoothColors;
|
||||
}
|
||||
|
||||
export function getColorSmoothingProps(smoothColors: PD.Values<ColorSmoothingParams>['smoothColors'], preferSmoothing?: boolean, resolution?: number) {
|
||||
if ((smoothColors.name === 'on' || (smoothColors.name === 'auto' && preferSmoothing)) && resolution && resolution < 3) {
|
||||
let stride = 3;
|
||||
if (smoothColors.name === 'on') {
|
||||
resolution *= smoothColors.params.resolutionFactor;
|
||||
stride = smoothColors.params.sampleStride;
|
||||
} else {
|
||||
// https://graphtoy.com/?f1(x,t)=(2-smoothstep(0,1.1,x))*x&coords=0.7,0.6,1.8
|
||||
resolution *= 2 - smoothstep(0, 1.1, resolution);
|
||||
resolution = Math.max(0.5, resolution);
|
||||
if (resolution > 1.2) stride = 2;
|
||||
}
|
||||
return { resolution, stride };
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
export namespace BaseGeometry {
|
||||
export const MaterialCategory: PD.Info = { category: 'Material' };
|
||||
export const ShadingCategory: PD.Info = { category: 'Shading' };
|
||||
export const CustomQualityParamInfo: PD.Info = {
|
||||
category: 'Custom Quality',
|
||||
hideIf: (params: PD.Values<Params>) => typeof params.quality !== 'undefined' && params.quality !== 'custom'
|
||||
};
|
||||
|
||||
export const Params = {
|
||||
alpha: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }, { label: 'Opacity', isEssential: true, description: 'How opaque/transparent the representation is rendered.' }),
|
||||
quality: PD.Select<VisualQuality>('auto', VisualQualityOptions, { isEssential: true, description: 'Visual/rendering quality of the representation.' }),
|
||||
material: Material.getParam(),
|
||||
};
|
||||
export type Params = typeof Params
|
||||
|
||||
export type Counts = { drawCount: number, vertexCount: number, groupCount: number, instanceCount: number }
|
||||
|
||||
export function createSimple(colorValue = ColorNames.grey, sizeValue = 1, transform?: TransformData) {
|
||||
@@ -65,11 +103,18 @@ export namespace BaseGeometry {
|
||||
uVertexCount: ValueCell.create(counts.vertexCount),
|
||||
uGroupCount: ValueCell.create(counts.groupCount),
|
||||
drawCount: ValueCell.create(counts.drawCount),
|
||||
uMetalness: ValueCell.create(props.material.metalness),
|
||||
uRoughness: ValueCell.create(props.material.roughness),
|
||||
uBumpiness: ValueCell.create(props.material.bumpiness),
|
||||
dLightCount: ValueCell.create(1),
|
||||
};
|
||||
}
|
||||
|
||||
export function updateValues(values: BaseValues, props: PD.Values<Params>) {
|
||||
ValueCell.updateIfChanged(values.alpha, props.alpha); // `uAlpha` is set in renderable.render
|
||||
ValueCell.updateIfChanged(values.uMetalness, props.material.metalness);
|
||||
ValueCell.updateIfChanged(values.uRoughness, props.material.roughness);
|
||||
ValueCell.updateIfChanged(values.uBumpiness, props.material.bumpiness);
|
||||
}
|
||||
|
||||
export function createRenderableState(props: Partial<PD.Values<Params>> = {}): RenderableState {
|
||||
|
||||
@@ -25,6 +25,7 @@ import { hashFnv32a } from '../../../mol-data/util';
|
||||
import { createEmptyClipping } from '../clipping-data';
|
||||
import { CylindersValues } from '../../../mol-gl/renderable/cylinders';
|
||||
import { RenderableState } from '../../../mol-gl/renderable';
|
||||
import { createEmptySubstance } from '../substance-data';
|
||||
|
||||
export interface Cylinders {
|
||||
readonly kind: 'cylinders',
|
||||
@@ -156,6 +157,8 @@ export namespace Cylinders {
|
||||
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
|
||||
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
xrayShaded: PD.Boolean(false, 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),
|
||||
};
|
||||
export type Params = typeof Params
|
||||
|
||||
@@ -200,6 +203,7 @@ export namespace Cylinders {
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const material = createEmptySubstance();
|
||||
const clipping = createEmptyClipping();
|
||||
|
||||
const counts = { drawCount: cylinders.cylinderCount * 4 * 3, vertexCount: cylinders.cylinderCount * 6, groupCount, instanceCount };
|
||||
@@ -224,6 +228,7 @@ export namespace Cylinders {
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...material,
|
||||
...clipping,
|
||||
...transform,
|
||||
|
||||
@@ -234,6 +239,8 @@ export namespace Cylinders {
|
||||
dDoubleSided: ValueCell.create(props.doubleSided),
|
||||
dIgnoreLight: ValueCell.create(props.ignoreLight),
|
||||
dXrayShaded: ValueCell.create(props.xrayShaded),
|
||||
uBumpFrequency: ValueCell.create(props.bumpFrequency),
|
||||
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -249,6 +256,8 @@ export namespace Cylinders {
|
||||
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
|
||||
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
|
||||
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
|
||||
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
|
||||
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
|
||||
}
|
||||
|
||||
function updateBoundingSphere(values: CylindersValues, cylinders: Cylinders) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import { LocationIterator, PositionLocation } from '../../../mol-geo/util/locati
|
||||
import { RenderableState } from '../../../mol-gl/renderable';
|
||||
import { DirectVolumeValues } from '../../../mol-gl/renderable/direct-volume';
|
||||
import { calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { Texture } from '../../../mol-gl/webgl/texture';
|
||||
import { createNullTexture, Texture } from '../../../mol-gl/webgl/texture';
|
||||
import { Box3D, Sphere3D } from '../../../mol-math/geometry';
|
||||
import { Mat4, Vec2, Vec3, Vec4 } from '../../../mol-math/linear-algebra';
|
||||
import { Theme } from '../../../mol-theme/theme';
|
||||
@@ -28,6 +28,7 @@ import { createTransferFunctionTexture, getControlPointsFromVec2Array } from './
|
||||
import { createEmptyClipping } from '../clipping-data';
|
||||
import { Grid, Volume } from '../../../mol-model/volume';
|
||||
import { ColorNames } from '../../../mol-util/color/names';
|
||||
import { createEmptySubstance } from '../substance-data';
|
||||
|
||||
const VolumeBox = Box();
|
||||
|
||||
@@ -129,7 +130,15 @@ export namespace DirectVolume {
|
||||
}
|
||||
|
||||
export function createEmpty(directVolume?: DirectVolume): DirectVolume {
|
||||
return {} as DirectVolume; // TODO
|
||||
const bbox = Box3D();
|
||||
const gridDimension = Vec3();
|
||||
const transform = Mat4.identity();
|
||||
const unitToCartn = Mat4.identity();
|
||||
const cellDim = Vec3();
|
||||
const texture = createNullTexture();
|
||||
const stats = Grid.One.stats;
|
||||
const packedGroup = false;
|
||||
return create(bbox, gridDimension, transform, unitToCartn, cellDim, texture, stats, packedGroup, directVolume);
|
||||
}
|
||||
|
||||
export function createRenderModeParam(stats?: Grid['stats']) {
|
||||
@@ -238,6 +247,7 @@ export namespace DirectVolume {
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const material = createEmptySubstance();
|
||||
const clipping = createEmptyClipping();
|
||||
|
||||
const [x, y, z] = gridDimension.ref.value;
|
||||
@@ -262,6 +272,7 @@ export namespace DirectVolume {
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...material,
|
||||
...clipping,
|
||||
...transform,
|
||||
...BaseGeometry.createValues(props, counts),
|
||||
@@ -311,8 +322,7 @@ export namespace DirectVolume {
|
||||
}
|
||||
|
||||
function updateValues(values: DirectVolumeValues, props: PD.Values<Params>) {
|
||||
ValueCell.updateIfChanged(values.alpha, props.alpha);
|
||||
ValueCell.updateIfChanged(values.uAlpha, props.alpha);
|
||||
BaseGeometry.updateValues(values, props);
|
||||
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
|
||||
ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded);
|
||||
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
import { LocationIterator } from '../../../mol-geo/util/location-iterator';
|
||||
import { RenderableState } from '../../../mol-gl/renderable';
|
||||
import { calculateTransformBoundingSphere, TextureImage } from '../../../mol-gl/renderable/util';
|
||||
import { calculateTransformBoundingSphere, createTextureImage, TextureImage } from '../../../mol-gl/renderable/util';
|
||||
import { Sphere3D } from '../../../mol-math/geometry';
|
||||
import { Vec2, Vec4, Vec3 } from '../../../mol-math/linear-algebra';
|
||||
import { Theme } from '../../../mol-theme/theme';
|
||||
@@ -26,6 +26,7 @@ import { fillSerial } from '../../../mol-util/array';
|
||||
import { createEmptyClipping } from '../clipping-data';
|
||||
import { NullLocation } from '../../../mol-model/location';
|
||||
import { QuadPositions } from '../../../mol-gl/compute/util';
|
||||
import { createEmptySubstance } from '../substance-data';
|
||||
|
||||
const QuadIndices = new Uint32Array([
|
||||
0, 1, 2,
|
||||
@@ -113,7 +114,10 @@ namespace Image {
|
||||
}
|
||||
|
||||
export function createEmpty(image?: Image): Image {
|
||||
return {} as Image; // TODO
|
||||
const imageTexture = createTextureImage(0, 4, Uint8Array);
|
||||
const corners = image ? image.cornerBuffer.ref.value : new Float32Array(8 * 3);
|
||||
const groupTexture = createTextureImage(0, 4, Uint8Array);
|
||||
return create(imageTexture, corners, groupTexture, image);
|
||||
}
|
||||
|
||||
export const Params = {
|
||||
@@ -142,6 +146,7 @@ namespace Image {
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const material = createEmptySubstance();
|
||||
const clipping = createEmptyClipping();
|
||||
|
||||
const counts = { drawCount: QuadIndices.length, vertexCount: QuadPositions.length / 3, groupCount, instanceCount };
|
||||
@@ -154,6 +159,7 @@ namespace Image {
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...material,
|
||||
...clipping,
|
||||
...transform,
|
||||
...BaseGeometry.createValues(props, counts),
|
||||
|
||||
@@ -26,6 +26,7 @@ import { createEmptyOverpaint } from '../overpaint-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
import { createEmptyClipping } from '../clipping-data';
|
||||
import { createEmptySubstance } from '../substance-data';
|
||||
|
||||
/** Wide line */
|
||||
export interface Lines {
|
||||
@@ -164,7 +165,7 @@ export namespace Lines {
|
||||
|
||||
export const Params = {
|
||||
...BaseGeometry.Params,
|
||||
sizeFactor: PD.Numeric(1.5, { min: 0, max: 10, step: 0.1 }),
|
||||
sizeFactor: PD.Numeric(3, { min: 0, max: 10, step: 0.1 }),
|
||||
lineSizeAttenuation: PD.Boolean(false),
|
||||
};
|
||||
export type Params = typeof Params
|
||||
@@ -210,6 +211,7 @@ export namespace Lines {
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const material = createEmptySubstance();
|
||||
const clipping = createEmptyClipping();
|
||||
|
||||
const counts = { drawCount: lines.lineCount * 2 * 3, vertexCount: lines.lineCount * 4, groupCount, instanceCount };
|
||||
@@ -231,6 +233,7 @@ export namespace Lines {
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...material,
|
||||
...clipping,
|
||||
...transform,
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
@@ -40,7 +40,7 @@ const v3set = Vec3.set;
|
||||
const caAdd3 = ChunkedArray.add3;
|
||||
const caAdd = ChunkedArray.add;
|
||||
|
||||
function addCap(offset: number, state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, width: number, leftHeight: number, rightHeight: number) {
|
||||
function addCap(offset: number, state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, width: number, leftHeight: number, rightHeight: number, flip: boolean) {
|
||||
const { vertices, normals, indices } = state;
|
||||
const vertexCount = vertices.elementCount;
|
||||
|
||||
@@ -74,11 +74,19 @@ function addCap(offset: number, state: MeshBuilder.State, controlPoints: ArrayLi
|
||||
v3copy(verticalVector, verticalLeftVector);
|
||||
}
|
||||
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]);
|
||||
if (flip) {
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
caAdd3(normals, -normalVector[0], -normalVector[1], -normalVector[2]);
|
||||
}
|
||||
caAdd3(indices, vertexCount, vertexCount + 1, vertexCount + 2);
|
||||
caAdd3(indices, vertexCount + 2, vertexCount + 3, vertexCount);
|
||||
} else {
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]);
|
||||
}
|
||||
caAdd3(indices, vertexCount + 2, vertexCount + 1, vertexCount);
|
||||
caAdd3(indices, vertexCount, vertexCount + 3, vertexCount + 2);
|
||||
}
|
||||
caAdd3(indices, vertexCount + 2, vertexCount + 1, vertexCount);
|
||||
caAdd3(indices, vertexCount, vertexCount + 3, vertexCount + 2);
|
||||
}
|
||||
|
||||
/** set arrowHeight = 0 for no arrow */
|
||||
@@ -193,19 +201,18 @@ export function addSheet(state: MeshBuilder.State, controlPoints: ArrayLike<numb
|
||||
const width = widthValues[0];
|
||||
const height = heightValues[0];
|
||||
const h = arrowHeight === 0 ? height : arrowHeight;
|
||||
addCap(0, state, controlPoints, normalVectors, binormalVectors, width, h, h);
|
||||
addCap(0, state, controlPoints, normalVectors, binormalVectors, width, h, h, false);
|
||||
} else if (arrowHeight > 0) {
|
||||
const width = widthValues[0];
|
||||
const height = heightValues[0];
|
||||
addCap(0, state, controlPoints, normalVectors, binormalVectors, width, arrowHeight, -height);
|
||||
addCap(0, state, controlPoints, normalVectors, binormalVectors, width, -arrowHeight, height);
|
||||
addCap(0, state, controlPoints, normalVectors, binormalVectors, width, arrowHeight, -height, false);
|
||||
addCap(0, state, controlPoints, normalVectors, binormalVectors, width, -arrowHeight, height, false);
|
||||
}
|
||||
|
||||
if (endCap && arrowHeight === 0) {
|
||||
const width = widthValues[linearSegments];
|
||||
const height = heightValues[linearSegments];
|
||||
// use negative height to flip the direction the cap's triangles are facing
|
||||
addCap(linearSegments * 3, state, controlPoints, normalVectors, binormalVectors, width, -height, -height);
|
||||
addCap(linearSegments * 3, state, controlPoints, normalVectors, binormalVectors, width, height, height, true);
|
||||
}
|
||||
|
||||
const addedVertexCount = (linearSegments + 1) * 8 +
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { Vec3 } from '../../../../mol-math/linear-algebra';
|
||||
import { ChunkedArray } from '../../../../mol-data/util';
|
||||
import { cantorPairing, ChunkedArray } from '../../../../mol-data/util';
|
||||
import { MeshBuilder } from '../mesh-builder';
|
||||
|
||||
const normalVector = Vec3();
|
||||
@@ -30,32 +30,38 @@ function add3AndScale2(out: Vec3, a: Vec3, b: Vec3, c: Vec3, sa: number, sb: num
|
||||
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
|
||||
const v3fromArray = Vec3.fromArray;
|
||||
const v3normalize = Vec3.normalize;
|
||||
const v3negate = Vec3.negate;
|
||||
const v3copy = Vec3.copy;
|
||||
const v3scaleAndAdd = Vec3.scaleAndAdd;
|
||||
const v3cross = Vec3.cross;
|
||||
const v3dot = Vec3.dot;
|
||||
const v3unitX = Vec3.unitX;
|
||||
const caAdd3 = ChunkedArray.add3;
|
||||
|
||||
const CosSinCache = new Map<number, { cos: number[], sin: number[] }>();
|
||||
function getCosSin(radialSegments: number) {
|
||||
if (!CosSinCache.has(radialSegments)) {
|
||||
function getCosSin(radialSegments: number, shift: boolean) {
|
||||
const offset = shift ? 1 : 0;
|
||||
const hash = cantorPairing(radialSegments, offset);
|
||||
if (!CosSinCache.has(hash)) {
|
||||
const cos: number[] = [];
|
||||
const sin: number[] = [];
|
||||
for (let j = 0; j < radialSegments; ++j) {
|
||||
const t = 2 * Math.PI * j / radialSegments;
|
||||
const t = (j * 2 + offset) / radialSegments * Math.PI;
|
||||
cos[j] = Math.cos(t);
|
||||
sin[j] = Math.sin(t);
|
||||
}
|
||||
CosSinCache.set(radialSegments, { cos, sin });
|
||||
CosSinCache.set(hash, { cos, sin });
|
||||
}
|
||||
return CosSinCache.get(radialSegments)!;
|
||||
return CosSinCache.get(hash)!;
|
||||
}
|
||||
|
||||
export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, radialSegments: number, widthValues: ArrayLike<number>, heightValues: ArrayLike<number>, startCap: boolean, endCap: boolean) {
|
||||
export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, radialSegments: number, widthValues: ArrayLike<number>, heightValues: ArrayLike<number>, startCap: boolean, endCap: boolean, crossSection: 'elliptical' | 'rounded') {
|
||||
const { currentGroup, vertices, normals, indices, groups } = state;
|
||||
|
||||
let vertexCount = vertices.elementCount;
|
||||
|
||||
const { cos, sin } = getCosSin(radialSegments);
|
||||
const { cos, sin } = getCosSin(radialSegments, crossSection === 'rounded');
|
||||
|
||||
const q1 = Math.round(radialSegments / 4);
|
||||
const q3 = q1 * 3;
|
||||
|
||||
for (let i = 0; i <= linearSegments; ++i) {
|
||||
const i3 = i * 3;
|
||||
@@ -65,14 +71,24 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe
|
||||
|
||||
const width = widthValues[i];
|
||||
const height = heightValues[i];
|
||||
const rounded = crossSection === 'rounded' && height > width;
|
||||
|
||||
for (let j = 0; j < radialSegments; ++j) {
|
||||
add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[j], width * sin[j]);
|
||||
if (radialSegments === 2) {
|
||||
v3copy(normalVector, v);
|
||||
v3normalize(normalVector, normalVector);
|
||||
if (j !== 0 || i % 2 === 0) v3negate(normalVector, normalVector);
|
||||
if (rounded) {
|
||||
add3AndScale2(surfacePoint, u, v, controlPoint, width * cos[j], width * sin[j]);
|
||||
const h = v3dot(v, v3unitX) < 0
|
||||
? (j < q1 || j >= q3) ? height - width : -height + width
|
||||
: (j >= q1 && j < q3) ? -height + width : height - width;
|
||||
v3scaleAndAdd(surfacePoint, surfacePoint, u, h);
|
||||
if (j === q1 || j === q1 - 1) {
|
||||
add2AndScale2(normalVector, u, v, 0, 1);
|
||||
} else if (j === q3 || j === q3 - 1) {
|
||||
add2AndScale2(normalVector, u, v, 0, -1);
|
||||
} else {
|
||||
add2AndScale2(normalVector, u, v, cos[j], sin[j]);
|
||||
}
|
||||
} else {
|
||||
add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[j], width * sin[j]);
|
||||
add2AndScale2(normalVector, u, v, width * cos[j], height * sin[j]);
|
||||
}
|
||||
v3normalize(normalVector, normalVector);
|
||||
@@ -82,19 +98,37 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe
|
||||
}
|
||||
}
|
||||
|
||||
const radialSegmentsHalf = Math.round(radialSegments / 2);
|
||||
|
||||
for (let i = 0; i < linearSegments; ++i) {
|
||||
for (let j = 0; j < radialSegments; ++j) {
|
||||
// the triangles are arranged such that opposing triangles of the sheet align
|
||||
// which prevents triangle intersection within tight curves
|
||||
for (let j = 0; j < radialSegmentsHalf; ++j) {
|
||||
caAdd3(
|
||||
indices,
|
||||
vertexCount + i * radialSegments + (j + 1) % radialSegments,
|
||||
vertexCount + (i + 1) * radialSegments + (j + 1) % radialSegments,
|
||||
vertexCount + i * radialSegments + j
|
||||
vertexCount + i * radialSegments + (j + 1) % radialSegments, // a
|
||||
vertexCount + (i + 1) * radialSegments + (j + 1) % radialSegments, // c
|
||||
vertexCount + i * radialSegments + j // b
|
||||
);
|
||||
caAdd3(
|
||||
indices,
|
||||
vertexCount + (i + 1) * radialSegments + (j + 1) % radialSegments,
|
||||
vertexCount + (i + 1) * radialSegments + j,
|
||||
vertexCount + i * radialSegments + j
|
||||
vertexCount + (i + 1) * radialSegments + (j + 1) % radialSegments, // c
|
||||
vertexCount + (i + 1) * radialSegments + j, // d
|
||||
vertexCount + i * radialSegments + j // b
|
||||
);
|
||||
}
|
||||
for (let j = radialSegmentsHalf; j < radialSegments; ++j) {
|
||||
caAdd3(
|
||||
indices,
|
||||
vertexCount + i * radialSegments + (j + 1) % radialSegments, // a
|
||||
vertexCount + (i + 1) * radialSegments + j, // d
|
||||
vertexCount + i * radialSegments + j // b
|
||||
);
|
||||
caAdd3(
|
||||
indices,
|
||||
vertexCount + (i + 1) * radialSegments + (j + 1) % radialSegments, // c
|
||||
vertexCount + (i + 1) * radialSegments + j, // d
|
||||
vertexCount + i * radialSegments + (j + 1) % radialSegments, // a
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -111,11 +145,18 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe
|
||||
caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]);
|
||||
|
||||
const width = widthValues[0];
|
||||
const height = heightValues[0];
|
||||
let height = heightValues[0];
|
||||
const rounded = crossSection === 'rounded' && height > width;
|
||||
if (rounded) height -= width;
|
||||
|
||||
vertexCount = vertices.elementCount;
|
||||
for (let i = 0; i < radialSegments; ++i) {
|
||||
add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[i], width * sin[i]);
|
||||
if (rounded) {
|
||||
add3AndScale2(surfacePoint, u, v, controlPoint, width * cos[i], width * sin[i]);
|
||||
v3scaleAndAdd(surfacePoint, surfacePoint, u, (i < q1 || i >= q3) ? height : -height);
|
||||
} else {
|
||||
add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[i], width * sin[i]);
|
||||
}
|
||||
|
||||
caAdd3(vertices, surfacePoint[0], surfacePoint[1], surfacePoint[2]);
|
||||
caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]);
|
||||
@@ -141,11 +182,18 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe
|
||||
caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]);
|
||||
|
||||
const width = widthValues[linearSegments];
|
||||
const height = heightValues[linearSegments];
|
||||
let height = heightValues[linearSegments];
|
||||
const rounded = crossSection === 'rounded' && height > width;
|
||||
if (rounded) height -= width;
|
||||
|
||||
vertexCount = vertices.elementCount;
|
||||
for (let i = 0; i < radialSegments; ++i) {
|
||||
add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[i], width * sin[i]);
|
||||
if (rounded) {
|
||||
add3AndScale2(surfacePoint, u, v, controlPoint, width * cos[i], width * sin[i]);
|
||||
v3scaleAndAdd(surfacePoint, surfacePoint, u, (i < q1 || i >= q3) ? height : -height);
|
||||
} else {
|
||||
add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[i], width * sin[i]);
|
||||
}
|
||||
|
||||
caAdd3(vertices, surfacePoint[0], surfacePoint[1], surfacePoint[2]);
|
||||
caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]);
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { MeshValues } from '../../../mol-gl/renderable/mesh';
|
||||
import { createTextureImage, TextureImage } from '../../../mol-gl/renderable/util';
|
||||
import { WebGLContext } from '../../../mol-gl/webgl/context';
|
||||
import { Texture } from '../../../mol-gl/webgl/texture';
|
||||
import { Box3D, Sphere3D } from '../../../mol-math/geometry';
|
||||
import { lerp } from '../../../mol-math/interpolate';
|
||||
import { Vec2, Vec3, Vec4 } from '../../../mol-math/linear-algebra';
|
||||
import { getVolumeTexture2dLayout } from '../../../mol-repr/volume/util';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
import { ValueCell } from '../../../mol-util';
|
||||
|
||||
interface ColorSmoothingInput {
|
||||
vertexCount: number
|
||||
@@ -24,10 +26,11 @@ interface ColorSmoothingInput {
|
||||
colorType: 'group' | 'groupInstance'
|
||||
boundingSphere: Sphere3D
|
||||
invariantBoundingSphere: Sphere3D
|
||||
itemSize: 4 | 3 | 1
|
||||
}
|
||||
|
||||
export function calcMeshColorSmoothing(input: ColorSmoothingInput, resolution: number, stride: number, webgl?: WebGLContext, texture?: Texture) {
|
||||
const { colorType, vertexCount, groupCount, positionBuffer, transformBuffer, groupBuffer } = input;
|
||||
const { colorType, vertexCount, groupCount, positionBuffer, transformBuffer, groupBuffer, itemSize } = input;
|
||||
|
||||
const isInstanceType = colorType.endsWith('Instance');
|
||||
const box = Box3D.fromSphere3D(Box3D(), isInstanceType ? input.boundingSphere : input.invariantBoundingSphere);
|
||||
@@ -43,7 +46,6 @@ export function calcMeshColorSmoothing(input: ColorSmoothingInput, resolution: n
|
||||
const { width, height } = getVolumeTexture2dLayout(gridDim);
|
||||
// console.log({ width, height, dim });
|
||||
|
||||
const itemSize = 3;
|
||||
const data = new Float32Array(width * height * itemSize);
|
||||
const count = new Float32Array(width * height);
|
||||
|
||||
@@ -78,10 +80,7 @@ export function calcMeshColorSmoothing(input: ColorSmoothingInput, resolution: n
|
||||
const z = Math.floor(vz);
|
||||
|
||||
// group colors
|
||||
const ci = i * groupCount + groupBuffer[j];
|
||||
const r = colors[ci * 3];
|
||||
const g = colors[ci * 3 + 1];
|
||||
const b = colors[ci * 3 + 2];
|
||||
const ci = (i * groupCount + groupBuffer[j]) * itemSize;
|
||||
|
||||
// Extents of grid to consider for this atom
|
||||
const begX = Math.max(0, x - p);
|
||||
@@ -106,10 +105,10 @@ export function calcMeshColorSmoothing(input: ColorSmoothingInput, resolution: n
|
||||
|
||||
const s = p - d;
|
||||
const index = getIndex(xi, yi, zi);
|
||||
data[index] += r * s;
|
||||
data[index + 1] += g * s;
|
||||
data[index + 2] += b * s;
|
||||
count[index / 3] += s;
|
||||
for (let k = 0; k < itemSize; ++k) {
|
||||
data[index + k] += colors[ci + k] * s;
|
||||
}
|
||||
count[index / itemSize] += s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,11 +116,11 @@ export function calcMeshColorSmoothing(input: ColorSmoothingInput, resolution: n
|
||||
}
|
||||
|
||||
for (let i = 0, il = count.length; i < il; ++i) {
|
||||
const i3 = i * 3;
|
||||
const is = i * itemSize;
|
||||
const c = count[i];
|
||||
grid[i3] = Math.round(data[i3] / c);
|
||||
grid[i3 + 1] = Math.round(data[i3 + 1] / c);
|
||||
grid[i3 + 2] = Math.round(data[i3 + 2] / c);
|
||||
for (let k = 0; k < itemSize; ++k) {
|
||||
grid[is + k] = Math.round(data[is + k] / c);
|
||||
}
|
||||
}
|
||||
|
||||
const gridTexDim = Vec2.create(width, height);
|
||||
@@ -129,12 +128,16 @@ export function calcMeshColorSmoothing(input: ColorSmoothingInput, resolution: n
|
||||
const type = isInstanceType ? 'volumeInstance' as const : 'volume' as const;
|
||||
|
||||
if (webgl) {
|
||||
if (!texture) texture = webgl.resources.texture('image-uint8', 'rgb', 'ubyte', 'linear');
|
||||
if (!texture) {
|
||||
const format = itemSize === 4 ? 'rgba' :
|
||||
itemSize === 3 ? 'rgb' : 'alpha';
|
||||
texture = webgl.resources.texture('image-uint8', format, 'ubyte', 'linear');
|
||||
}
|
||||
texture.load(textureImage);
|
||||
|
||||
return { kind: 'volume' as const, texture, gridTexDim, gridDim, gridTransform, type };
|
||||
} else {
|
||||
const interpolated = getTrilinearlyInterpolated({ vertexCount, instanceCount, transformBuffer, positionBuffer, colorType: type, grid, gridDim, gridTexDim, gridTransform, vertexStride: 3, colorStride: 3 });
|
||||
const interpolated = getTrilinearlyInterpolated({ vertexCount, instanceCount, transformBuffer, positionBuffer, colorType: type, grid, gridDim, gridTexDim, gridTransform, vertexStride: 3, colorStride: itemSize, outputStride: itemSize });
|
||||
|
||||
return {
|
||||
kind: 'vertex' as const,
|
||||
@@ -157,16 +160,24 @@ interface ColorInterpolationInput {
|
||||
gridTexDim: Vec2
|
||||
gridDim: Vec3
|
||||
gridTransform: Vec4
|
||||
vertexStride: number
|
||||
colorStride: number
|
||||
vertexStride: 3 | 4
|
||||
colorStride: 1 | 3 | 4
|
||||
outputStride: 1 | 3 | 4
|
||||
itemOffset?: 0 | 1 | 2 | 3
|
||||
}
|
||||
|
||||
export function getTrilinearlyInterpolated(input: ColorInterpolationInput): TextureImage<Uint8Array> {
|
||||
const { vertexCount, positionBuffer, transformBuffer, grid, gridDim, gridTexDim, gridTransform, vertexStride, colorStride } = input;
|
||||
|
||||
const itemOffset = input.itemOffset || 0;
|
||||
const outputStride = input.outputStride;
|
||||
if (outputStride + itemOffset > colorStride) {
|
||||
throw new Error('outputStride + itemOffset must NOT be larger than colorStride');
|
||||
}
|
||||
|
||||
const isInstanceType = input.colorType.endsWith('Instance');
|
||||
const instanceCount = isInstanceType ? input.instanceCount : 1;
|
||||
const image = createTextureImage(Math.max(1, instanceCount * vertexCount), 3, Uint8Array);
|
||||
const image = createTextureImage(Math.max(1, instanceCount * vertexCount), outputStride, Uint8Array);
|
||||
const { array } = image;
|
||||
|
||||
const [xn, yn] = gridDim;
|
||||
@@ -204,26 +215,178 @@ export function getTrilinearlyInterpolated(input: ColorInterpolationInput): Text
|
||||
const [x1, y1, z1] = v1;
|
||||
const [xd, yd, zd] = vd;
|
||||
|
||||
const s000 = Color.fromArray(grid, getIndex(x0, y0, z0));
|
||||
const s100 = Color.fromArray(grid, getIndex(x1, y0, z0));
|
||||
const s001 = Color.fromArray(grid, getIndex(x0, y0, z1));
|
||||
const s101 = Color.fromArray(grid, getIndex(x1, y0, z1));
|
||||
const s010 = Color.fromArray(grid, getIndex(x0, y1, z0));
|
||||
const s110 = Color.fromArray(grid, getIndex(x1, y1, z0));
|
||||
const s011 = Color.fromArray(grid, getIndex(x0, y1, z1));
|
||||
const s111 = Color.fromArray(grid, getIndex(x1, y1, z1));
|
||||
const i000 = getIndex(x0, y0, z0) + itemOffset;
|
||||
const i100 = getIndex(x1, y0, z0) + itemOffset;
|
||||
const i001 = getIndex(x0, y0, z1) + itemOffset;
|
||||
const i101 = getIndex(x1, y0, z1) + itemOffset;
|
||||
const i010 = getIndex(x0, y1, z0) + itemOffset;
|
||||
const i110 = getIndex(x1, y1, z0) + itemOffset;
|
||||
const i011 = getIndex(x0, y1, z1) + itemOffset;
|
||||
const i111 = getIndex(x1, y1, z1) + itemOffset;
|
||||
|
||||
const s00 = Color.interpolate(s000, s100, xd);
|
||||
const s01 = Color.interpolate(s001, s101, xd);
|
||||
const s10 = Color.interpolate(s010, s110, xd);
|
||||
const s11 = Color.interpolate(s011, s111, xd);
|
||||
const o = (i * vertexCount + j) * outputStride;
|
||||
|
||||
const s0 = Color.interpolate(s00, s10, yd);
|
||||
const s1 = Color.interpolate(s01, s11, yd);
|
||||
for (let k = 0; k < outputStride; ++k) {
|
||||
const s000 = grid[i000 + k];
|
||||
const s100 = grid[i100 + k];
|
||||
const s001 = grid[i001 + k];
|
||||
const s101 = grid[i101 + k];
|
||||
const s010 = grid[i010 + k];
|
||||
const s110 = grid[i110 + k];
|
||||
const s011 = grid[i011 + k];
|
||||
const s111 = grid[i111 + k];
|
||||
|
||||
Color.toArray(Color.interpolate(s0, s1, zd), array, (i * vertexCount + j) * 3);
|
||||
const s00 = lerp(s000, s100, xd);
|
||||
const s01 = lerp(s001, s101, xd);
|
||||
const s10 = lerp(s010, s110, xd);
|
||||
const s11 = lerp(s011, s111, xd);
|
||||
|
||||
const s0 = lerp(s00, s10, yd);
|
||||
const s1 = lerp(s01, s11, yd);
|
||||
|
||||
array[o + k] = lerp(s0, s1, zd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
function isSupportedColorType(x: string): x is 'group' | 'groupInstance' {
|
||||
return x === 'group' || x === 'groupInstance';
|
||||
}
|
||||
|
||||
export function applyMeshColorSmoothing(values: MeshValues, resolution: number, stride: number, webgl?: WebGLContext, colorTexture?: Texture) {
|
||||
if (!isSupportedColorType(values.dColorType.ref.value)) return;
|
||||
|
||||
const smoothingData = calcMeshColorSmoothing({
|
||||
vertexCount: values.uVertexCount.ref.value,
|
||||
instanceCount: values.uInstanceCount.ref.value,
|
||||
groupCount: values.uGroupCount.ref.value,
|
||||
transformBuffer: values.aTransform.ref.value,
|
||||
instanceBuffer: values.aInstance.ref.value,
|
||||
positionBuffer: values.aPosition.ref.value,
|
||||
groupBuffer: values.aGroup.ref.value,
|
||||
colorData: values.tColor.ref.value,
|
||||
colorType: values.dColorType.ref.value,
|
||||
boundingSphere: values.boundingSphere.ref.value,
|
||||
invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
|
||||
itemSize: 3
|
||||
}, resolution, stride, webgl, colorTexture);
|
||||
|
||||
if (smoothingData.kind === 'volume') {
|
||||
ValueCell.updateIfChanged(values.dColorType, smoothingData.type);
|
||||
ValueCell.update(values.tColorGrid, smoothingData.texture);
|
||||
ValueCell.update(values.uColorTexDim, smoothingData.gridTexDim);
|
||||
ValueCell.update(values.uColorGridDim, smoothingData.gridDim);
|
||||
ValueCell.update(values.uColorGridTransform, smoothingData.gridTransform);
|
||||
} else if (smoothingData.kind === 'vertex') {
|
||||
ValueCell.updateIfChanged(values.dColorType, smoothingData.type);
|
||||
ValueCell.update(values.tColor, smoothingData.texture);
|
||||
ValueCell.update(values.uColorTexDim, smoothingData.texDim);
|
||||
}
|
||||
}
|
||||
|
||||
function isSupportedOverpaintType(x: string): x is 'groupInstance' {
|
||||
return x === 'groupInstance';
|
||||
}
|
||||
|
||||
export function applyMeshOverpaintSmoothing(values: MeshValues, resolution: number, stride: number, webgl?: WebGLContext, colorTexture?: Texture) {
|
||||
if (!isSupportedOverpaintType(values.dOverpaintType.ref.value)) return;
|
||||
|
||||
const smoothingData = calcMeshColorSmoothing({
|
||||
vertexCount: values.uVertexCount.ref.value,
|
||||
instanceCount: values.uInstanceCount.ref.value,
|
||||
groupCount: values.uGroupCount.ref.value,
|
||||
transformBuffer: values.aTransform.ref.value,
|
||||
instanceBuffer: values.aInstance.ref.value,
|
||||
positionBuffer: values.aPosition.ref.value,
|
||||
groupBuffer: values.aGroup.ref.value,
|
||||
colorData: values.tOverpaint.ref.value,
|
||||
colorType: values.dOverpaintType.ref.value,
|
||||
boundingSphere: values.boundingSphere.ref.value,
|
||||
invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
|
||||
itemSize: 4
|
||||
}, resolution, stride, webgl, colorTexture);
|
||||
if (smoothingData.kind === 'volume') {
|
||||
ValueCell.updateIfChanged(values.dOverpaintType, smoothingData.type);
|
||||
ValueCell.update(values.tOverpaintGrid, smoothingData.texture);
|
||||
ValueCell.update(values.uOverpaintTexDim, smoothingData.gridTexDim);
|
||||
ValueCell.update(values.uOverpaintGridDim, smoothingData.gridDim);
|
||||
ValueCell.update(values.uOverpaintGridTransform, smoothingData.gridTransform);
|
||||
} else if (smoothingData.kind === 'vertex') {
|
||||
ValueCell.updateIfChanged(values.dOverpaintType, smoothingData.type);
|
||||
ValueCell.update(values.tOverpaint, smoothingData.texture);
|
||||
ValueCell.update(values.uOverpaintTexDim, smoothingData.texDim);
|
||||
}
|
||||
}
|
||||
|
||||
function isSupportedTransparencyType(x: string): x is 'groupInstance' {
|
||||
return x === 'groupInstance';
|
||||
}
|
||||
|
||||
export function applyMeshTransparencySmoothing(values: MeshValues, resolution: number, stride: number, webgl?: WebGLContext, colorTexture?: Texture) {
|
||||
if (!isSupportedTransparencyType(values.dTransparencyType.ref.value)) return;
|
||||
|
||||
const smoothingData = calcMeshColorSmoothing({
|
||||
vertexCount: values.uVertexCount.ref.value,
|
||||
instanceCount: values.uInstanceCount.ref.value,
|
||||
groupCount: values.uGroupCount.ref.value,
|
||||
transformBuffer: values.aTransform.ref.value,
|
||||
instanceBuffer: values.aInstance.ref.value,
|
||||
positionBuffer: values.aPosition.ref.value,
|
||||
groupBuffer: values.aGroup.ref.value,
|
||||
colorData: values.tTransparency.ref.value,
|
||||
colorType: values.dTransparencyType.ref.value,
|
||||
boundingSphere: values.boundingSphere.ref.value,
|
||||
invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
|
||||
itemSize: 1
|
||||
}, resolution, stride, webgl, colorTexture);
|
||||
if (smoothingData.kind === 'volume') {
|
||||
ValueCell.updateIfChanged(values.dTransparencyType, smoothingData.type);
|
||||
ValueCell.update(values.tTransparencyGrid, smoothingData.texture);
|
||||
ValueCell.update(values.uTransparencyTexDim, smoothingData.gridTexDim);
|
||||
ValueCell.update(values.uTransparencyGridDim, smoothingData.gridDim);
|
||||
ValueCell.update(values.uTransparencyGridTransform, smoothingData.gridTransform);
|
||||
} else if (smoothingData.kind === 'vertex') {
|
||||
ValueCell.updateIfChanged(values.dTransparencyType, smoothingData.type);
|
||||
ValueCell.update(values.tTransparency, smoothingData.texture);
|
||||
ValueCell.update(values.uTransparencyTexDim, smoothingData.texDim);
|
||||
}
|
||||
}
|
||||
|
||||
function isSupportedSubstanceType(x: string): x is 'groupInstance' {
|
||||
return x === 'groupInstance';
|
||||
}
|
||||
|
||||
export function applyMeshSubstanceSmoothing(values: MeshValues, resolution: number, stride: number, webgl?: WebGLContext, colorTexture?: Texture) {
|
||||
if (!isSupportedSubstanceType(values.dSubstanceType.ref.value)) return;
|
||||
|
||||
const smoothingData = calcMeshColorSmoothing({
|
||||
vertexCount: values.uVertexCount.ref.value,
|
||||
instanceCount: values.uInstanceCount.ref.value,
|
||||
groupCount: values.uGroupCount.ref.value,
|
||||
transformBuffer: values.aTransform.ref.value,
|
||||
instanceBuffer: values.aInstance.ref.value,
|
||||
positionBuffer: values.aPosition.ref.value,
|
||||
groupBuffer: values.aGroup.ref.value,
|
||||
colorData: values.tSubstance.ref.value,
|
||||
colorType: values.dSubstanceType.ref.value,
|
||||
boundingSphere: values.boundingSphere.ref.value,
|
||||
invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
|
||||
itemSize: 4
|
||||
}, resolution, stride, webgl, colorTexture);
|
||||
if (smoothingData.kind === 'volume') {
|
||||
ValueCell.updateIfChanged(values.dSubstanceType, smoothingData.type);
|
||||
ValueCell.update(values.tSubstanceGrid, smoothingData.texture);
|
||||
ValueCell.update(values.uSubstanceTexDim, smoothingData.gridTexDim);
|
||||
ValueCell.update(values.uSubstanceGridDim, smoothingData.gridDim);
|
||||
ValueCell.update(values.uSubstanceGridTransform, smoothingData.gridTransform);
|
||||
} else if (smoothingData.kind === 'vertex') {
|
||||
ValueCell.updateIfChanged(values.dSubstanceType, smoothingData.type);
|
||||
ValueCell.update(values.tSubstance, smoothingData.texture);
|
||||
ValueCell.update(values.uSubstanceTexDim, smoothingData.texDim);
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import { createEmptyClipping } from '../clipping-data';
|
||||
import { RenderableState } from '../../../mol-gl/renderable';
|
||||
import { arraySetAdd } from '../../../mol-util/array';
|
||||
import { degToRad } from '../../../mol-math/misc';
|
||||
import { createEmptySubstance } from '../substance-data';
|
||||
|
||||
export interface Mesh {
|
||||
readonly kind: 'mesh',
|
||||
@@ -624,6 +625,8 @@ export namespace Mesh {
|
||||
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
xrayShaded: PD.Boolean(false, 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),
|
||||
};
|
||||
export type Params = typeof Params
|
||||
|
||||
@@ -665,6 +668,7 @@ export namespace Mesh {
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const material = createEmptySubstance();
|
||||
const clipping = createEmptyClipping();
|
||||
|
||||
const counts = { drawCount: mesh.triangleCount * 3, vertexCount: mesh.vertexCount, groupCount, instanceCount };
|
||||
@@ -684,6 +688,7 @@ export namespace Mesh {
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...material,
|
||||
...clipping,
|
||||
...transform,
|
||||
|
||||
@@ -693,6 +698,8 @@ export namespace Mesh {
|
||||
dFlipSided: ValueCell.create(props.flipSided),
|
||||
dIgnoreLight: ValueCell.create(props.ignoreLight),
|
||||
dXrayShaded: ValueCell.create(props.xrayShaded),
|
||||
uBumpFrequency: ValueCell.create(props.bumpFrequency),
|
||||
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
|
||||
|
||||
meta: ValueCell.create(mesh.meta),
|
||||
};
|
||||
@@ -711,6 +718,8 @@ export namespace Mesh {
|
||||
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
|
||||
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
|
||||
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
|
||||
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
|
||||
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
|
||||
}
|
||||
|
||||
function updateBoundingSphere(values: MeshValues, mesh: Mesh) {
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../mol-util/value-cell';
|
||||
import { Vec2 } from '../../mol-math/linear-algebra';
|
||||
import { Vec2, Vec3, Vec4 } from '../../mol-math/linear-algebra';
|
||||
import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
|
||||
|
||||
export type OverpaintData = {
|
||||
tOverpaint: ValueCell<TextureImage<Uint8Array>>
|
||||
uOverpaintTexDim: ValueCell<Vec2>
|
||||
dOverpaint: ValueCell<boolean>,
|
||||
|
||||
tOverpaintGrid: ValueCell<Texture>,
|
||||
uOverpaintGridDim: ValueCell<Vec3>,
|
||||
uOverpaintGridTransform: ValueCell<Vec4>,
|
||||
dOverpaintType: ValueCell<string>,
|
||||
}
|
||||
|
||||
export function applyOverpaintColor(array: Uint8Array, start: number, end: number, color: Color) {
|
||||
@@ -40,6 +46,11 @@ export function createOverpaint(count: number, overpaintData?: OverpaintData): O
|
||||
tOverpaint: ValueCell.create(overpaint),
|
||||
uOverpaintTexDim: ValueCell.create(Vec2.create(overpaint.width, overpaint.height)),
|
||||
dOverpaint: ValueCell.create(count > 0),
|
||||
|
||||
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'),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -55,6 +66,11 @@ export function createEmptyOverpaint(overpaintData?: OverpaintData): OverpaintDa
|
||||
tOverpaint: ValueCell.create(emptyOverpaintTexture),
|
||||
uOverpaintTexDim: ValueCell.create(Vec2.create(1, 1)),
|
||||
dOverpaint: ValueCell.create(false),
|
||||
|
||||
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'),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import { createEmptyOverpaint } from '../overpaint-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
import { createEmptyClipping } from '../clipping-data';
|
||||
import { createEmptySubstance } from '../substance-data';
|
||||
|
||||
/** Point cloud */
|
||||
export interface Points {
|
||||
@@ -127,7 +128,7 @@ export namespace Points {
|
||||
|
||||
export const Params = {
|
||||
...BaseGeometry.Params,
|
||||
sizeFactor: PD.Numeric(1.5, { min: 0, max: 10, step: 0.1 }),
|
||||
sizeFactor: PD.Numeric(3, { min: 0, max: 10, step: 0.1 }),
|
||||
pointSizeAttenuation: PD.Boolean(false),
|
||||
pointStyle: PD.Select('square', PD.objectToOptions(StyleTypes)),
|
||||
};
|
||||
@@ -172,6 +173,7 @@ export namespace Points {
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const material = createEmptySubstance();
|
||||
const clipping = createEmptyClipping();
|
||||
|
||||
const counts = { drawCount: points.pointCount, vertexCount: points.pointCount, groupCount, instanceCount };
|
||||
@@ -190,6 +192,7 @@ export namespace Points {
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...material,
|
||||
...clipping,
|
||||
...transform,
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import { GroupMapping, createGroupMapping } from '../../util';
|
||||
import { createEmptyClipping } from '../clipping-data';
|
||||
import { Vec3, Vec4 } from '../../../mol-math/linear-algebra';
|
||||
import { RenderableState } from '../../../mol-gl/renderable';
|
||||
import { createEmptySubstance } from '../substance-data';
|
||||
|
||||
export interface Spheres {
|
||||
readonly kind: 'spheres',
|
||||
@@ -128,6 +129,8 @@ export namespace Spheres {
|
||||
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
|
||||
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
xrayShaded: PD.Boolean(false, 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),
|
||||
};
|
||||
export type Params = typeof Params
|
||||
|
||||
@@ -170,6 +173,7 @@ export namespace Spheres {
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const material = createEmptySubstance();
|
||||
const clipping = createEmptyClipping();
|
||||
|
||||
const counts = { drawCount: spheres.sphereCount * 2 * 3, vertexCount: spheres.sphereCount * 4, groupCount, instanceCount };
|
||||
@@ -191,6 +195,7 @@ export namespace Spheres {
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...material,
|
||||
...clipping,
|
||||
...transform,
|
||||
|
||||
@@ -201,6 +206,8 @@ export namespace Spheres {
|
||||
dDoubleSided: ValueCell.create(props.doubleSided),
|
||||
dIgnoreLight: ValueCell.create(props.ignoreLight),
|
||||
dXrayShaded: ValueCell.create(props.xrayShaded),
|
||||
uBumpFrequency: ValueCell.create(props.bumpFrequency),
|
||||
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -216,6 +223,8 @@ export namespace Spheres {
|
||||
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
|
||||
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
|
||||
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
|
||||
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
|
||||
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
|
||||
}
|
||||
|
||||
function updateBoundingSphere(values: SpheresValues, spheres: Spheres) {
|
||||
|
||||
76
src/mol-geo/geometry/substance-data.ts
Normal file
76
src/mol-geo/geometry/substance-data.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../mol-util/value-cell';
|
||||
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';
|
||||
import { Material } from '../../mol-util/material';
|
||||
|
||||
export type SubstanceData = {
|
||||
tSubstance: ValueCell<TextureImage<Uint8Array>>
|
||||
uSubstanceTexDim: ValueCell<Vec2>
|
||||
dSubstance: ValueCell<boolean>,
|
||||
|
||||
tSubstanceGrid: ValueCell<Texture>,
|
||||
uSubstanceGridDim: ValueCell<Vec3>,
|
||||
uSubstanceGridTransform: ValueCell<Vec4>,
|
||||
dSubstanceType: ValueCell<string>,
|
||||
}
|
||||
|
||||
export function applySubstanceMaterial(array: Uint8Array, start: number, end: number, material: Material) {
|
||||
for (let i = start; i < end; ++i) {
|
||||
Material.toArray(material, array, i * 4);
|
||||
array[i * 4 + 3] = 255;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function clearSubstance(array: Uint8Array, start: number, end: number) {
|
||||
array.fill(0, start * 4, end * 4);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function createSubstance(count: number, 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);
|
||||
return substanceData;
|
||||
} else {
|
||||
return {
|
||||
tSubstance: ValueCell.create(substance),
|
||||
uSubstanceTexDim: ValueCell.create(Vec2.create(substance.width, substance.height)),
|
||||
dSubstance: ValueCell.create(count > 0),
|
||||
|
||||
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'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const emptySubstanceTexture = { array: new Uint8Array(4), width: 1, height: 1 };
|
||||
export function createEmptySubstance(substanceData?: SubstanceData): SubstanceData {
|
||||
if (substanceData) {
|
||||
ValueCell.update(substanceData.tSubstance, emptySubstanceTexture);
|
||||
ValueCell.update(substanceData.uSubstanceTexDim, Vec2.create(1, 1));
|
||||
return substanceData;
|
||||
} else {
|
||||
return {
|
||||
tSubstance: ValueCell.create(emptySubstanceTexture),
|
||||
uSubstanceTexDim: ValueCell.create(Vec2.create(1, 1)),
|
||||
dSubstance: ValueCell.create(false),
|
||||
|
||||
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'),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import { createEmptyTransparency } from '../transparency-data';
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
import { GroupMapping, createGroupMapping } from '../../util';
|
||||
import { createEmptyClipping } from '../clipping-data';
|
||||
import { createEmptySubstance } from '../substance-data';
|
||||
|
||||
type TextAttachment = (
|
||||
'bottom-left' | 'bottom-center' | 'bottom-right' |
|
||||
@@ -213,6 +214,7 @@ export namespace Text {
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const substance = createEmptySubstance();
|
||||
const clipping = createEmptyClipping();
|
||||
|
||||
const counts = { drawCount: text.charCount * 2 * 3, vertexCount: text.charCount * 4, groupCount, instanceCount };
|
||||
@@ -235,6 +237,7 @@ export namespace Text {
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...substance,
|
||||
...clipping,
|
||||
...transform,
|
||||
|
||||
@@ -245,7 +248,7 @@ export namespace Text {
|
||||
...BaseGeometry.createValues(props, counts),
|
||||
uSizeFactor: ValueCell.create(props.sizeFactor),
|
||||
|
||||
uBorderWidth: ValueCell.create(clamp(props.borderWidth / 2, 0, 0.5)),
|
||||
uBorderWidth: ValueCell.create(clamp(props.borderWidth, 0, 0.5)),
|
||||
uBorderColor: ValueCell.create(Color.toArrayNormalized(props.borderColor, Vec3.zero(), 0)),
|
||||
uOffsetX: ValueCell.create(props.offsetX),
|
||||
uOffsetY: ValueCell.create(props.offsetY),
|
||||
|
||||
@@ -18,7 +18,8 @@ import { Vec2, Vec3, Vec4 } from '../../../mol-math/linear-algebra';
|
||||
import { Box3D, Sphere3D } from '../../../mol-math/geometry';
|
||||
import { accumulate_frag } from '../../../mol-gl/shader/compute/color-smoothing/accumulate.frag';
|
||||
import { accumulate_vert } from '../../../mol-gl/shader/compute/color-smoothing/accumulate.vert';
|
||||
import { TextureImage } from '../../../mol-gl/renderable/util';
|
||||
import { isWebGL2 } from '../../../mol-gl/webgl/compat';
|
||||
import { TextureMeshValues } from '../../../mol-gl/renderable/texture-mesh';
|
||||
|
||||
export const ColorAccumulateSchema = {
|
||||
drawCount: ValueSpec('number'),
|
||||
@@ -38,7 +39,7 @@ export const ColorAccumulateSchema = {
|
||||
tGroup: TextureSpec('texture', 'rgba', 'float', 'nearest'),
|
||||
|
||||
uColorTexDim: UniformSpec('v2'),
|
||||
tColor: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'),
|
||||
tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
dColorType: DefineSpec('string', ['group', 'groupInstance', 'vertex', 'vertexInstance']),
|
||||
|
||||
uCurrentSlice: UniformSpec('f'),
|
||||
@@ -50,6 +51,7 @@ export const ColorAccumulateSchema = {
|
||||
};
|
||||
type ColorAccumulateValues = Values<typeof ColorAccumulateSchema>
|
||||
const ColorAccumulateName = 'color-accumulate';
|
||||
const ColorCountName = 'color-count';
|
||||
|
||||
interface AccumulateInput {
|
||||
vertexCount: number
|
||||
@@ -59,7 +61,7 @@ interface AccumulateInput {
|
||||
instanceBuffer: Float32Array
|
||||
positionTexture: Texture
|
||||
groupTexture: Texture
|
||||
colorData: TextureImage<Uint8Array>
|
||||
colorData: Texture
|
||||
colorType: 'group' | 'groupInstance'
|
||||
}
|
||||
|
||||
@@ -96,7 +98,7 @@ function getAccumulateRenderable(ctx: WebGLContext, input: AccumulateInput, box:
|
||||
ValueCell.update(v.tPosition, input.positionTexture);
|
||||
ValueCell.update(v.tGroup, input.groupTexture);
|
||||
|
||||
ValueCell.update(v.uColorTexDim, Vec2.set(v.uColorTexDim.ref.value, input.colorData.width, input.colorData.height));
|
||||
ValueCell.update(v.uColorTexDim, Vec2.set(v.uColorTexDim.ref.value, input.colorData.getWidth(), input.colorData.getHeight()));
|
||||
ValueCell.update(v.tColor, input.colorData);
|
||||
ValueCell.updateIfChanged(v.dColorType, input.colorType);
|
||||
|
||||
@@ -135,7 +137,7 @@ function createAccumulateRenderable(ctx: WebGLContext, input: AccumulateInput, b
|
||||
tPosition: ValueCell.create(input.positionTexture),
|
||||
tGroup: ValueCell.create(input.groupTexture),
|
||||
|
||||
uColorTexDim: ValueCell.create(Vec2.create(input.colorData.width, input.colorData.height)),
|
||||
uColorTexDim: ValueCell.create(Vec2.create(input.colorData.getWidth(), input.colorData.getHeight())),
|
||||
tColor: ValueCell.create(input.colorData),
|
||||
dColorType: ValueCell.create(input.colorType),
|
||||
|
||||
@@ -148,7 +150,7 @@ function createAccumulateRenderable(ctx: WebGLContext, input: AccumulateInput, b
|
||||
};
|
||||
|
||||
const schema = { ...ColorAccumulateSchema };
|
||||
const shaderCode = ShaderCode('accumulate', accumulate_vert, accumulate_frag);
|
||||
const shaderCode = ShaderCode('accumulate', accumulate_vert, accumulate_frag, { drawBuffers: 'required' });
|
||||
const renderItem = createComputeRenderItem(ctx, 'points', shaderCode, schema, values);
|
||||
|
||||
return createComputeRenderable(renderItem, values);
|
||||
@@ -172,30 +174,33 @@ export const ColorNormalizeSchema = {
|
||||
...QuadSchema,
|
||||
|
||||
tColor: TextureSpec('texture', 'rgba', 'float', 'nearest'),
|
||||
tCount: TextureSpec('texture', 'alpha', 'float', 'nearest'),
|
||||
uTexSize: UniformSpec('v2'),
|
||||
|
||||
};
|
||||
type ColorNormalizeValues = Values<typeof ColorNormalizeSchema>
|
||||
const ColorNormalizeName = 'color-normalize';
|
||||
|
||||
function getNormalizeRenderable(ctx: WebGLContext, color: Texture): ComputeRenderable<ColorNormalizeValues> {
|
||||
function getNormalizeRenderable(ctx: WebGLContext, color: Texture, count: Texture): ComputeRenderable<ColorNormalizeValues> {
|
||||
if (ctx.namedComputeRenderables[ColorNormalizeName]) {
|
||||
const v = ctx.namedComputeRenderables[ColorNormalizeName].values as ColorNormalizeValues;
|
||||
|
||||
ValueCell.update(v.tColor, color);
|
||||
ValueCell.update(v.tCount, count);
|
||||
ValueCell.update(v.uTexSize, Vec2.set(v.uTexSize.ref.value, color.getWidth(), color.getHeight()));
|
||||
|
||||
ctx.namedComputeRenderables[ColorNormalizeName].update();
|
||||
} else {
|
||||
ctx.namedComputeRenderables[ColorNormalizeName] = createColorNormalizeRenderable(ctx, color);
|
||||
ctx.namedComputeRenderables[ColorNormalizeName] = createColorNormalizeRenderable(ctx, color, count);
|
||||
}
|
||||
return ctx.namedComputeRenderables[ColorNormalizeName];
|
||||
}
|
||||
|
||||
function createColorNormalizeRenderable(ctx: WebGLContext, color: Texture) {
|
||||
function createColorNormalizeRenderable(ctx: WebGLContext, color: Texture, count: Texture) {
|
||||
const values: ColorNormalizeValues = {
|
||||
...QuadValues,
|
||||
tColor: ValueCell.create(color),
|
||||
tCount: ValueCell.create(count),
|
||||
uTexSize: ValueCell.create(Vec2.create(color.getWidth(), color.getHeight())),
|
||||
};
|
||||
|
||||
@@ -247,6 +252,9 @@ interface ColorSmoothingInput extends AccumulateInput {
|
||||
}
|
||||
|
||||
export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolution: number, stride: number, webgl: WebGLContext, texture?: Texture) {
|
||||
const { drawBuffers } = webgl.extensions;
|
||||
if (!drawBuffers) throw new Error('need WebGL draw buffers');
|
||||
|
||||
const { gl, resources, state, extensions: { colorBufferHalfFloat, textureHalfFloat } } = webgl;
|
||||
|
||||
const isInstanceType = input.colorType.endsWith('Instance');
|
||||
@@ -263,29 +271,55 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
|
||||
const { texDimX: width, texDimY: height, texCols } = getTexture2dSize(gridDim);
|
||||
// console.log({ width, height, texCols, dim, resolution });
|
||||
|
||||
if (!webgl.namedTextures[ColorAccumulateName]) {
|
||||
webgl.namedTextures[ColorAccumulateName] = colorBufferHalfFloat && textureHalfFloat
|
||||
? resources.texture('image-float16', 'rgba', 'fp16', 'nearest')
|
||||
: resources.texture('image-float32', 'rgba', 'float', 'nearest');
|
||||
}
|
||||
const accumulateTexture = webgl.namedTextures[ColorAccumulateName];
|
||||
accumulateTexture.define(width, height);
|
||||
|
||||
const accumulateRenderable = getAccumulateRenderable(webgl, input, box, resolution, stride);
|
||||
|
||||
//
|
||||
|
||||
const { uCurrentSlice, uCurrentX, uCurrentY } = accumulateRenderable.values;
|
||||
|
||||
if (!webgl.namedFramebuffers[ColorAccumulateName]) {
|
||||
webgl.namedFramebuffers[ColorAccumulateName] = webgl.resources.framebuffer();
|
||||
}
|
||||
const framebuffer = webgl.namedFramebuffers[ColorAccumulateName];
|
||||
|
||||
if (isWebGL2(gl)) {
|
||||
if (!webgl.namedTextures[ColorAccumulateName]) {
|
||||
webgl.namedTextures[ColorAccumulateName] = colorBufferHalfFloat && textureHalfFloat
|
||||
? resources.texture('image-float16', 'rgba', 'fp16', 'nearest')
|
||||
: resources.texture('image-float32', 'rgba', 'float', 'nearest');
|
||||
}
|
||||
|
||||
if (!webgl.namedTextures[ColorCountName]) {
|
||||
webgl.namedTextures[ColorCountName] = resources.texture('image-float32', 'alpha', 'float', 'nearest');
|
||||
}
|
||||
} else {
|
||||
// in webgl1 drawbuffers must be in the same format for some reason
|
||||
// this is quite wasteful but good enough for medium size meshes
|
||||
|
||||
if (!webgl.namedTextures[ColorAccumulateName]) {
|
||||
webgl.namedTextures[ColorAccumulateName] = resources.texture('image-float32', 'rgba', 'float', 'nearest');
|
||||
}
|
||||
|
||||
if (!webgl.namedTextures[ColorCountName]) {
|
||||
webgl.namedTextures[ColorCountName] = resources.texture('image-float32', 'rgba', 'float', 'nearest');
|
||||
}
|
||||
}
|
||||
|
||||
const accumulateTexture = webgl.namedTextures[ColorAccumulateName];
|
||||
const countTexture = webgl.namedTextures[ColorCountName];
|
||||
|
||||
accumulateTexture.define(width, height);
|
||||
countTexture.define(width, height);
|
||||
|
||||
accumulateTexture.attachFramebuffer(framebuffer, 0);
|
||||
countTexture.attachFramebuffer(framebuffer, 1);
|
||||
|
||||
const accumulateRenderable = getAccumulateRenderable(webgl, input, box, resolution, stride);
|
||||
state.currentRenderItemId = -1;
|
||||
|
||||
framebuffer.bind();
|
||||
drawBuffers.drawBuffers([
|
||||
drawBuffers.COLOR_ATTACHMENT0,
|
||||
drawBuffers.COLOR_ATTACHMENT1,
|
||||
]);
|
||||
|
||||
const { uCurrentSlice, uCurrentX, uCurrentY } = accumulateRenderable.values;
|
||||
|
||||
setAccumulateDefaults(webgl);
|
||||
state.currentRenderItemId = -1;
|
||||
accumulateTexture.attachFramebuffer(framebuffer, 0);
|
||||
gl.viewport(0, 0, width, height);
|
||||
gl.scissor(0, 0, width, height);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
@@ -310,21 +344,31 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
|
||||
currX += dx;
|
||||
}
|
||||
|
||||
accumulateTexture.detachFramebuffer(framebuffer, 0);
|
||||
countTexture.detachFramebuffer(framebuffer, 1);
|
||||
drawBuffers.drawBuffers([gl.COLOR_ATTACHMENT0, gl.NONE]);
|
||||
|
||||
// const accImage = new Float32Array(width * height * 4);
|
||||
// accumulateTexture.attachFramebuffer(framebuffer, 0);
|
||||
// webgl.readPixels(0, 0, width, height, accImage);
|
||||
// console.log(accImage);
|
||||
// printTextureImage({ array: accImage, width, height }, 1 / 4);
|
||||
// printTextureImage({ array: accImage, width, height }, { scale: 1 });
|
||||
|
||||
// const cntImage = new Float32Array(width * height * 4);
|
||||
// countTexture.attachFramebuffer(framebuffer, 0);
|
||||
// webgl.readPixels(0, 0, width, height, cntImage);
|
||||
// console.log(cntImage);
|
||||
// printTextureImage({ array: cntImage, width, height }, { scale: 1 });
|
||||
|
||||
// normalize
|
||||
|
||||
if (!texture) texture = resources.texture('image-uint8', 'rgb', 'ubyte', 'linear');
|
||||
if (!texture) texture = resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
|
||||
texture.define(width, height);
|
||||
|
||||
const normalizeRenderable = getNormalizeRenderable(webgl, accumulateTexture);
|
||||
const normalizeRenderable = getNormalizeRenderable(webgl, accumulateTexture, countTexture);
|
||||
state.currentRenderItemId = -1;
|
||||
|
||||
setNormalizeDefaults(webgl);
|
||||
state.currentRenderItemId = -1;
|
||||
texture.attachFramebuffer(framebuffer, 0);
|
||||
gl.viewport(0, 0, width, height);
|
||||
gl.scissor(0, 0, width, height);
|
||||
@@ -335,10 +379,160 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
|
||||
// texture.attachFramebuffer(framebuffer, 0);
|
||||
// webgl.readPixels(0, 0, width, height, normImage);
|
||||
// console.log(normImage);
|
||||
// printTextureImage({ array: normImage, width, height }, 1 / 4);
|
||||
// printTextureImage({ array: normImage, width, height }, { scale: 1 });
|
||||
|
||||
const gridTransform = Vec4.create(min[0], min[1], min[2], scaleFactor);
|
||||
const type = isInstanceType ? 'volumeInstance' : 'volume';
|
||||
|
||||
return { texture, gridDim, gridTexDim: Vec2.create(width, height), gridTransform, type };
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const ColorSmoothingRgbName = 'color-smoothing-rgb';
|
||||
const ColorSmoothingRgbaName = 'color-smoothing-rgba';
|
||||
const ColorSmoothingAlphaName = 'color-smoothing-alpha';
|
||||
|
||||
function isSupportedColorType(x: string): x is 'group' | 'groupInstance' {
|
||||
return x === 'group' || x === 'groupInstance';
|
||||
}
|
||||
|
||||
export function applyTextureMeshColorSmoothing(values: TextureMeshValues, resolution: number, stride: number, webgl: WebGLContext, colorTexture?: Texture) {
|
||||
if (!isSupportedColorType(values.dColorType.ref.value)) return;
|
||||
|
||||
stride *= 3; // triple because TextureMesh is never indexed (no elements buffer)
|
||||
|
||||
if (!webgl.namedTextures[ColorSmoothingRgbName]) {
|
||||
webgl.namedTextures[ColorSmoothingRgbName] = webgl.resources.texture('image-uint8', 'rgb', 'ubyte', 'nearest');
|
||||
}
|
||||
const colorData = webgl.namedTextures[ColorSmoothingRgbName];
|
||||
colorData.load(values.tColor.ref.value);
|
||||
|
||||
const smoothingData = calcTextureMeshColorSmoothing({
|
||||
vertexCount: values.uVertexCount.ref.value,
|
||||
instanceCount: values.uInstanceCount.ref.value,
|
||||
groupCount: values.uGroupCount.ref.value,
|
||||
transformBuffer: values.aTransform.ref.value,
|
||||
instanceBuffer: values.aInstance.ref.value,
|
||||
positionTexture: values.tPosition.ref.value,
|
||||
groupTexture: values.tGroup.ref.value,
|
||||
colorData,
|
||||
colorType: values.dColorType.ref.value,
|
||||
boundingSphere: values.boundingSphere.ref.value,
|
||||
invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
|
||||
}, resolution, stride, webgl, colorTexture);
|
||||
|
||||
ValueCell.updateIfChanged(values.dColorType, smoothingData.type);
|
||||
ValueCell.update(values.tColorGrid, smoothingData.texture);
|
||||
ValueCell.update(values.uColorTexDim, smoothingData.gridTexDim);
|
||||
ValueCell.update(values.uColorGridDim, smoothingData.gridDim);
|
||||
ValueCell.update(values.uColorGridTransform, smoothingData.gridTransform);
|
||||
}
|
||||
|
||||
function isSupportedOverpaintType(x: string): x is 'groupInstance' {
|
||||
return x === 'groupInstance';
|
||||
}
|
||||
|
||||
export function applyTextureMeshOverpaintSmoothing(values: TextureMeshValues, resolution: number, stride: number, webgl: WebGLContext, colorTexture?: Texture) {
|
||||
if (!isSupportedOverpaintType(values.dOverpaintType.ref.value)) return;
|
||||
|
||||
stride *= 3; // triple because TextureMesh is never indexed (no elements buffer)
|
||||
|
||||
if (!webgl.namedTextures[ColorSmoothingRgbaName]) {
|
||||
webgl.namedTextures[ColorSmoothingRgbaName] = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
|
||||
}
|
||||
const colorData = webgl.namedTextures[ColorSmoothingRgbaName];
|
||||
colorData.load(values.tOverpaint.ref.value);
|
||||
|
||||
const smoothingData = calcTextureMeshColorSmoothing({
|
||||
vertexCount: values.uVertexCount.ref.value,
|
||||
instanceCount: values.uInstanceCount.ref.value,
|
||||
groupCount: values.uGroupCount.ref.value,
|
||||
transformBuffer: values.aTransform.ref.value,
|
||||
instanceBuffer: values.aInstance.ref.value,
|
||||
positionTexture: values.tPosition.ref.value,
|
||||
groupTexture: values.tGroup.ref.value,
|
||||
colorData,
|
||||
colorType: values.dOverpaintType.ref.value,
|
||||
boundingSphere: values.boundingSphere.ref.value,
|
||||
invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
|
||||
}, resolution, stride, webgl, colorTexture);
|
||||
|
||||
ValueCell.updateIfChanged(values.dOverpaintType, smoothingData.type);
|
||||
ValueCell.update(values.tOverpaintGrid, smoothingData.texture);
|
||||
ValueCell.update(values.uOverpaintTexDim, smoothingData.gridTexDim);
|
||||
ValueCell.update(values.uOverpaintGridDim, smoothingData.gridDim);
|
||||
ValueCell.update(values.uOverpaintGridTransform, smoothingData.gridTransform);
|
||||
}
|
||||
|
||||
function isSupportedTransparencyType(x: string): x is 'groupInstance' {
|
||||
return x === 'groupInstance';
|
||||
}
|
||||
|
||||
export function applyTextureMeshTransparencySmoothing(values: TextureMeshValues, resolution: number, stride: number, webgl: WebGLContext, colorTexture?: Texture) {
|
||||
if (!isSupportedTransparencyType(values.dTransparencyType.ref.value)) return;
|
||||
|
||||
stride *= 3; // triple because TextureMesh is never indexed (no elements buffer)
|
||||
|
||||
if (!webgl.namedTextures[ColorSmoothingAlphaName]) {
|
||||
webgl.namedTextures[ColorSmoothingAlphaName] = webgl.resources.texture('image-uint8', 'alpha', 'ubyte', 'nearest');
|
||||
}
|
||||
const colorData = webgl.namedTextures[ColorSmoothingAlphaName];
|
||||
colorData.load(values.tTransparency.ref.value);
|
||||
|
||||
const smoothingData = calcTextureMeshColorSmoothing({
|
||||
vertexCount: values.uVertexCount.ref.value,
|
||||
instanceCount: values.uInstanceCount.ref.value,
|
||||
groupCount: values.uGroupCount.ref.value,
|
||||
transformBuffer: values.aTransform.ref.value,
|
||||
instanceBuffer: values.aInstance.ref.value,
|
||||
positionTexture: values.tPosition.ref.value,
|
||||
groupTexture: values.tGroup.ref.value,
|
||||
colorData,
|
||||
colorType: values.dTransparencyType.ref.value,
|
||||
boundingSphere: values.boundingSphere.ref.value,
|
||||
invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
|
||||
}, resolution, stride, webgl, colorTexture);
|
||||
|
||||
ValueCell.updateIfChanged(values.dTransparencyType, smoothingData.type);
|
||||
ValueCell.update(values.tTransparencyGrid, smoothingData.texture);
|
||||
ValueCell.update(values.uTransparencyTexDim, smoothingData.gridTexDim);
|
||||
ValueCell.update(values.uTransparencyGridDim, smoothingData.gridDim);
|
||||
ValueCell.update(values.uTransparencyGridTransform, smoothingData.gridTransform);
|
||||
}
|
||||
|
||||
function isSupportedSubstanceType(x: string): x is 'groupInstance' {
|
||||
return x === 'groupInstance';
|
||||
}
|
||||
|
||||
export function applyTextureMeshSubstanceSmoothing(values: TextureMeshValues, resolution: number, stride: number, webgl: WebGLContext, colorTexture?: Texture) {
|
||||
if (!isSupportedSubstanceType(values.dSubstanceType.ref.value)) return;
|
||||
|
||||
stride *= 3; // triple because TextureMesh is never indexed (no elements buffer)
|
||||
|
||||
if (!webgl.namedTextures[ColorSmoothingRgbaName]) {
|
||||
webgl.namedTextures[ColorSmoothingRgbaName] = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
|
||||
}
|
||||
const colorData = webgl.namedTextures[ColorSmoothingRgbaName];
|
||||
colorData.load(values.tSubstance.ref.value);
|
||||
|
||||
const smoothingData = calcTextureMeshColorSmoothing({
|
||||
vertexCount: values.uVertexCount.ref.value,
|
||||
instanceCount: values.uInstanceCount.ref.value,
|
||||
groupCount: values.uGroupCount.ref.value,
|
||||
transformBuffer: values.aTransform.ref.value,
|
||||
instanceBuffer: values.aInstance.ref.value,
|
||||
positionTexture: values.tPosition.ref.value,
|
||||
groupTexture: values.tGroup.ref.value,
|
||||
colorData,
|
||||
colorType: values.dSubstanceType.ref.value,
|
||||
boundingSphere: values.boundingSphere.ref.value,
|
||||
invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
|
||||
}, resolution, stride, webgl, colorTexture);
|
||||
|
||||
ValueCell.updateIfChanged(values.dSubstanceType, smoothingData.type);
|
||||
ValueCell.update(values.tSubstanceGrid, smoothingData.texture);
|
||||
ValueCell.update(values.uSubstanceTexDim, smoothingData.gridTexDim);
|
||||
ValueCell.update(values.uSubstanceGridDim, smoothingData.gridDim);
|
||||
ValueCell.update(values.uSubstanceGridTransform, smoothingData.gridTransform);
|
||||
}
|
||||
|
||||
@@ -19,10 +19,11 @@ import { createEmptyOverpaint } from '../overpaint-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { TextureMeshValues } from '../../../mol-gl/renderable/texture-mesh';
|
||||
import { calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { Texture } from '../../../mol-gl/webgl/texture';
|
||||
import { createNullTexture, Texture } from '../../../mol-gl/webgl/texture';
|
||||
import { Vec2, Vec4 } from '../../../mol-math/linear-algebra';
|
||||
import { createEmptyClipping } from '../clipping-data';
|
||||
import { NullLocation } from '../../../mol-model/location';
|
||||
import { createEmptySubstance } from '../substance-data';
|
||||
|
||||
export interface TextureMesh {
|
||||
readonly kind: 'texture-mesh',
|
||||
@@ -40,7 +41,7 @@ export interface TextureMesh {
|
||||
|
||||
readonly boundingSphere: Sphere3D
|
||||
|
||||
meta?: unknown
|
||||
readonly meta: { [k: string]: unknown }
|
||||
}
|
||||
|
||||
export namespace TextureMesh {
|
||||
@@ -92,12 +93,17 @@ export namespace TextureMesh {
|
||||
normalTexture: ValueCell.create(normalTexture),
|
||||
doubleBuffer: new DoubleBuffer(),
|
||||
boundingSphere: Sphere3D.clone(boundingSphere),
|
||||
meta: {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function createEmpty(textureMesh?: TextureMesh): TextureMesh {
|
||||
return {} as TextureMesh; // TODO
|
||||
const vt = textureMesh ? textureMesh.vertexTexture.ref.value : createNullTexture();
|
||||
const gt = textureMesh ? textureMesh.groupTexture.ref.value : createNullTexture();
|
||||
const nt = textureMesh ? textureMesh.normalTexture.ref.value : createNullTexture();
|
||||
const bs = textureMesh ? textureMesh.boundingSphere : Sphere3D();
|
||||
return create(0, 0, vt, gt, nt, bs, textureMesh);
|
||||
}
|
||||
|
||||
export const Params = {
|
||||
@@ -107,6 +113,8 @@ export namespace TextureMesh {
|
||||
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
xrayShaded: PD.Boolean(false, 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),
|
||||
};
|
||||
export type Params = typeof Params
|
||||
|
||||
@@ -130,6 +138,7 @@ export namespace TextureMesh {
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const substance = createEmptySubstance();
|
||||
const clipping = createEmptyClipping();
|
||||
|
||||
const counts = { drawCount: textureMesh.vertexCount, vertexCount: textureMesh.vertexCount, groupCount, instanceCount };
|
||||
@@ -151,6 +160,7 @@ export namespace TextureMesh {
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...substance,
|
||||
...clipping,
|
||||
...transform,
|
||||
|
||||
@@ -160,7 +170,11 @@ export namespace TextureMesh {
|
||||
dFlipSided: ValueCell.create(props.flipSided),
|
||||
dIgnoreLight: ValueCell.create(props.ignoreLight),
|
||||
dXrayShaded: ValueCell.create(props.xrayShaded),
|
||||
uBumpFrequency: ValueCell.create(props.bumpFrequency),
|
||||
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
|
||||
dGeoTexture: ValueCell.create(true),
|
||||
|
||||
meta: ValueCell.create(textureMesh.meta),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -177,6 +191,8 @@ export namespace TextureMesh {
|
||||
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
|
||||
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
|
||||
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
|
||||
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
|
||||
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
|
||||
}
|
||||
|
||||
function updateBoundingSphere(values: TextureMeshValues, textureMesh: TextureMesh) {
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../mol-util/value-cell';
|
||||
import { Vec2 } from '../../mol-math/linear-algebra';
|
||||
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 TransparencyData = {
|
||||
tTransparency: ValueCell<TextureImage<Uint8Array>>
|
||||
uTransparencyTexDim: ValueCell<Vec2>
|
||||
dTransparency: ValueCell<boolean>,
|
||||
transparencyAverage: ValueCell<number>,
|
||||
|
||||
tTransparencyGrid: ValueCell<Texture>,
|
||||
uTransparencyGridDim: ValueCell<Vec3>,
|
||||
uTransparencyGridTransform: ValueCell<Vec4>,
|
||||
dTransparencyType: ValueCell<string>,
|
||||
}
|
||||
|
||||
export function applyTransparencyValue(array: Uint8Array, start: number, end: number, value: number) {
|
||||
@@ -48,6 +54,11 @@ export function createTransparency(count: number, transparencyData?: Transparenc
|
||||
uTransparencyTexDim: ValueCell.create(Vec2.create(transparency.width, transparency.height)),
|
||||
dTransparency: ValueCell.create(count > 0),
|
||||
transparencyAverage: ValueCell.create(0),
|
||||
|
||||
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'),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -64,6 +75,11 @@ export function createEmptyTransparency(transparencyData?: TransparencyData): Tr
|
||||
uTransparencyTexDim: ValueCell.create(Vec2.create(1, 1)),
|
||||
dTransparency: ValueCell.create(false),
|
||||
transparencyAverage: ValueCell.create(0),
|
||||
|
||||
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'),
|
||||
};
|
||||
}
|
||||
}
|
||||
36
src/mol-gl/_spec/cylinders.spec.ts
Normal file
36
src/mol-gl/_spec/cylinders.spec.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { createRenderObject } from '../render-object';
|
||||
import { Scene } from '../scene';
|
||||
import { getGLContext, tryGetGLContext } from './gl';
|
||||
import { setDebugMode } from '../../mol-util/debug';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Cylinders } from '../../mol-geo/geometry/cylinders/cylinders';
|
||||
|
||||
export function createCylinders() {
|
||||
const cylinders = Cylinders.createEmpty();
|
||||
const props = PD.getDefaultValues(Cylinders.Params);
|
||||
const values = Cylinders.Utils.createValuesSimple(cylinders, props, ColorNames.orange, 1);
|
||||
const state = Cylinders.Utils.createRenderableState(props);
|
||||
return createRenderObject('cylinders', values, state, -1);
|
||||
}
|
||||
|
||||
describe('cylinders', () => {
|
||||
const ctx = tryGetGLContext(32, 32, { fragDepth: true });
|
||||
|
||||
(ctx ? it : it.skip)('basic', async () => {
|
||||
const ctx = getGLContext(32, 32);
|
||||
const scene = Scene.create(ctx);
|
||||
const cylinders = createCylinders();
|
||||
scene.add(cylinders);
|
||||
setDebugMode(true);
|
||||
expect(() => scene.commit()).not.toThrow();
|
||||
setDebugMode(false);
|
||||
ctx.destroy();
|
||||
});
|
||||
});
|
||||
36
src/mol-gl/_spec/direct-volume.spec.ts
Normal file
36
src/mol-gl/_spec/direct-volume.spec.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { createRenderObject } from '../render-object';
|
||||
import { Scene } from '../scene';
|
||||
import { getGLContext, tryGetGLContext } from './gl';
|
||||
import { setDebugMode } from '../../mol-util/debug';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { DirectVolume } from '../../mol-geo/geometry/direct-volume/direct-volume';
|
||||
|
||||
export function createDirectVolume() {
|
||||
const directVolume = DirectVolume.createEmpty();
|
||||
const props = PD.getDefaultValues(DirectVolume.Params);
|
||||
const values = DirectVolume.Utils.createValuesSimple(directVolume, props, ColorNames.orange, 1);
|
||||
const state = DirectVolume.Utils.createRenderableState(props);
|
||||
return createRenderObject('direct-volume', values, state, -1);
|
||||
}
|
||||
|
||||
describe('direct-volume', () => {
|
||||
const ctx = tryGetGLContext(32, 32);
|
||||
|
||||
(ctx ? it : it.skip)('basic', async () => {
|
||||
const ctx = getGLContext(32, 32);
|
||||
const scene = Scene.create(ctx);
|
||||
const directVolume = createDirectVolume();
|
||||
scene.add(directVolume);
|
||||
setDebugMode(true);
|
||||
expect(() => scene.commit()).not.toThrow();
|
||||
setDebugMode(false);
|
||||
ctx.destroy();
|
||||
});
|
||||
});
|
||||
@@ -760,8 +760,8 @@ export function createGl(width: number, height: number, contextAttributes: WebGL
|
||||
validateProgram: function () { },
|
||||
generateMipmap: function () { },
|
||||
isContextLost: function () { return false; },
|
||||
drawingBufferWidth: 1024,
|
||||
drawingBufferHeight: 1024,
|
||||
drawingBufferWidth: width,
|
||||
drawingBufferHeight: height,
|
||||
blendColor: function () { },
|
||||
blendEquation: function () { },
|
||||
blendEquationSeparate: function () { },
|
||||
|
||||
28
src/mol-gl/_spec/gl.ts
Normal file
28
src/mol-gl/_spec/gl.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { createContext } from '../webgl/context';
|
||||
|
||||
export function getGLContext(width: number, height: number) {
|
||||
const gl = require('gl')(width, height, {
|
||||
alpha: true,
|
||||
depth: true,
|
||||
premultipliedAlpha: true,
|
||||
preserveDrawingBuffer: true,
|
||||
antialias: true,
|
||||
});
|
||||
return createContext(gl);
|
||||
}
|
||||
|
||||
export function tryGetGLContext(width: number, height: number, requiredExtensions?: { fragDepth?: boolean }) {
|
||||
try {
|
||||
const ctx = getGLContext(width, height);
|
||||
if (requiredExtensions?.fragDepth && !ctx.extensions.fragDepth) return;
|
||||
return ctx;
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
36
src/mol-gl/_spec/image.spec.ts
Normal file
36
src/mol-gl/_spec/image.spec.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { createRenderObject } from '../render-object';
|
||||
import { Scene } from '../scene';
|
||||
import { getGLContext, tryGetGLContext } from './gl';
|
||||
import { setDebugMode } from '../../mol-util/debug';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Image } from '../../mol-geo/geometry/image/image';
|
||||
|
||||
export function createImage() {
|
||||
const image = Image.createEmpty();
|
||||
const props = PD.getDefaultValues(Image.Params);
|
||||
const values = Image.Utils.createValuesSimple(image, props, ColorNames.orange, 1);
|
||||
const state = Image.Utils.createRenderableState(props);
|
||||
return createRenderObject('image', values, state, -1);
|
||||
}
|
||||
|
||||
describe('image', () => {
|
||||
const ctx = tryGetGLContext(32, 32);
|
||||
|
||||
(ctx ? it : it.skip)('basic', async () => {
|
||||
const ctx = getGLContext(32, 32);
|
||||
const scene = Scene.create(ctx);
|
||||
const image = createImage();
|
||||
scene.add(image);
|
||||
setDebugMode(true);
|
||||
expect(() => scene.commit()).not.toThrow();
|
||||
setDebugMode(false);
|
||||
ctx.destroy();
|
||||
});
|
||||
});
|
||||
36
src/mol-gl/_spec/lines.spec.ts
Normal file
36
src/mol-gl/_spec/lines.spec.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { createRenderObject } from '../render-object';
|
||||
import { Scene } from '../scene';
|
||||
import { getGLContext, tryGetGLContext } from './gl';
|
||||
import { setDebugMode } from '../../mol-util/debug';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Lines } from '../../mol-geo/geometry/lines/lines';
|
||||
|
||||
export function createLines() {
|
||||
const lines = Lines.createEmpty();
|
||||
const props = PD.getDefaultValues(Lines.Params);
|
||||
const values = Lines.Utils.createValuesSimple(lines, props, ColorNames.orange, 1);
|
||||
const state = Lines.Utils.createRenderableState(props);
|
||||
return createRenderObject('lines', values, state, -1);
|
||||
}
|
||||
|
||||
describe('lines', () => {
|
||||
const ctx = tryGetGLContext(32, 32);
|
||||
|
||||
(ctx ? it : it.skip)('basic', async () => {
|
||||
const ctx = getGLContext(32, 32);
|
||||
const scene = Scene.create(ctx);
|
||||
const lines = createLines();
|
||||
scene.add(lines);
|
||||
setDebugMode(true);
|
||||
expect(() => scene.commit()).not.toThrow();
|
||||
setDebugMode(false);
|
||||
ctx.destroy();
|
||||
});
|
||||
});
|
||||
36
src/mol-gl/_spec/mesh.spec.ts
Normal file
36
src/mol-gl/_spec/mesh.spec.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { createRenderObject } from '../render-object';
|
||||
import { Scene } from '../scene';
|
||||
import { getGLContext, tryGetGLContext } from './gl';
|
||||
import { setDebugMode } from '../../mol-util/debug';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
|
||||
|
||||
export function createMesh() {
|
||||
const mesh = Mesh.createEmpty();
|
||||
const props = PD.getDefaultValues(Mesh.Params);
|
||||
const values = Mesh.Utils.createValuesSimple(mesh, props, ColorNames.orange, 1);
|
||||
const state = Mesh.Utils.createRenderableState(props);
|
||||
return createRenderObject('mesh', values, state, -1);
|
||||
}
|
||||
|
||||
describe('mesh', () => {
|
||||
const ctx = tryGetGLContext(32, 32);
|
||||
|
||||
(ctx ? it : it.skip)('basic', async () => {
|
||||
const ctx = getGLContext(32, 32);
|
||||
const scene = Scene.create(ctx);
|
||||
const mesh = createMesh();
|
||||
scene.add(mesh);
|
||||
setDebugMode(true);
|
||||
expect(() => scene.commit()).not.toThrow();
|
||||
setDebugMode(false);
|
||||
ctx.destroy();
|
||||
});
|
||||
});
|
||||
36
src/mol-gl/_spec/points.spec.ts
Normal file
36
src/mol-gl/_spec/points.spec.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { createRenderObject } from '../render-object';
|
||||
import { Scene } from '../scene';
|
||||
import { getGLContext, tryGetGLContext } from './gl';
|
||||
import { setDebugMode } from '../../mol-util/debug';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Points } from '../../mol-geo/geometry/points/points';
|
||||
|
||||
export function createPoints() {
|
||||
const points = Points.createEmpty();
|
||||
const props = PD.getDefaultValues(Points.Params);
|
||||
const values = Points.Utils.createValuesSimple(points, props, ColorNames.orange, 1);
|
||||
const state = Points.Utils.createRenderableState(props);
|
||||
return createRenderObject('points', values, state, -1);
|
||||
}
|
||||
|
||||
describe('points', () => {
|
||||
const ctx = tryGetGLContext(32, 32);
|
||||
|
||||
(ctx ? it : it.skip)('basic', async () => {
|
||||
const ctx = getGLContext(32, 32);
|
||||
const scene = Scene.create(ctx);
|
||||
const points = createPoints();
|
||||
scene.add(points);
|
||||
setDebugMode(true);
|
||||
expect(() => scene.commit()).not.toThrow();
|
||||
setDebugMode(false);
|
||||
ctx.destroy();
|
||||
});
|
||||
});
|
||||
@@ -5,28 +5,14 @@
|
||||
*/
|
||||
|
||||
import { createGl } from './gl.shim';
|
||||
|
||||
import { Camera } from '../../mol-canvas3d/camera';
|
||||
import { Vec3, Mat4, Vec4 } from '../../mol-math/linear-algebra';
|
||||
import { ValueCell } from '../../mol-util';
|
||||
|
||||
import { Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { Renderer } from '../renderer';
|
||||
import { createValueColor } from '../../mol-geo/geometry/color-data';
|
||||
import { createValueSize } from '../../mol-geo/geometry/size-data';
|
||||
import { createContext } from '../webgl/context';
|
||||
import { RenderableState } from '../renderable';
|
||||
import { createRenderObject } from '../render-object';
|
||||
import { PointsValues } from '../renderable/points';
|
||||
import { Scene } from '../scene';
|
||||
import { createEmptyMarkers } from '../../mol-geo/geometry/marker-data';
|
||||
import { fillSerial } from '../../mol-util/array';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { Sphere3D } from '../../mol-math/geometry';
|
||||
import { createEmptyOverpaint } from '../../mol-geo/geometry/overpaint-data';
|
||||
import { createEmptyTransparency } from '../../mol-geo/geometry/transparency-data';
|
||||
import { createEmptyClipping } from '../../mol-geo/geometry/clipping-data';
|
||||
import { createPoints } from './points.spec';
|
||||
|
||||
function createRenderer(gl: WebGLRenderingContext) {
|
||||
export function createRenderer(gl: WebGLRenderingContext) {
|
||||
const ctx = createContext(gl);
|
||||
const camera = new Camera({
|
||||
position: Vec3.create(0, 0, 50)
|
||||
@@ -35,80 +21,14 @@ function createRenderer(gl: WebGLRenderingContext) {
|
||||
return { ctx, camera, renderer };
|
||||
}
|
||||
|
||||
function createPoints() {
|
||||
const aPosition = ValueCell.create(new Float32Array([0, -1, 0, -1, 0, 0, 1, 1, 0]));
|
||||
const aGroup = ValueCell.create(fillSerial(new Float32Array(3)));
|
||||
const aInstance = ValueCell.create(fillSerial(new Float32Array(1)));
|
||||
const color = createValueColor(Color(0xFF0000));
|
||||
const size = createValueSize(1);
|
||||
const marker = createEmptyMarkers();
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const clipping = createEmptyClipping();
|
||||
|
||||
const aTransform = ValueCell.create(new Float32Array(16));
|
||||
const m4 = Mat4.identity();
|
||||
Mat4.toArray(m4, aTransform.ref.value, 0);
|
||||
const transform = ValueCell.create(new Float32Array(aTransform.ref.value));
|
||||
const extraTransform = ValueCell.create(new Float32Array(aTransform.ref.value));
|
||||
|
||||
const boundingSphere = ValueCell.create(Sphere3D.create(Vec3.zero(), 2));
|
||||
const invariantBoundingSphere = ValueCell.create(Sphere3D.create(Vec3.zero(), 2));
|
||||
|
||||
const values: PointsValues = {
|
||||
aPosition,
|
||||
aGroup,
|
||||
aTransform,
|
||||
aInstance,
|
||||
...color,
|
||||
...marker,
|
||||
...size,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...clipping,
|
||||
|
||||
uAlpha: ValueCell.create(1.0),
|
||||
uVertexCount: ValueCell.create(3),
|
||||
uInstanceCount: ValueCell.create(1),
|
||||
uGroupCount: ValueCell.create(3),
|
||||
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere.ref.value)),
|
||||
|
||||
alpha: ValueCell.create(1.0),
|
||||
drawCount: ValueCell.create(3),
|
||||
instanceCount: ValueCell.create(1),
|
||||
matrix: ValueCell.create(m4),
|
||||
transform,
|
||||
extraTransform,
|
||||
hasReflection: ValueCell.create(false),
|
||||
boundingSphere,
|
||||
invariantBoundingSphere,
|
||||
|
||||
uSizeFactor: ValueCell.create(1),
|
||||
dPointSizeAttenuation: ValueCell.create(true),
|
||||
dPointStyle: ValueCell.create('square'),
|
||||
};
|
||||
const state: RenderableState = {
|
||||
disposed: false,
|
||||
visible: true,
|
||||
alphaFactor: 1,
|
||||
pickable: true,
|
||||
colorOnly: false,
|
||||
opaque: true,
|
||||
writeDepth: true,
|
||||
noClip: false,
|
||||
};
|
||||
|
||||
return createRenderObject('points', values, state, -1);
|
||||
}
|
||||
|
||||
describe('renderer', () => {
|
||||
it('basic', () => {
|
||||
const [width, height] = [32, 32];
|
||||
const gl = createGl(width, height, { preserveDrawingBuffer: true });
|
||||
const { ctx, renderer } = createRenderer(gl);
|
||||
|
||||
expect(ctx.gl.canvas.width).toBe(32);
|
||||
expect(ctx.gl.canvas.height).toBe(32);
|
||||
expect(ctx.gl.drawingBufferWidth).toBe(32);
|
||||
expect(ctx.gl.drawingBufferHeight).toBe(32);
|
||||
|
||||
expect(ctx.stats.resourceCounts.attribute).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(0);
|
||||
@@ -132,8 +52,8 @@ describe('renderer', () => {
|
||||
scene.add(points);
|
||||
scene.commit();
|
||||
expect(ctx.stats.resourceCounts.attribute).toBe(ctx.isWebGL2 ? 4 : 5);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(7);
|
||||
expect(ctx.stats.resourceCounts.vertexArray).toBe(8);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(8);
|
||||
expect(ctx.stats.resourceCounts.vertexArray).toBe(ctx.extensions.vertexArrayObject ? 8 : 0);
|
||||
expect(ctx.stats.resourceCounts.program).toBe(8);
|
||||
expect(ctx.stats.resourceCounts.shader).toBe(16);
|
||||
|
||||
|
||||
36
src/mol-gl/_spec/spheres.spec.ts
Normal file
36
src/mol-gl/_spec/spheres.spec.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { createRenderObject } from '../render-object';
|
||||
import { Scene } from '../scene';
|
||||
import { getGLContext, tryGetGLContext } from './gl';
|
||||
import { setDebugMode } from '../../mol-util/debug';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Spheres } from '../../mol-geo/geometry/spheres/spheres';
|
||||
|
||||
export function createSpheres() {
|
||||
const spheres = Spheres.createEmpty();
|
||||
const props = PD.getDefaultValues(Spheres.Params);
|
||||
const values = Spheres.Utils.createValuesSimple(spheres, props, ColorNames.orange, 1);
|
||||
const state = Spheres.Utils.createRenderableState(props);
|
||||
return createRenderObject('spheres', values, state, -1);
|
||||
}
|
||||
|
||||
describe('spheres', () => {
|
||||
const ctx = tryGetGLContext(32, 32, { fragDepth: true });
|
||||
|
||||
(ctx ? it : it.skip)('basic', async () => {
|
||||
const ctx = getGLContext(32, 32);
|
||||
const scene = Scene.create(ctx);
|
||||
const spheres = createSpheres();
|
||||
scene.add(spheres);
|
||||
setDebugMode(true);
|
||||
expect(() => scene.commit()).not.toThrow();
|
||||
setDebugMode(false);
|
||||
ctx.destroy();
|
||||
});
|
||||
});
|
||||
36
src/mol-gl/_spec/text.spec.ts
Normal file
36
src/mol-gl/_spec/text.spec.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { createRenderObject } from '../render-object';
|
||||
import { Scene } from '../scene';
|
||||
import { getGLContext, tryGetGLContext } from './gl';
|
||||
import { setDebugMode } from '../../mol-util/debug';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Text } from '../../mol-geo/geometry/text/text';
|
||||
|
||||
export function createText() {
|
||||
const text = Text.createEmpty();
|
||||
const props = PD.getDefaultValues(Text.Params);
|
||||
const values = Text.Utils.createValuesSimple(text, props, ColorNames.orange, 1);
|
||||
const state = Text.Utils.createRenderableState(props);
|
||||
return createRenderObject('text', values, state, -1);
|
||||
}
|
||||
|
||||
describe('text', () => {
|
||||
const ctx = tryGetGLContext(32, 32);
|
||||
|
||||
(ctx ? it : it.skip)('basic', async () => {
|
||||
const ctx = getGLContext(32, 32);
|
||||
const scene = Scene.create(ctx);
|
||||
const text = createText();
|
||||
scene.add(text);
|
||||
setDebugMode(true);
|
||||
expect(() => scene.commit()).not.toThrow();
|
||||
setDebugMode(false);
|
||||
ctx.destroy();
|
||||
});
|
||||
});
|
||||
36
src/mol-gl/_spec/texture-mesh.spec.ts
Normal file
36
src/mol-gl/_spec/texture-mesh.spec.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { createRenderObject } from '../render-object';
|
||||
import { Scene } from '../scene';
|
||||
import { getGLContext, tryGetGLContext } from './gl';
|
||||
import { setDebugMode } from '../../mol-util/debug';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { TextureMesh } from '../../mol-geo/geometry/texture-mesh/texture-mesh';
|
||||
|
||||
export function createTextureMesh() {
|
||||
const textureMesh = TextureMesh.createEmpty();
|
||||
const props = PD.getDefaultValues(TextureMesh.Params);
|
||||
const values = TextureMesh.Utils.createValuesSimple(textureMesh, props, ColorNames.orange, 1);
|
||||
const state = TextureMesh.Utils.createRenderableState(props);
|
||||
return createRenderObject('texture-mesh', values, state, -1);
|
||||
}
|
||||
|
||||
describe('texture-mesh', () => {
|
||||
const ctx = tryGetGLContext(32, 32);
|
||||
|
||||
(ctx ? it : it.skip)('basic', async () => {
|
||||
const ctx = getGLContext(32, 32);
|
||||
const scene = Scene.create(ctx);
|
||||
const textureMesh = createTextureMesh();
|
||||
scene.add(textureMesh);
|
||||
setDebugMode(true);
|
||||
expect(() => scene.commit()).not.toThrow();
|
||||
setDebugMode(false);
|
||||
ctx.destroy();
|
||||
});
|
||||
});
|
||||
@@ -116,6 +116,9 @@ function setRenderingDefaults(ctx: WebGLContext) {
|
||||
}
|
||||
|
||||
export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
|
||||
const { drawBuffers } = ctx.extensions;
|
||||
if (!drawBuffers) throw new Error('need WebGL draw buffers');
|
||||
|
||||
const { gl, resources, extensions } = ctx;
|
||||
const { pyramidTex, height, levels, scale, count } = histogramPyramid;
|
||||
const width = pyramidTex.getWidth();
|
||||
@@ -173,9 +176,6 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
|
||||
const renderable = getIsosurfaceRenderable(ctx, pyramidTex, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup);
|
||||
ctx.state.currentRenderItemId = -1;
|
||||
|
||||
const { drawBuffers } = ctx.extensions;
|
||||
if (!drawBuffers) throw new Error('need WebGL draw buffers');
|
||||
|
||||
framebuffer.bind();
|
||||
drawBuffers.drawBuffers([
|
||||
drawBuffers.COLOR_ATTACHMENT0,
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { Texture } from '../../mol-gl/webgl/texture';
|
||||
import { PrintImageOptions, printTextureImage } from '../../mol-gl/renderable/util';
|
||||
import { defaults, ValueCell } from '../../mol-util';
|
||||
import { ValueSpec, AttributeSpec, UniformSpec, Values } from '../../mol-gl/renderable/schema';
|
||||
import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
|
||||
import { ValueCell } from '../../mol-util';
|
||||
import { ValueSpec, AttributeSpec, UniformSpec, Values, TextureSpec } from '../../mol-gl/renderable/schema';
|
||||
import { Vec2 } from '../../mol-math/linear-algebra';
|
||||
import { GLRenderingContext } from '../../mol-gl/webgl/compat';
|
||||
import { PixelData } from '../../mol-util/image';
|
||||
import { ShaderCode } from '../shader-code';
|
||||
import { copy_frag } from '../shader/copy.frag';
|
||||
import { quad_vert } from '../shader/quad.vert';
|
||||
import { createComputeRenderItem } from '../webgl/render-item';
|
||||
import { ComputeRenderable, createComputeRenderable } from '../renderable';
|
||||
|
||||
export const QuadPositions = new Float32Array([
|
||||
1.0, 1.0, -1.0, 1.0, -1.0, -1.0, // First triangle
|
||||
@@ -34,21 +36,57 @@ export const QuadValues: Values<typeof QuadSchema> = {
|
||||
|
||||
//
|
||||
|
||||
function getArrayForTexture(gl: GLRenderingContext, texture: Texture, size: number) {
|
||||
switch (texture.type) {
|
||||
case gl.UNSIGNED_BYTE: return new Uint8Array(size);
|
||||
case gl.FLOAT: return new Float32Array(size);
|
||||
}
|
||||
throw new Error('unknown/unsupported texture type');
|
||||
const CopySchema = {
|
||||
...QuadSchema,
|
||||
tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
uTexSize: UniformSpec('v2'),
|
||||
};
|
||||
const CopyShaderCode = ShaderCode('copy', quad_vert, copy_frag);
|
||||
export type CopyRenderable = ComputeRenderable<Values<typeof CopySchema>>
|
||||
|
||||
export function createCopyRenderable(ctx: WebGLContext, texture: Texture): CopyRenderable {
|
||||
const values: Values<typeof CopySchema> = {
|
||||
...QuadValues,
|
||||
tColor: ValueCell.create(texture),
|
||||
uTexSize: ValueCell.create(Vec2.create(texture.getWidth(), texture.getHeight())),
|
||||
};
|
||||
|
||||
const schema = { ...CopySchema };
|
||||
const renderItem = createComputeRenderItem(ctx, 'triangles', CopyShaderCode, schema, values);
|
||||
|
||||
return createComputeRenderable(renderItem, values);
|
||||
}
|
||||
|
||||
export function readTexture(ctx: WebGLContext, texture: Texture, width?: number, height?: number): PixelData {
|
||||
const SharedCopyName = 'shared-copy';
|
||||
|
||||
export function getSharedCopyRenderable(ctx: WebGLContext, texture: Texture) {
|
||||
if (!ctx.namedComputeRenderables[SharedCopyName]) {
|
||||
ctx.namedComputeRenderables[SharedCopyName] = createCopyRenderable(ctx, createNullTexture());
|
||||
}
|
||||
const copy = ctx.namedComputeRenderables[SharedCopyName] as CopyRenderable;
|
||||
ValueCell.update(copy.values.tColor, texture);
|
||||
ValueCell.update(copy.values.uTexSize, Vec2.set(copy.values.uTexSize.ref.value, texture.getWidth(), texture.getHeight()));
|
||||
copy.update();
|
||||
return copy;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const ReadTextureName = 'read-texture';
|
||||
const ReadAlphaTextureName = 'read-alpha-texture';
|
||||
|
||||
export function readTexture(ctx: WebGLContext, texture: Texture) {
|
||||
const { gl, resources } = ctx;
|
||||
width = defaults(width, texture.getWidth());
|
||||
height = defaults(height, texture.getHeight());
|
||||
const size = width * height * 4;
|
||||
const framebuffer = resources.framebuffer();
|
||||
const array = getArrayForTexture(gl, texture, size);
|
||||
if (texture.type !== gl.UNSIGNED_BYTE) throw new Error('unsupported texture type');
|
||||
|
||||
if (!ctx.namedFramebuffers[ReadTextureName]) {
|
||||
ctx.namedFramebuffers[ReadTextureName] = resources.framebuffer();
|
||||
}
|
||||
const framebuffer = ctx.namedFramebuffers[ReadTextureName];
|
||||
|
||||
const width = texture.getWidth();
|
||||
const height = texture.getHeight();
|
||||
const array = new Uint8Array(width * height * 4);
|
||||
framebuffer.bind();
|
||||
texture.attachFramebuffer(framebuffer, 0);
|
||||
ctx.readPixels(0, 0, width, height, array);
|
||||
@@ -56,8 +94,44 @@ export function readTexture(ctx: WebGLContext, texture: Texture, width?: number,
|
||||
return { array, width, height };
|
||||
}
|
||||
|
||||
export function printTexture(ctx: WebGLContext, texture: Texture, options: Partial<PrintImageOptions> = {}) {
|
||||
const pixelData = readTexture(ctx, texture);
|
||||
PixelData.flipY(pixelData);
|
||||
printTextureImage(pixelData, options);
|
||||
export function readAlphaTexture(ctx: WebGLContext, texture: Texture) {
|
||||
const { gl, state, resources } = ctx;
|
||||
if (texture.type !== gl.UNSIGNED_BYTE) throw new Error('unsupported texture type');
|
||||
|
||||
const width = texture.getWidth();
|
||||
const height = texture.getHeight();
|
||||
|
||||
const copy = getSharedCopyRenderable(ctx, texture);
|
||||
state.currentRenderItemId = -1;
|
||||
|
||||
if (!ctx.namedFramebuffers[ReadAlphaTextureName]) {
|
||||
ctx.namedFramebuffers[ReadAlphaTextureName] = resources.framebuffer();
|
||||
}
|
||||
const framebuffer = ctx.namedFramebuffers[ReadAlphaTextureName];
|
||||
framebuffer.bind();
|
||||
|
||||
if (!ctx.namedTextures[ReadAlphaTextureName]) {
|
||||
ctx.namedTextures[ReadAlphaTextureName] = resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
|
||||
}
|
||||
const copyTex = ctx.namedTextures[ReadAlphaTextureName];
|
||||
copyTex.define(width, height);
|
||||
copyTex.attachFramebuffer(framebuffer, 0);
|
||||
|
||||
state.disable(gl.CULL_FACE);
|
||||
state.enable(gl.BLEND);
|
||||
state.disable(gl.DEPTH_TEST);
|
||||
state.enable(gl.SCISSOR_TEST);
|
||||
state.depthMask(false);
|
||||
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);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
copy.render();
|
||||
|
||||
const array = new Uint8Array(width * height * 4);
|
||||
ctx.readPixels(0, 0, width, height, array);
|
||||
|
||||
return { array, width, height };
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -7,7 +7,7 @@
|
||||
import { Renderable, RenderableState, createRenderable } from '../renderable';
|
||||
import { WebGLContext } from '../webgl/context';
|
||||
import { createGraphicsRenderItem } from '../webgl/render-item';
|
||||
import { GlobalUniformSchema, BaseSchema, AttributeSpec, Values, InternalSchema, SizeSchema, InternalValues, ElementsSpec, ValueSpec, DefineSpec, GlobalTextureSchema } from './schema';
|
||||
import { GlobalUniformSchema, BaseSchema, AttributeSpec, Values, InternalSchema, SizeSchema, InternalValues, ElementsSpec, ValueSpec, DefineSpec, GlobalTextureSchema, UniformSpec } from './schema';
|
||||
import { CylindersShaderCode } from '../shader-code';
|
||||
import { ValueCell } from '../../mol-util';
|
||||
|
||||
@@ -26,6 +26,8 @@ export const CylindersSchema = {
|
||||
dDoubleSided: DefineSpec('boolean'),
|
||||
dIgnoreLight: DefineSpec('boolean'),
|
||||
dXrayShaded: DefineSpec('boolean'),
|
||||
uBumpFrequency: UniformSpec('f'),
|
||||
uBumpAmplitude: UniformSpec('f'),
|
||||
};
|
||||
export type CylindersSchema = typeof CylindersSchema
|
||||
export type CylindersValues = Values<CylindersSchema>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -7,7 +7,7 @@
|
||||
import { Renderable, RenderableState, createRenderable } from '../renderable';
|
||||
import { WebGLContext } from '../webgl/context';
|
||||
import { createGraphicsRenderItem } from '../webgl/render-item';
|
||||
import { GlobalUniformSchema, BaseSchema, AttributeSpec, ElementsSpec, DefineSpec, Values, InternalSchema, InternalValues, GlobalTextureSchema, ValueSpec } from './schema';
|
||||
import { GlobalUniformSchema, BaseSchema, AttributeSpec, ElementsSpec, DefineSpec, Values, InternalSchema, InternalValues, GlobalTextureSchema, ValueSpec, UniformSpec } from './schema';
|
||||
import { MeshShaderCode } from '../shader-code';
|
||||
import { ValueCell } from '../../mol-util';
|
||||
|
||||
@@ -22,6 +22,8 @@ export const MeshSchema = {
|
||||
dFlipSided: DefineSpec('boolean'),
|
||||
dIgnoreLight: DefineSpec('boolean'),
|
||||
dXrayShaded: DefineSpec('boolean'),
|
||||
uBumpFrequency: UniformSpec('f'),
|
||||
uBumpAmplitude: UniformSpec('f'),
|
||||
meta: ValueSpec('unknown')
|
||||
} as const;
|
||||
export type MeshSchema = typeof MeshSchema
|
||||
|
||||
@@ -10,7 +10,6 @@ import { createGraphicsRenderItem } from '../webgl/render-item';
|
||||
import { GlobalUniformSchema, BaseSchema, AttributeSpec, DefineSpec, Values, InternalSchema, SizeSchema, InternalValues, GlobalTextureSchema } from './schema';
|
||||
import { PointsShaderCode } from '../shader-code';
|
||||
import { ValueCell } from '../../mol-util';
|
||||
import { Points } from '../../mol-geo/geometry/points/points';
|
||||
|
||||
export const PointsSchema = {
|
||||
...BaseSchema,
|
||||
@@ -18,7 +17,7 @@ export const PointsSchema = {
|
||||
aGroup: AttributeSpec('float32', 1, 0),
|
||||
aPosition: AttributeSpec('float32', 3, 0),
|
||||
dPointSizeAttenuation: DefineSpec('boolean'),
|
||||
dPointStyle: DefineSpec('string', Points.StyleTypeNames),
|
||||
dPointStyle: DefineSpec('string', ['square', 'circle', 'fuzzy']),
|
||||
};
|
||||
export type PointsSchema = typeof PointsSchema
|
||||
export type PointsValues = Values<PointsSchema>
|
||||
|
||||
@@ -141,15 +141,9 @@ export const GlobalUniformSchema = {
|
||||
uClipObjectRotation: UniformSpec('v4[]'),
|
||||
uClipObjectScale: UniformSpec('v3[]'),
|
||||
|
||||
// all the following could in principle be per object
|
||||
// as a kind of 'material' parameter set
|
||||
// would need to test performance implications
|
||||
uLightIntensity: UniformSpec('f'),
|
||||
uAmbientIntensity: UniformSpec('f'),
|
||||
|
||||
uMetalness: UniformSpec('f'),
|
||||
uRoughness: UniformSpec('f'),
|
||||
uReflectivity: UniformSpec('f'),
|
||||
uLightDirection: UniformSpec('v3[]'),
|
||||
uLightColor: UniformSpec('v3[]'),
|
||||
uAmbientColor: UniformSpec('v3'),
|
||||
|
||||
uPickingAlphaThreshold: UniformSpec('f'),
|
||||
|
||||
@@ -224,6 +218,11 @@ export const OverpaintSchema = {
|
||||
uOverpaintTexDim: UniformSpec('v2'),
|
||||
tOverpaint: TextureSpec('image-uint8', 'rgba', 'ubyte', 'nearest'),
|
||||
dOverpaint: DefineSpec('boolean'),
|
||||
|
||||
uOverpaintGridDim: UniformSpec('v3'),
|
||||
uOverpaintGridTransform: UniformSpec('v4'),
|
||||
tOverpaintGrid: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
|
||||
dOverpaintType: DefineSpec('string', ['groupInstance', 'volumeInstance']),
|
||||
} as const;
|
||||
export type OverpaintSchema = typeof OverpaintSchema
|
||||
export type OverpaintValues = Values<OverpaintSchema>
|
||||
@@ -233,10 +232,28 @@ export const TransparencySchema = {
|
||||
tTransparency: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
|
||||
dTransparency: DefineSpec('boolean'),
|
||||
transparencyAverage: ValueSpec('number'),
|
||||
|
||||
uTransparencyGridDim: UniformSpec('v3'),
|
||||
uTransparencyGridTransform: UniformSpec('v4'),
|
||||
tTransparencyGrid: TextureSpec('texture', 'alpha', 'ubyte', 'linear'),
|
||||
dTransparencyType: DefineSpec('string', ['groupInstance', 'volumeInstance']),
|
||||
} as const;
|
||||
export type TransparencySchema = typeof TransparencySchema
|
||||
export type TransparencyValues = Values<TransparencySchema>
|
||||
|
||||
export const SubstanceSchema = {
|
||||
uSubstanceTexDim: UniformSpec('v2'),
|
||||
tSubstance: TextureSpec('image-uint8', 'rgba', 'ubyte', 'nearest'),
|
||||
dSubstance: DefineSpec('boolean'),
|
||||
|
||||
uSubstanceGridDim: UniformSpec('v3'),
|
||||
uSubstanceGridTransform: UniformSpec('v4'),
|
||||
tSubstanceGrid: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
|
||||
dSubstanceType: DefineSpec('string', ['groupInstance', 'volumeInstance']),
|
||||
} as const;
|
||||
export type SubstanceSchema = typeof SubstanceSchema
|
||||
export type SubstanceValues = Values<SubstanceSchema>
|
||||
|
||||
export const ClippingSchema = {
|
||||
dClipObjectCount: DefineSpec('number'),
|
||||
dClipVariant: DefineSpec('string', ['instance', 'pixel']),
|
||||
@@ -253,8 +270,11 @@ export const BaseSchema = {
|
||||
...MarkerSchema,
|
||||
...OverpaintSchema,
|
||||
...TransparencySchema,
|
||||
...SubstanceSchema,
|
||||
...ClippingSchema,
|
||||
|
||||
dLightCount: DefineSpec('number'),
|
||||
|
||||
aInstance: AttributeSpec('float32', 1, 1),
|
||||
/**
|
||||
* final per-instance transform calculated for instance `i` as
|
||||
@@ -266,6 +286,10 @@ export const BaseSchema = {
|
||||
* final alpha, calculated as `values.alpha * state.alpha`
|
||||
*/
|
||||
uAlpha: UniformSpec('f', 'material'),
|
||||
uMetalness: UniformSpec('f', 'material'),
|
||||
uRoughness: UniformSpec('f', 'material'),
|
||||
uBumpiness: UniformSpec('f', 'material'),
|
||||
|
||||
uVertexCount: UniformSpec('i'),
|
||||
uInstanceCount: UniformSpec('i'),
|
||||
uGroupCount: UniformSpec('i'),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -7,7 +7,7 @@
|
||||
import { Renderable, RenderableState, createRenderable } from '../renderable';
|
||||
import { WebGLContext } from '../webgl/context';
|
||||
import { createGraphicsRenderItem } from '../webgl/render-item';
|
||||
import { GlobalUniformSchema, BaseSchema, AttributeSpec, Values, InternalSchema, SizeSchema, InternalValues, ElementsSpec, ValueSpec, DefineSpec, GlobalTextureSchema } from './schema';
|
||||
import { GlobalUniformSchema, BaseSchema, AttributeSpec, Values, InternalSchema, SizeSchema, InternalValues, ElementsSpec, ValueSpec, DefineSpec, GlobalTextureSchema, UniformSpec } from './schema';
|
||||
import { SpheresShaderCode } from '../shader-code';
|
||||
import { ValueCell } from '../../mol-util';
|
||||
|
||||
@@ -23,6 +23,8 @@ export const SpheresSchema = {
|
||||
dDoubleSided: DefineSpec('boolean'),
|
||||
dIgnoreLight: DefineSpec('boolean'),
|
||||
dXrayShaded: DefineSpec('boolean'),
|
||||
uBumpFrequency: UniformSpec('f'),
|
||||
uBumpAmplitude: UniformSpec('f'),
|
||||
};
|
||||
export type SpheresSchema = typeof SpheresSchema
|
||||
export type SpheresValues = Values<SpheresSchema>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { Renderable, RenderableState, createRenderable } from '../renderable';
|
||||
import { WebGLContext } from '../webgl/context';
|
||||
import { createGraphicsRenderItem } from '../webgl/render-item';
|
||||
import { GlobalUniformSchema, BaseSchema, DefineSpec, Values, InternalSchema, InternalValues, UniformSpec, TextureSpec, GlobalTextureSchema } from './schema';
|
||||
import { GlobalUniformSchema, BaseSchema, DefineSpec, Values, InternalSchema, InternalValues, UniformSpec, TextureSpec, GlobalTextureSchema, ValueSpec } from './schema';
|
||||
import { MeshShaderCode } from '../shader-code';
|
||||
import { ValueCell } from '../../mol-util';
|
||||
|
||||
@@ -24,6 +24,9 @@ export const TextureMeshSchema = {
|
||||
dIgnoreLight: DefineSpec('boolean'),
|
||||
dXrayShaded: DefineSpec('boolean'),
|
||||
dGeoTexture: DefineSpec('boolean'),
|
||||
uBumpFrequency: UniformSpec('f'),
|
||||
uBumpAmplitude: UniformSpec('f'),
|
||||
meta: ValueSpec('unknown')
|
||||
};
|
||||
export type TextureMeshSchema = typeof TextureMeshSchema
|
||||
export type TextureMeshValues = Values<TextureMeshSchema>
|
||||
|
||||
@@ -91,7 +91,7 @@ export function printImageData(imageData: ImageData, options: Partial<PrintImage
|
||||
}
|
||||
|
||||
canvas.toBlob(imgBlob => {
|
||||
const objectURL = URL.createObjectURL(imgBlob);
|
||||
const objectURL = URL.createObjectURL(imgBlob!);
|
||||
const existingImg = document.getElementById(o.id) as HTMLImageElement;
|
||||
const img = existingImg || document.createElement('img');
|
||||
img.id = o.id;
|
||||
|
||||
@@ -62,6 +62,7 @@ interface Renderer {
|
||||
setViewport: (x: number, y: number, width: number, height: number) => void
|
||||
setTransparentBackground: (value: boolean) => void
|
||||
setDrawingBufferSize: (width: number, height: number) => void
|
||||
setPixelRatio: (value: number) => void
|
||||
|
||||
dispose: () => void
|
||||
}
|
||||
@@ -69,7 +70,6 @@ interface Renderer {
|
||||
export const RendererParams = {
|
||||
backgroundColor: PD.Color(Color(0x000000), { description: 'Background color of the 3D canvas' }),
|
||||
|
||||
// the following are general 'material' parameters
|
||||
pickingAlphaThreshold: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }, { description: 'The minimum opacity value needed for an object to be pickable.' }),
|
||||
|
||||
interiorDarkening: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
@@ -84,20 +84,19 @@ export const RendererParams = {
|
||||
|
||||
xrayEdgeFalloff: PD.Numeric(1, { min: 0.0, max: 3.0, step: 0.1 }),
|
||||
|
||||
style: PD.MappedStatic('matte', {
|
||||
custom: PD.Group({
|
||||
lightIntensity: PD.Numeric(0.6, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
ambientIntensity: PD.Numeric(0.4, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
metalness: PD.Numeric(0.0, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
roughness: PD.Numeric(1.0, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
reflectivity: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
}, { isExpanded: true }),
|
||||
flat: PD.Group({}),
|
||||
matte: PD.Group({}),
|
||||
glossy: PD.Group({}),
|
||||
metallic: PD.Group({}),
|
||||
plastic: PD.Group({}),
|
||||
}, { label: 'Lighting', description: 'Style in which the 3D scene is rendered/lighted' }),
|
||||
light: PD.ObjectList({
|
||||
inclination: PD.Numeric(180, { min: 0, max: 180, step: 1 }),
|
||||
azimuth: PD.Numeric(0, { min: 0, max: 360, step: 1 }),
|
||||
color: PD.Color(Color.fromNormalizedRgb(1.0, 1.0, 1.0)),
|
||||
intensity: PD.Numeric(0.6, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
}, o => Color.toHexString(o.color), { defaultValue: [{
|
||||
inclination: 180,
|
||||
azimuth: 0,
|
||||
color: Color.fromNormalizedRgb(1.0, 1.0, 1.0),
|
||||
intensity: 0.6
|
||||
}] }),
|
||||
ambientColor: PD.Color(Color.fromNormalizedRgb(1.0, 1.0, 1.0)),
|
||||
ambientIntensity: PD.Numeric(0.4, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
|
||||
clip: PD.Group({
|
||||
variant: PD.Select('instance', PD.arrayToOptions<Clipping.Variant>(['instance', 'pixel'])),
|
||||
@@ -115,44 +114,27 @@ export const RendererParams = {
|
||||
};
|
||||
export type RendererProps = PD.Values<typeof RendererParams>
|
||||
|
||||
export type Style = {
|
||||
lightIntensity: number
|
||||
ambientIntensity: number
|
||||
metalness: number
|
||||
roughness: number
|
||||
reflectivity: number
|
||||
type Light = {
|
||||
count: number
|
||||
direction: number[]
|
||||
color: number[]
|
||||
}
|
||||
|
||||
export function getStyle(props: RendererProps['style']): Style {
|
||||
switch (props.name) {
|
||||
case 'custom':
|
||||
return props.params as Style;
|
||||
case 'flat':
|
||||
return {
|
||||
lightIntensity: 0, ambientIntensity: 1,
|
||||
metalness: 0, roughness: 0.4, reflectivity: 0.5
|
||||
};
|
||||
case 'matte':
|
||||
return {
|
||||
lightIntensity: 0.7, ambientIntensity: 0.3,
|
||||
metalness: 0, roughness: 1, reflectivity: 0.5
|
||||
};
|
||||
case 'glossy':
|
||||
return {
|
||||
lightIntensity: 0.7, ambientIntensity: 0.3,
|
||||
metalness: 0, roughness: 0.4, reflectivity: 0.5
|
||||
};
|
||||
case 'metallic':
|
||||
return {
|
||||
lightIntensity: 0.7, ambientIntensity: 0.7,
|
||||
metalness: 0.6, roughness: 0.6, reflectivity: 0.5
|
||||
};
|
||||
case 'plastic':
|
||||
return {
|
||||
lightIntensity: 0.7, ambientIntensity: 0.3,
|
||||
metalness: 0, roughness: 0.2, reflectivity: 0.5
|
||||
};
|
||||
const tmpDir = Vec3();
|
||||
const tmpColor = Vec3();
|
||||
function getLight(props: RendererProps['light'], light?: Light): Light {
|
||||
const { direction, color } = light || {
|
||||
direction: (new Array(5 * 3)).fill(0),
|
||||
color: (new Array(5 * 3)).fill(0),
|
||||
};
|
||||
for (let i = 0, il = props.length; i < il; ++i) {
|
||||
const p = props[i];
|
||||
Vec3.directionFromSpherical(tmpDir, degToRad(p.inclination), degToRad(p.azimuth), 1);
|
||||
Vec3.toArray(tmpDir, direction, i * 3);
|
||||
Vec3.scale(tmpColor, Color.toVec3Normalized(tmpColor, p.color), p.intensity);
|
||||
Vec3.toArray(tmpColor, color, i * 3);
|
||||
}
|
||||
return { count: props.length, direction, color };
|
||||
}
|
||||
|
||||
type Clip = {
|
||||
@@ -194,7 +176,7 @@ namespace Renderer {
|
||||
export function create(ctx: WebGLContext, props: Partial<RendererProps> = {}): Renderer {
|
||||
const { gl, state, stats, extensions: { fragDepth } } = ctx;
|
||||
const p = PD.merge(RendererParams, PD.getDefaultValues(RendererParams), props);
|
||||
const style = getStyle(p.style);
|
||||
const light = getLight(p.light);
|
||||
const clip = getClip(p.clip);
|
||||
|
||||
const viewport = Viewport();
|
||||
@@ -219,6 +201,9 @@ namespace Renderer {
|
||||
const cameraDir = Vec3();
|
||||
const viewOffset = Vec2();
|
||||
|
||||
const ambientColor = Vec3();
|
||||
Vec3.scale(ambientColor, Color.toArrayNormalized(p.ambientColor, ambientColor, 0), p.ambientIntensity);
|
||||
|
||||
const globalUniforms: GlobalUniformValues = {
|
||||
uModel: ValueCell.create(Mat4.identity()),
|
||||
uView: ValueCell.create(view),
|
||||
@@ -256,13 +241,9 @@ namespace Renderer {
|
||||
uClipObjectRotation: ValueCell.create(clip.objects.rotation),
|
||||
uClipObjectScale: ValueCell.create(clip.objects.scale),
|
||||
|
||||
// the following are general 'material' uniforms
|
||||
uLightIntensity: ValueCell.create(style.lightIntensity),
|
||||
uAmbientIntensity: ValueCell.create(style.ambientIntensity),
|
||||
|
||||
uMetalness: ValueCell.create(style.metalness),
|
||||
uRoughness: ValueCell.create(style.roughness),
|
||||
uReflectivity: ValueCell.create(style.reflectivity),
|
||||
uLightDirection: ValueCell.create(light.direction),
|
||||
uLightColor: ValueCell.create(light.color),
|
||||
uAmbientColor: ValueCell.create(ambientColor),
|
||||
|
||||
uPickingAlphaThreshold: ValueCell.create(p.pickingAlphaThreshold),
|
||||
|
||||
@@ -303,6 +284,10 @@ namespace Renderer {
|
||||
definesNeedUpdate = true;
|
||||
}
|
||||
}
|
||||
if (r.values.dLightCount.ref.value !== light.count) {
|
||||
ValueCell.update(r.values.dLightCount, light.count);
|
||||
definesNeedUpdate = true;
|
||||
}
|
||||
if (definesNeedUpdate) r.update();
|
||||
|
||||
const program = r.getProgram(variant);
|
||||
@@ -319,6 +304,10 @@ namespace Renderer {
|
||||
}
|
||||
|
||||
if (r.values.dRenderMode) { // indicates direct-volume
|
||||
if ((variant[0] === 'p' || variant === 'depth') && r.values.dRenderMode.ref.value === 'volume') {
|
||||
return; // no picking/depth in volume mode
|
||||
}
|
||||
|
||||
// culling done in fragment shader
|
||||
state.disable(gl.CULL_FACE);
|
||||
state.frontFace(gl.CCW);
|
||||
@@ -681,14 +670,21 @@ namespace Renderer {
|
||||
ValueCell.update(globalUniforms.uXrayEdgeFalloff, p.xrayEdgeFalloff);
|
||||
}
|
||||
|
||||
if (props.style !== undefined) {
|
||||
p.style = props.style;
|
||||
Object.assign(style, getStyle(props.style));
|
||||
ValueCell.updateIfChanged(globalUniforms.uLightIntensity, style.lightIntensity);
|
||||
ValueCell.updateIfChanged(globalUniforms.uAmbientIntensity, style.ambientIntensity);
|
||||
ValueCell.updateIfChanged(globalUniforms.uMetalness, style.metalness);
|
||||
ValueCell.updateIfChanged(globalUniforms.uRoughness, style.roughness);
|
||||
ValueCell.updateIfChanged(globalUniforms.uReflectivity, style.reflectivity);
|
||||
if (props.light !== undefined && !deepEqual(props.light, p.light)) {
|
||||
p.light = props.light;
|
||||
Object.assign(light, getLight(props.light, light));
|
||||
ValueCell.update(globalUniforms.uLightDirection, light.direction);
|
||||
ValueCell.update(globalUniforms.uLightColor, light.color);
|
||||
}
|
||||
if (props.ambientColor !== undefined && props.ambientColor !== p.ambientColor) {
|
||||
p.ambientColor = props.ambientColor;
|
||||
Vec3.scale(ambientColor, Color.toArrayNormalized(p.ambientColor, ambientColor, 0), p.ambientIntensity);
|
||||
ValueCell.update(globalUniforms.uAmbientColor, ambientColor);
|
||||
}
|
||||
if (props.ambientIntensity !== undefined && props.ambientIntensity !== p.ambientIntensity) {
|
||||
p.ambientIntensity = props.ambientIntensity;
|
||||
Vec3.scale(ambientColor, Color.toArrayNormalized(p.ambientColor, ambientColor, 0), p.ambientIntensity);
|
||||
ValueCell.update(globalUniforms.uAmbientColor, ambientColor);
|
||||
}
|
||||
|
||||
if (props.clip !== undefined && !deepEqual(props.clip, p.clip)) {
|
||||
@@ -716,6 +712,9 @@ namespace Renderer {
|
||||
ValueCell.update(globalUniforms.uDrawingBufferSize, Vec2.set(drawingBufferSize, width, height));
|
||||
}
|
||||
},
|
||||
setPixelRatio: (value: number) => {
|
||||
ValueCell.update(globalUniforms.uPixelRatio, value);
|
||||
},
|
||||
|
||||
props: p,
|
||||
get stats(): RendererStats {
|
||||
|
||||
@@ -17,7 +17,6 @@ const shaderCodeId = idFactory();
|
||||
|
||||
type ShaderExtensionsValue = 'required' | 'optional'
|
||||
export interface ShaderExtensions {
|
||||
readonly standardDerivatives?: ShaderExtensionsValue
|
||||
readonly fragDepth?: ShaderExtensionsValue
|
||||
readonly drawBuffers?: ShaderExtensionsValue
|
||||
readonly shaderTextureLod?: ShaderExtensionsValue
|
||||
@@ -101,7 +100,8 @@ const ShaderChunks: { [k: string]: string } = {
|
||||
wboit_write
|
||||
};
|
||||
|
||||
const reInclude = /^(?!\/\/)\s*#include\s+(\S+)/gmi;
|
||||
const reInclude = /^(?!\/\/)\s*#include\s+(\S+)/gm;
|
||||
const reUnrollLoop = /#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*\+\+i\s*\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g;
|
||||
const reSingleLineComment = /[ \t]*\/\/.*\n/g;
|
||||
const reMultiLineComment = /[ \t]*\/\*[\s\S]*?\*\//g;
|
||||
const reMultipleLinebreaks = /\n{2,}/g;
|
||||
@@ -119,6 +119,30 @@ function addIncludes(text: string) {
|
||||
.replace(reMultipleLinebreaks, '\n');
|
||||
}
|
||||
|
||||
function unrollLoops(str: string) {
|
||||
return str.replace(reUnrollLoop, loopReplacer);
|
||||
}
|
||||
|
||||
function loopReplacer(match: string, start: string, end: string, snippet: string) {
|
||||
let out = '';
|
||||
for (let i = parseInt(start); i < parseInt(end); ++i) {
|
||||
out += snippet
|
||||
.replace(/\[\s*i\s*\]/g, `[${i}]`)
|
||||
.replace(/UNROLLED_LOOP_INDEX/g, `${i}`);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function replaceCounts(str: string, defines: ShaderDefines) {
|
||||
if (defines.dLightCount) str = str.replace(/dLightCount/g, `${defines.dLightCount.ref.value}`);
|
||||
if (defines.dClipObjectCount) str = str.replace(/dClipObjectCount/g, `${defines.dClipObjectCount.ref.value}`);
|
||||
return str;
|
||||
}
|
||||
|
||||
function preprocess(str: string, defines: ShaderDefines) {
|
||||
return unrollLoops(replaceCounts(str, defines));
|
||||
}
|
||||
|
||||
export function ShaderCode(name: string, vert: string, frag: string, extensions: ShaderExtensions = {}, outTypes: FragOutTypes = {}): ShaderCode {
|
||||
return { id: shaderCodeId(), name, vert: addIncludes(vert), frag: addIncludes(frag), extensions, outTypes };
|
||||
}
|
||||
@@ -139,7 +163,7 @@ export const CylindersShaderCode = ShaderCode('cylinders', cylinders_vert, cylin
|
||||
|
||||
import { text_vert } from './shader/text.vert';
|
||||
import { text_frag } from './shader/text.frag';
|
||||
export const TextShaderCode = ShaderCode('text', text_vert, text_frag, { standardDerivatives: 'required', drawBuffers: 'optional' });
|
||||
export const TextShaderCode = ShaderCode('text', text_vert, text_frag, { drawBuffers: 'optional' });
|
||||
|
||||
import { lines_vert } from './shader/lines.vert';
|
||||
import { lines_frag } from './shader/lines.frag';
|
||||
@@ -147,7 +171,7 @@ export const LinesShaderCode = ShaderCode('lines', lines_vert, lines_frag, { dra
|
||||
|
||||
import { mesh_vert } from './shader/mesh.vert';
|
||||
import { mesh_frag } from './shader/mesh.frag';
|
||||
export const MeshShaderCode = ShaderCode('mesh', mesh_vert, mesh_frag, { standardDerivatives: 'optional', drawBuffers: 'optional' });
|
||||
export const MeshShaderCode = ShaderCode('mesh', mesh_vert, mesh_frag, { drawBuffers: 'optional' });
|
||||
|
||||
import { directVolume_vert } from './shader/direct-volume.vert';
|
||||
import { directVolume_frag } from './shader/direct-volume.frag';
|
||||
@@ -185,11 +209,9 @@ function getDefinesCode(defines: ShaderDefines) {
|
||||
}
|
||||
|
||||
function getGlsl100FragPrefix(extensions: WebGLExtensions, shaderExtensions: ShaderExtensions) {
|
||||
const prefix: string[] = [];
|
||||
if (shaderExtensions.standardDerivatives) {
|
||||
prefix.push('#extension GL_OES_standard_derivatives : enable');
|
||||
prefix.push('#define enabledStandardDerivatives');
|
||||
}
|
||||
const prefix: string[] = [
|
||||
'#extension GL_OES_standard_derivatives : enable'
|
||||
];
|
||||
if (shaderExtensions.fragDepth) {
|
||||
if (extensions.fragDepth) {
|
||||
prefix.push('#extension GL_EXT_frag_depth : enable');
|
||||
@@ -244,9 +266,6 @@ function getGlsl300FragPrefix(gl: WebGL2RenderingContext, extensions: WebGLExten
|
||||
`layout(location = 0) out highp ${outTypes[0] || 'vec4'} out_FragData0;`
|
||||
];
|
||||
|
||||
if (shaderExtensions.standardDerivatives) {
|
||||
prefix.push('#define enabledStandardDerivatives');
|
||||
}
|
||||
if (shaderExtensions.fragDepth) {
|
||||
prefix.push('#define enabledFragDepth');
|
||||
}
|
||||
@@ -278,8 +297,8 @@ export function addShaderDefines(gl: GLRenderingContext, extensions: WebGLExtens
|
||||
return {
|
||||
id: shaderCodeId(),
|
||||
name: shaders.name,
|
||||
vert: `${vertPrefix}${header}${shaders.vert}`,
|
||||
frag: `${fragPrefix}${header}${frag}`,
|
||||
vert: `${vertPrefix}${header}${preprocess(shaders.vert, defines)}`,
|
||||
frag: `${fragPrefix}${header}${preprocess(frag, defines)}`,
|
||||
extensions: shaders.extensions,
|
||||
outTypes: shaders.outTypes
|
||||
};
|
||||
|
||||
@@ -1,34 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*
|
||||
* adapted from three.js (https://github.com/mrdoob/three.js/)
|
||||
* which under the MIT License, Copyright © 2010-2019 three.js authors
|
||||
* which under the MIT License, Copyright © 2010-2021 three.js authors
|
||||
*/
|
||||
|
||||
export const apply_light_color = `
|
||||
// inputs
|
||||
// - vec4 material
|
||||
// - vec3 vViewPosition
|
||||
// - vec3 normal
|
||||
// - float uMetalness
|
||||
// - float uRoughness
|
||||
// - float uReflectivity
|
||||
// - float uLightIntensity
|
||||
// - float uAmbientIntensity
|
||||
|
||||
// outputs
|
||||
// - sets gl_FragColor
|
||||
#ifdef bumpEnabled
|
||||
if (uBumpFrequency > 0.0 && uBumpAmplitude > 0.0) {
|
||||
vec3 bumpNormal = perturbNormal(-vViewPosition, normal, fbm(vModelPosition * uBumpFrequency), (uBumpAmplitude * bumpiness) / uBumpFrequency);
|
||||
#ifdef enabledFragDepth
|
||||
if (!any(isNaN(bumpNormal))) normal = bumpNormal;
|
||||
#else
|
||||
normal = bumpNormal;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
vec4 color = material;
|
||||
|
||||
ReflectedLight reflectedLight = ReflectedLight(vec3(0.0), vec3(0.0), vec3(0.0));
|
||||
ReflectedLight reflectedLight = ReflectedLight(vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0));
|
||||
|
||||
PhysicalMaterial physicalMaterial;
|
||||
physicalMaterial.diffuseColor = color.rgb * (1.0 - uMetalness);
|
||||
physicalMaterial.specularRoughness = clamp(uRoughness, 0.04, 1.0);
|
||||
physicalMaterial.specularColor = mix(vec3(0.16 * pow2(uReflectivity)), color.rgb, uMetalness);
|
||||
physicalMaterial.diffuseColor = color.rgb * (1.0 - metalness);
|
||||
#ifdef enabledFragDepth
|
||||
physicalMaterial.roughness = min(max(roughness, 0.0525), 1.0);
|
||||
#else
|
||||
vec3 dxy = max(abs(dFdx(normal)), abs(dFdy(normal)));
|
||||
float geometryRoughness = max(max(dxy.x, dxy.y), dxy.z);
|
||||
physicalMaterial.roughness = min(max(roughness, 0.0525) + geometryRoughness, 1.0);
|
||||
#endif
|
||||
physicalMaterial.specularColor = mix(vec3(0.04), color.rgb, metalness);
|
||||
physicalMaterial.specularF90 = 1.0;
|
||||
|
||||
GeometricContext geometry;
|
||||
geometry.position = -vViewPosition;
|
||||
@@ -36,19 +41,28 @@ geometry.normal = normal;
|
||||
geometry.viewDir = normalize(vViewPosition);
|
||||
|
||||
IncidentLight directLight;
|
||||
directLight.direction = vec3(0.0, 0.0, -1.0);
|
||||
directLight.color = vec3(uLightIntensity);
|
||||
#pragma unroll_loop_start
|
||||
for (int i = 0; i < dLightCount; ++i) {
|
||||
directLight.direction = uLightDirection[i];
|
||||
directLight.color = uLightColor[i] * PI; // * PI for punctual light
|
||||
RE_Direct_Physical(directLight, geometry, physicalMaterial, reflectedLight);
|
||||
}
|
||||
#pragma unroll_loop_end
|
||||
|
||||
RE_Direct_Physical(directLight, geometry, physicalMaterial, reflectedLight);
|
||||
|
||||
vec3 irradiance = vec3(uAmbientIntensity) * PI;
|
||||
vec3 irradiance = uAmbientColor * PI; // * PI for punctual light
|
||||
RE_IndirectDiffuse_Physical(irradiance, geometry, physicalMaterial, reflectedLight);
|
||||
|
||||
vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular;
|
||||
// indirect specular only metals
|
||||
vec3 radiance = uAmbientColor * metalness;
|
||||
vec3 iblIrradiance = uAmbientColor * metalness;
|
||||
vec3 clearcoatRadiance = vec3(0.0);
|
||||
RE_IndirectSpecular_Physical(radiance, iblIrradiance, clearcoatRadiance, geometry, physicalMaterial, reflectedLight);
|
||||
|
||||
vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular;
|
||||
|
||||
gl_FragColor = vec4(outgoingLight, color.a);
|
||||
|
||||
#ifdef dXrayShaded
|
||||
gl_FragColor.a *= 1.0 - pow(abs(dot(normal, vec3(0, 0, 1))), uXrayEdgeFalloff);
|
||||
gl_FragColor.a *= 1.0 - pow(abs(dot(normal, vec3(0.0, 0.0, 1.0))), uXrayEdgeFalloff);
|
||||
#endif
|
||||
`;
|
||||
@@ -13,11 +13,11 @@ export const assign_color_varying = `
|
||||
#elif defined(dColorType_vertexInstance)
|
||||
vColor.rgb = readFromTexture(tColor, int(aInstance) * uVertexCount + VertexID, uColorTexDim).rgb;
|
||||
#elif defined(dColorType_volume)
|
||||
vec3 gridPos = (uColorGridTransform.w * (position - uColorGridTransform.xyz)) / uColorGridDim;
|
||||
vColor.rgb = texture3dFrom2dLinear(tColorGrid, gridPos, uColorGridDim, uColorTexDim).rgb;
|
||||
vec3 cgridPos = (uColorGridTransform.w * (position - uColorGridTransform.xyz)) / uColorGridDim;
|
||||
vColor.rgb = texture3dFrom2dLinear(tColorGrid, cgridPos, uColorGridDim, uColorTexDim).rgb;
|
||||
#elif defined(dColorType_volumeInstance)
|
||||
vec3 gridPos = (uColorGridTransform.w * (vModelPosition - uColorGridTransform.xyz)) / uColorGridDim;
|
||||
vColor.rgb = texture3dFrom2dLinear(tColorGrid, gridPos, uColorGridDim, uColorTexDim).rgb;
|
||||
vec3 cgridPos = (uColorGridTransform.w * (vModelPosition - uColorGridTransform.xyz)) / uColorGridDim;
|
||||
vColor.rgb = texture3dFrom2dLinear(tColorGrid, cgridPos, uColorGridDim, uColorTexDim).rgb;
|
||||
#endif
|
||||
|
||||
#ifdef dUsePalette
|
||||
@@ -25,7 +25,35 @@ export const assign_color_varying = `
|
||||
#endif
|
||||
|
||||
#ifdef dOverpaint
|
||||
vOverpaint = readFromTexture(tOverpaint, aInstance * float(uGroupCount) + group, uOverpaintTexDim);
|
||||
#if defined(dOverpaintType_groupInstance)
|
||||
vOverpaint = readFromTexture(tOverpaint, aInstance * float(uGroupCount) + group, uOverpaintTexDim);
|
||||
#elif defined(dOverpaintType_vertexInstance)
|
||||
vOverpaint = readFromTexture(tOverpaint, int(aInstance) * uVertexCount + VertexID, uOverpaintTexDim);
|
||||
#elif defined(dOverpaintType_volumeInstance)
|
||||
vec3 ogridPos = (uOverpaintGridTransform.w * (vModelPosition - uOverpaintGridTransform.xyz)) / uOverpaintGridDim;
|
||||
vOverpaint = texture3dFrom2dLinear(tOverpaintGrid, ogridPos, uOverpaintGridDim, uOverpaintTexDim);
|
||||
#endif
|
||||
|
||||
// pre-mix to avoid darkening due to empty overpaint
|
||||
#ifdef dColorType_uniform
|
||||
vOverpaint.rgb = mix(uColor.rgb, vOverpaint.rgb, vOverpaint.a);
|
||||
#else
|
||||
vOverpaint.rgb = mix(vColor.rgb, vOverpaint.rgb, vOverpaint.a);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef dSubstance
|
||||
#if defined(dSubstanceType_groupInstance)
|
||||
vSubstance = readFromTexture(tSubstance, aInstance * float(uGroupCount) + group, uSubstanceTexDim);
|
||||
#elif defined(dSubstanceType_vertexInstance)
|
||||
vSubstance = readFromTexture(tSubstance, int(aInstance) * uVertexCount + VertexID, uSubstanceTexDim);
|
||||
#elif defined(dSubstanceType_volumeInstance)
|
||||
vec3 sgridPos = (uSubstanceGridTransform.w * (vModelPosition - uSubstanceGridTransform.xyz)) / uSubstanceGridDim;
|
||||
vSubstance = texture3dFrom2dLinear(tSubstanceGrid, sgridPos, uSubstanceGridDim, uSubstanceTexDim);
|
||||
#endif
|
||||
|
||||
// pre-mix to avoid artifacts due to empty substance
|
||||
vSubstance.rgb = mix(vec3(uMetalness, uRoughness, uBumpiness), vSubstance.rgb, vSubstance.a);
|
||||
#endif
|
||||
#elif defined(dRenderVariant_pick)
|
||||
#if defined(dRenderVariant_pickObject)
|
||||
@@ -39,6 +67,14 @@ export const assign_color_varying = `
|
||||
|
||||
#ifdef dTransparency
|
||||
vGroup = group;
|
||||
vTransparency = readFromTexture(tTransparency, aInstance * float(uGroupCount) + group, uTransparencyTexDim).a;
|
||||
|
||||
#if defined(dTransparencyType_groupInstance)
|
||||
vTransparency = readFromTexture(tTransparency, aInstance * float(uGroupCount) + group, uTransparencyTexDim).a;
|
||||
#elif defined(dTransparencyType_vertexInstance)
|
||||
vTransparency = readFromTexture(tTransparency, int(aInstance) * uVertexCount + VertexID, uTransparencyTexDim).a;
|
||||
#elif defined(dTransparencyType_volumeInstance)
|
||||
vec3 tgridPos = (uTransparencyGridTransform.w * (vModelPosition - uTransparencyGridTransform.xyz)) / uTransparencyGridDim;
|
||||
vTransparency = texture3dFrom2dLinear(tTransparencyGrid, tgridPos, uTransparencyGridDim, uTransparencyTexDim).a;
|
||||
#endif
|
||||
#endif
|
||||
`;
|
||||
@@ -3,9 +3,8 @@ export const assign_material_color = `
|
||||
#if defined(dMarkerType_uniform)
|
||||
float marker = uMarker;
|
||||
#elif defined(dMarkerType_groupInstance)
|
||||
float marker = vMarker;
|
||||
float marker = floor(vMarker * 255.0 + 0.5); // rounding required to work on some cards on win
|
||||
#endif
|
||||
marker = floor(marker * 255.0 + 0.5); // rounding required to work on some cards on win
|
||||
#endif
|
||||
|
||||
#if defined(dRenderVariant_color)
|
||||
@@ -21,6 +20,15 @@ export const assign_material_color = `
|
||||
#if defined(dOverpaint)
|
||||
material.rgb = mix(material.rgb, vOverpaint.rgb, vOverpaint.a);
|
||||
#endif
|
||||
|
||||
float metalness = uMetalness;
|
||||
float roughness = uRoughness;
|
||||
float bumpiness = uBumpiness;
|
||||
#ifdef dSubstance
|
||||
metalness = mix(metalness, vSubstance.r, vSubstance.a);
|
||||
roughness = mix(roughness, vSubstance.g, vSubstance.a);
|
||||
bumpiness = mix(bumpiness, vSubstance.b, vSubstance.a);
|
||||
#endif
|
||||
#elif defined(dRenderVariant_pick)
|
||||
vec4 material = vColor;
|
||||
#elif defined(dRenderVariant_depth)
|
||||
@@ -55,6 +63,9 @@ export const assign_material_color = `
|
||||
// apply screendoor transparency
|
||||
#if defined(dTransparency)
|
||||
float ta = 1.0 - vTransparency;
|
||||
#if defined(dRenderVariant_colorWboit)
|
||||
if (vTransparency < 0.2) ta = 1.0; // hard cutoff looks better with wboit
|
||||
#endif
|
||||
|
||||
#if defined(dRenderVariant_pick)
|
||||
if (ta < uPickingAlphaThreshold)
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
export const color_frag_params = `
|
||||
uniform float uMetalness;
|
||||
uniform float uRoughness;
|
||||
uniform float uBumpiness;
|
||||
#ifdef bumpEnabled
|
||||
uniform float uBumpFrequency;
|
||||
uniform float uBumpAmplitude;
|
||||
#endif
|
||||
|
||||
#if defined(dRenderVariant_color)
|
||||
#if defined(dColorType_uniform)
|
||||
uniform vec3 uColor;
|
||||
@@ -9,6 +17,10 @@ export const color_frag_params = `
|
||||
#ifdef dOverpaint
|
||||
varying vec4 vOverpaint;
|
||||
#endif
|
||||
|
||||
#ifdef dSubstance
|
||||
varying vec4 vSubstance;
|
||||
#endif
|
||||
#elif defined(dRenderVariant_pick)
|
||||
#if __VERSION__ == 100
|
||||
varying vec4 vColor;
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
export const color_vert_params = `
|
||||
uniform float uMetalness;
|
||||
uniform float uRoughness;
|
||||
uniform float uBumpiness;
|
||||
|
||||
#if defined(dRenderVariant_color)
|
||||
#if defined(dColorType_uniform)
|
||||
uniform vec3 uColor;
|
||||
@@ -18,9 +22,31 @@ export const color_vert_params = `
|
||||
#endif
|
||||
|
||||
#ifdef dOverpaint
|
||||
varying vec4 vOverpaint;
|
||||
uniform vec2 uOverpaintTexDim;
|
||||
uniform sampler2D tOverpaint;
|
||||
#if defined(dOverpaintType_groupInstance) || defined(dOverpaintType_vertexInstance)
|
||||
varying vec4 vOverpaint;
|
||||
uniform vec2 uOverpaintTexDim;
|
||||
uniform sampler2D tOverpaint;
|
||||
#elif defined(dOverpaintType_volumeInstance)
|
||||
varying vec4 vOverpaint;
|
||||
uniform vec2 uOverpaintTexDim;
|
||||
uniform vec3 uOverpaintGridDim;
|
||||
uniform vec4 uOverpaintGridTransform;
|
||||
uniform sampler2D tOverpaintGrid;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef dSubstance
|
||||
#if defined(dSubstanceType_groupInstance) || defined(dSubstanceType_vertexInstance)
|
||||
varying vec4 vSubstance;
|
||||
uniform vec2 uSubstanceTexDim;
|
||||
uniform sampler2D tSubstance;
|
||||
#elif defined(dSubstanceType_volumeInstance)
|
||||
varying vec4 vSubstance;
|
||||
uniform vec2 uSubstanceTexDim;
|
||||
uniform vec3 uSubstanceGridDim;
|
||||
uniform vec4 uSubstanceGridTransform;
|
||||
uniform sampler2D tSubstanceGrid;
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(dRenderVariant_pick)
|
||||
#if __VERSION__ == 100
|
||||
@@ -32,9 +58,17 @@ export const color_vert_params = `
|
||||
|
||||
#ifdef dTransparency
|
||||
varying float vGroup;
|
||||
varying float vTransparency;
|
||||
uniform vec2 uTransparencyTexDim;
|
||||
uniform sampler2D tTransparency;
|
||||
#if defined(dTransparencyType_groupInstance) || defined(dTransparencyType_vertexInstance)
|
||||
varying float vTransparency;
|
||||
uniform vec2 uTransparencyTexDim;
|
||||
uniform sampler2D tTransparency;
|
||||
#elif defined(dTransparencyType_volumeInstance)
|
||||
varying float vTransparency;
|
||||
uniform vec2 uTransparencyTexDim;
|
||||
uniform vec3 uTransparencyGridDim;
|
||||
uniform vec4 uTransparencyGridTransform;
|
||||
uniform sampler2D tTransparencyGrid;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef dUsePalette
|
||||
|
||||
@@ -89,8 +89,9 @@ export const common_clip = `
|
||||
|
||||
// flag is a bit-flag for clip-objects to ignore (note, object ids start at 1 not 0)
|
||||
bool clipTest(vec4 sphere, int flag) {
|
||||
#pragma unroll_loop_start
|
||||
for (int i = 0; i < dClipObjectCount; ++i) {
|
||||
if (flag == 0 || hasBit(flag, i + 1)) {
|
||||
if (flag == 0 || hasBit(flag, UNROLLED_LOOP_INDEX + 1)) {
|
||||
// TODO take sphere radius into account?
|
||||
bool test = getSignedDistance(sphere.xyz, uClipObjectType[i], uClipObjectPosition[i], uClipObjectRotation[i], uClipObjectScale[i]) <= 0.0;
|
||||
if ((!uClipObjectInvert[i] && test) || (uClipObjectInvert[i] && !test)) {
|
||||
@@ -98,6 +99,7 @@ export const common_clip = `
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma unroll_loop_end
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -76,4 +76,49 @@ float calcDepth(const in vec3 pos) {
|
||||
vec2 clipZW = pos.z * uProjection[2].zw + uProjection[3].zw;
|
||||
return 0.5 + 0.5 * clipZW.x / clipZW.y;
|
||||
}
|
||||
|
||||
// "Bump Mapping Unparametrized Surfaces on the GPU" Morten S. Mikkelsen
|
||||
// https://mmikk.github.io/papers3d/mm_sfgrad_bump.pdf
|
||||
vec3 perturbNormal(in vec3 position, in vec3 normal, in float height, in float scale) {
|
||||
vec3 sigmaS = dFdx(position);
|
||||
vec3 sigmaT = dFdy(position);
|
||||
|
||||
vec3 r1 = cross(sigmaT, normal);
|
||||
vec3 r2 = cross(normal, sigmaS);
|
||||
float det = dot(sigmaS, r1);
|
||||
|
||||
float bs = dFdx(height);
|
||||
float bt = dFdy(height);
|
||||
|
||||
vec3 surfGrad = sign(det) * (bs * r1 + bt * r2);
|
||||
return normalize(abs(det) * normal - scale * surfGrad);
|
||||
}
|
||||
|
||||
float hash(in float h) {
|
||||
return fract(sin(h) * 43758.5453123);
|
||||
}
|
||||
|
||||
float noise(in vec3 x) {
|
||||
vec3 p = floor(x);
|
||||
vec3 f = fract(x);
|
||||
f = f * f * (3.0 - 2.0 * f);
|
||||
|
||||
float n = p.x + p.y * 157.0 + 113.0 * p.z;
|
||||
return mix(
|
||||
mix(mix(hash(n + 0.0), hash(n + 1.0), f.x),
|
||||
mix(hash(n + 157.0), hash(n + 158.0), f.x), f.y),
|
||||
mix(mix(hash(n + 113.0), hash(n + 114.0), f.x),
|
||||
mix(hash(n + 270.0), hash(n + 271.0), f.x), f.y), f.z);
|
||||
}
|
||||
|
||||
float fbm(in vec3 p) {
|
||||
float f = 0.0;
|
||||
f += 0.5 * noise(p);
|
||||
p *= 2.01;
|
||||
f += 0.25 * noise(p);
|
||||
p *= 2.02;
|
||||
f += 0.125 * noise(p);
|
||||
|
||||
return f;
|
||||
}
|
||||
`;
|
||||
@@ -214,6 +214,9 @@ float depthToViewZ(const in float isOrtho, const in float linearClipZ, const in
|
||||
a31 * b01 - a30 * b03 - a32 * b00,
|
||||
a20 * b03 - a21 * b01 + a22 * b00) / det;
|
||||
}
|
||||
|
||||
#define isNaN(x) ( (x) != (x) )
|
||||
#define isInf(x) ( (x) == (x)+1. )
|
||||
#else
|
||||
#define transpose2(m) transpose(m)
|
||||
#define transpose3(m) transpose(m)
|
||||
@@ -222,5 +225,8 @@ float depthToViewZ(const in float isOrtho, const in float linearClipZ, const in
|
||||
#define inverse2(m) inverse(m)
|
||||
#define inverse3(m) inverse(m)
|
||||
#define inverse4(m) inverse(m)
|
||||
|
||||
#define isNaN isnan
|
||||
#define isInf isinf
|
||||
#endif
|
||||
`;
|
||||
@@ -1,23 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*
|
||||
* adapted from three.js (https://github.com/mrdoob/three.js/)
|
||||
* which under the MIT License, Copyright © 2010-2019 three.js authors
|
||||
* which under the MIT License, Copyright © 2010-2021 three.js authors
|
||||
*/
|
||||
|
||||
export const light_frag_params = `
|
||||
uniform float uLightIntensity;
|
||||
uniform float uAmbientIntensity;
|
||||
uniform float uReflectivity;
|
||||
uniform float uMetalness;
|
||||
uniform float uRoughness;
|
||||
#if dLightCount != 0
|
||||
uniform vec3 uLightDirection[dLightCount];
|
||||
uniform vec3 uLightColor[dLightCount];
|
||||
#endif
|
||||
uniform vec3 uAmbientColor;
|
||||
|
||||
struct PhysicalMaterial {
|
||||
vec3 diffuseColor;
|
||||
float specularRoughness;
|
||||
float roughness;
|
||||
vec3 specularColor;
|
||||
float specularF90;
|
||||
};
|
||||
|
||||
struct IncidentLight {
|
||||
@@ -29,6 +30,7 @@ struct ReflectedLight {
|
||||
vec3 directDiffuse;
|
||||
vec3 directSpecular;
|
||||
vec3 indirectDiffuse;
|
||||
vec3 indirectSpecular;
|
||||
};
|
||||
|
||||
struct GeometricContext {
|
||||
@@ -37,20 +39,23 @@ struct GeometricContext {
|
||||
vec3 viewDir;
|
||||
};
|
||||
|
||||
vec3 F_Schlick(const in vec3 specularColor, const in float dotLH) {
|
||||
vec3 BRDF_Lambert(const in vec3 diffuseColor) {
|
||||
return RECIPROCAL_PI * diffuseColor;
|
||||
}
|
||||
|
||||
vec3 F_Schlick(const in vec3 f0, const in float f90, const in float dotVH) {
|
||||
// Original approximation by Christophe Schlick '94
|
||||
// float fresnel = pow( 1.0 - dotLH, 5.0 );
|
||||
// float fresnel = pow( 1.0 - dotVH, 5.0 );
|
||||
// Optimized variant (presented by Epic at SIGGRAPH '13)
|
||||
// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
|
||||
float fresnel = exp2((-5.55473 * dotLH - 6.98316) * dotLH);
|
||||
return (1.0 - specularColor) * fresnel + specularColor;
|
||||
float fresnel = exp2((-5.55473 * dotVH - 6.98316) * dotVH);
|
||||
return f0 * (1.0 - fresnel) + (f90 * fresnel);
|
||||
}
|
||||
|
||||
// Moving Frostbite to Physically Based Rendering 3.0 - page 12, listing 2
|
||||
// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
|
||||
float G_GGX_SmithCorrelated(const in float alpha, const in float dotNL, const in float dotNV) {
|
||||
float V_GGX_SmithCorrelated(const in float alpha, const in float dotNL, const in float dotNV) {
|
||||
float a2 = pow2(alpha);
|
||||
// dotNL and dotNV are explicitly swapped. This is not a mistake.
|
||||
float gv = dotNL * sqrt(a2 + (1.0 - a2) * pow2(dotNV));
|
||||
float gl = dotNV * sqrt(a2 + (1.0 - a2) * pow2(dotNL));
|
||||
return 0.5 / max(gv + gl, EPSILON);
|
||||
@@ -65,47 +70,68 @@ float D_GGX(const in float alpha, const in float dotNH) {
|
||||
return RECIPROCAL_PI * a2 / pow2(denom);
|
||||
}
|
||||
|
||||
vec3 BRDF_Diffuse_Lambert(const in vec3 diffuseColor) {
|
||||
return RECIPROCAL_PI * diffuseColor;
|
||||
}
|
||||
|
||||
// GGX Distribution, Schlick Fresnel, GGX-Smith Visibility
|
||||
vec3 BRDF_Specular_GGX(const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness) {
|
||||
// GGX Distribution, Schlick Fresnel, GGX_SmithCorrelated Visibility
|
||||
vec3 BRDF_GGX(const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 f0, const in float f90, const in float roughness) {
|
||||
float alpha = pow2(roughness); // UE4's roughness
|
||||
vec3 halfDir = normalize(incidentLight.direction + geometry.viewDir);
|
||||
|
||||
float dotNL = saturate(dot(geometry.normal, incidentLight.direction));
|
||||
float dotNV = saturate(dot(geometry.normal, geometry.viewDir));
|
||||
float dotNH = saturate(dot(geometry.normal, halfDir));
|
||||
float dotLH = saturate(dot(incidentLight.direction, halfDir));
|
||||
|
||||
vec3 F = F_Schlick(specularColor, dotLH);
|
||||
float G = G_GGX_SmithCorrelated(alpha, dotNL, dotNV);
|
||||
vec3 halfDir = normalize( lightDir + viewDir);
|
||||
float dotNL = saturate(dot(normal, lightDir));
|
||||
float dotNV = saturate(dot(normal, viewDir));
|
||||
float dotNH = saturate(dot(normal, halfDir));
|
||||
float dotVH = saturate(dot(viewDir, halfDir));
|
||||
vec3 F = F_Schlick(f0, f90, dotVH);
|
||||
float V = V_GGX_SmithCorrelated(alpha, dotNL, dotNV);
|
||||
float D = D_GGX(alpha, dotNH);
|
||||
return F * (G * D);
|
||||
return F * (V * D);
|
||||
}
|
||||
|
||||
// ref: https://www.unrealengine.com/blog/physically-based-shading-on-mobile - environmentBRDF for GGX on mobile
|
||||
vec3 BRDF_Specular_GGX_Environment(const in GeometricContext geometry, const in vec3 specularColor, const in float roughness) {
|
||||
float dotNV = saturate(dot(geometry.normal, geometry.viewDir));
|
||||
// Analytical approximation of the DFG LUT, one half of the
|
||||
// split-sum approximation used in indirect specular lighting.
|
||||
// via 'environmentBRDF' from "Physically Based Shading on Mobile"
|
||||
// https://www.unrealengine.com/blog/physically-based-shading-on-mobile
|
||||
vec2 DFGApprox(const in vec3 normal, const in vec3 viewDir, const in float roughness) {
|
||||
float dotNV = saturate(dot(normal, viewDir));
|
||||
const vec4 c0 = vec4(-1, -0.0275, -0.572, 0.022);
|
||||
const vec4 c1 = vec4(1, 0.0425, 1.04, -0.04);
|
||||
vec4 r = roughness * c0 + c1;
|
||||
float a004 = min(r.x * r.x, exp2(-9.28 * dotNV)) * r.x + r.y;
|
||||
vec2 AB = vec2(-1.04, 1.04) * a004 + r.zw;
|
||||
return specularColor * AB.x + AB.y;
|
||||
vec2 fab = vec2(-1.04, 1.04) * a004 + r.zw;
|
||||
return fab;
|
||||
}
|
||||
|
||||
// Fdez-Agüera's "Multiple-Scattering Microfacet Model for Real-Time Image Based Lighting"
|
||||
// Approximates multiscattering in order to preserve energy.
|
||||
// http://www.jcgt.org/published/0008/01/03/
|
||||
void computeMultiscattering(const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter) {
|
||||
vec2 fab = DFGApprox(normal, viewDir, roughness);
|
||||
vec3 FssEss = specularColor * fab.x + specularF90 * fab.y;
|
||||
float Ess = fab.x + fab.y;
|
||||
float Ems = 1.0 - Ess;
|
||||
vec3 Favg = specularColor + (1.0 - specularColor) * 0.047619; // 1/21
|
||||
vec3 Fms = FssEss * Favg / (1.0 - Ems * Favg);
|
||||
singleScatter += FssEss;
|
||||
multiScatter += Fms * Ems;
|
||||
}
|
||||
|
||||
void RE_Direct_Physical(const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {
|
||||
float dotNL = saturate(dot(geometry.normal, directLight.direction));
|
||||
vec3 irradiance = dotNL * directLight.color;
|
||||
irradiance *= PI; // punctual light
|
||||
|
||||
reflectedLight.directSpecular += irradiance * BRDF_Specular_GGX(directLight, geometry, material.specularColor, material.specularRoughness);
|
||||
reflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert(material.diffuseColor);
|
||||
reflectedLight.directSpecular += irradiance * BRDF_GGX(directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularF90, material.roughness);
|
||||
reflectedLight.directDiffuse += irradiance * BRDF_Lambert(material.diffuseColor);
|
||||
}
|
||||
|
||||
void RE_IndirectDiffuse_Physical(const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {
|
||||
reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert(material.diffuseColor);
|
||||
reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert(material.diffuseColor);
|
||||
}
|
||||
|
||||
void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {
|
||||
// Both indirect specular and indirect diffuse light accumulate here
|
||||
vec3 singleScattering = vec3(0.0);
|
||||
vec3 multiScattering = vec3(0.0);
|
||||
vec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;
|
||||
computeMultiscattering(geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering);
|
||||
vec3 diffuse = material.diffuseColor * (1.0 - ( singleScattering + multiScattering));
|
||||
reflectedLight.indirectSpecular += radiance * singleScattering;
|
||||
reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;
|
||||
reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;
|
||||
}
|
||||
`;
|
||||
@@ -8,7 +8,7 @@ export const accumulate_frag = `
|
||||
precision highp float;
|
||||
|
||||
varying vec3 vPosition;
|
||||
varying vec3 vColor;
|
||||
varying vec4 vColor;
|
||||
|
||||
uniform float uCurrentSlice;
|
||||
uniform float uCurrentX;
|
||||
@@ -23,6 +23,8 @@ void main() {
|
||||
float dist = distance(fragPos, vPosition);
|
||||
if (dist > p) discard;
|
||||
|
||||
gl_FragColor = vec4(vColor, 1.0) * (p - dist);
|
||||
float f = p - dist;
|
||||
gl_FragColor = vColor * f;
|
||||
gl_FragData[1] = vec4(f);
|
||||
}
|
||||
`;
|
||||
@@ -27,7 +27,7 @@ uniform vec2 uColorTexDim;
|
||||
uniform sampler2D tColor;
|
||||
|
||||
varying vec3 vPosition;
|
||||
varying vec3 vColor;
|
||||
varying vec4 vColor;
|
||||
|
||||
uniform vec3 uBboxSize;
|
||||
uniform vec3 uBboxMin;
|
||||
@@ -43,9 +43,9 @@ void main() {
|
||||
gl_Position = vec4(((position - uBboxMin) / uBboxSize) * 2.0 - 1.0, 1.0);
|
||||
|
||||
#if defined(dColorType_group)
|
||||
vColor = readFromTexture(tColor, group, uColorTexDim).rgb;
|
||||
vColor = readFromTexture(tColor, group, uColorTexDim);
|
||||
#elif defined(dColorType_groupInstance)
|
||||
vColor = readFromTexture(tColor, aInstance * float(uGroupCount) + group, uColorTexDim).rgb;
|
||||
vColor = readFromTexture(tColor, aInstance * float(uGroupCount) + group, uColorTexDim);
|
||||
#endif
|
||||
}
|
||||
`;
|
||||
@@ -9,12 +9,14 @@ precision highp float;
|
||||
precision highp sampler2D;
|
||||
|
||||
uniform sampler2D tColor;
|
||||
uniform sampler2D tCount;
|
||||
uniform vec2 uTexSize;
|
||||
|
||||
void main(void) {
|
||||
vec2 coords = gl_FragCoord.xy / uTexSize;
|
||||
vec4 color = texture2D(tColor, coords);
|
||||
float count = texture2D(tCount, coords).r;
|
||||
|
||||
gl_FragColor.rgb = color.rgb / color.a;
|
||||
gl_FragColor = color / count;
|
||||
}
|
||||
`;
|
||||
@@ -8,6 +8,8 @@ export const cylinders_frag = `
|
||||
precision highp float;
|
||||
precision highp int;
|
||||
|
||||
#define bumpEnabled
|
||||
|
||||
uniform mat4 uView;
|
||||
|
||||
varying mat4 vTransform;
|
||||
@@ -18,6 +20,7 @@ varying float vCap;
|
||||
|
||||
uniform vec3 uCameraDir;
|
||||
uniform vec3 uCameraPosition;
|
||||
uniform mat4 uInvView;
|
||||
|
||||
#include common
|
||||
#include common_frag_params
|
||||
@@ -108,7 +111,8 @@ void main() {
|
||||
vViewPosition = (uView * vec4(vViewPosition, 1.0)).xyz;
|
||||
gl_FragDepthEXT = calcDepth(vViewPosition);
|
||||
|
||||
// bugfix (mac only?)
|
||||
vec3 vModelPosition = (uInvView * vec4(vViewPosition, 1.0)).xyz;
|
||||
|
||||
if (gl_FragDepthEXT < 0.0) discard;
|
||||
if (gl_FragDepthEXT > 1.0) discard;
|
||||
|
||||
|
||||
@@ -64,6 +64,9 @@ uniform int uMarkerPriority;
|
||||
uniform sampler2D tMarker;
|
||||
#endif
|
||||
|
||||
uniform float uMetalness;
|
||||
uniform float uRoughness;
|
||||
|
||||
uniform float uFogNear;
|
||||
uniform float uFogFar;
|
||||
uniform vec3 uFogColor;
|
||||
@@ -110,9 +113,17 @@ uniform mat4 uCartnToUnit;
|
||||
#endif
|
||||
|
||||
#ifdef dOverpaint
|
||||
varying vec4 vOverpaint;
|
||||
uniform vec2 uOverpaintTexDim;
|
||||
uniform sampler2D tOverpaint;
|
||||
#if defined(dOverpaintType_groupInstance) || defined(dOverpaintType_vertexInstance)
|
||||
uniform vec2 uOverpaintTexDim;
|
||||
uniform sampler2D tOverpaint;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef dSubstance
|
||||
#if defined(dSubstanceType_groupInstance) || defined(dSubstanceType_vertexInstance)
|
||||
uniform vec2 uSubstanceTexDim;
|
||||
uniform sampler2D tSubstance;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -192,6 +203,11 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
|
||||
float nextValue;
|
||||
|
||||
vec3 color = vec3(0.45, 0.55, 0.8);
|
||||
vec4 overpaint = vec4(0.0);
|
||||
vec3 substance = vec3(0.0);
|
||||
float metalness = uMetalness;
|
||||
float roughness = uRoughness;
|
||||
|
||||
vec3 gradient = vec3(1.0);
|
||||
vec3 dx = vec3(gradOffset * scaleVol.x, 0.0, 0.0);
|
||||
vec3 dy = vec3(0.0, gradOffset * scaleVol.y, 0.0);
|
||||
@@ -297,6 +313,16 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
|
||||
color = texture3dFrom1dTrilinear(tColor, isoPos, uGridDim, uColorTexDim, vInstance * float(uVertexCount)).rgb;
|
||||
#endif
|
||||
|
||||
#ifdef dOverpaint
|
||||
#if defined(dOverpaintType_groupInstance)
|
||||
overpaint = readFromTexture(tOverpaint, vInstance * float(uGroupCount) + group, uOverpaintTexDim);
|
||||
#elif defined(dOverpaintType_vertexInstance)
|
||||
overpaint = texture3dFrom1dTrilinear(tOverpaint, isoPos, uGridDim, uOverpaintTexDim, vInstance * float(uVertexCount));
|
||||
#endif
|
||||
|
||||
color = mix(color, overpaint.rgb, overpaint.a);
|
||||
#endif
|
||||
|
||||
// handle flipping and negative isosurfaces
|
||||
#ifdef dFlipSided
|
||||
bool flipped = value < uIsoValue.y; // flipped
|
||||
@@ -332,6 +358,15 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
|
||||
vec3 normal = -normalize(normalMatrix * normalize(gradient));
|
||||
normal = normal * (float(flipped) * 2.0 - 1.0);
|
||||
normal = normal * -(float(interior) * 2.0 - 1.0);
|
||||
#ifdef dSubstance
|
||||
#if defined(dSubstanceType_groupInstance)
|
||||
substance = readFromTexture(tSubstance, vInstance * float(uGroupCount) + group, uSubstanceTexDim).rgb;
|
||||
#elif defined(dSubstanceType_vertexInstance)
|
||||
substance = texture3dFrom1dTrilinear(tSubstance, isoPos, uGridDim, uSubstanceTexDim, vInstance * float(uVertexCount)).rgb;
|
||||
#endif
|
||||
metalness = mix(metalness, substance.r, substance.b);
|
||||
roughness = mix(roughness, substance.g, substance.b);
|
||||
#endif
|
||||
#include apply_light_color
|
||||
#endif
|
||||
|
||||
|
||||
@@ -105,6 +105,7 @@ void main(){
|
||||
#else
|
||||
linewidth = size * uPixelRatio;
|
||||
#endif
|
||||
linewidth = max(1.0, linewidth);
|
||||
|
||||
// adjust for linewidth
|
||||
offset *= linewidth;
|
||||
|
||||
@@ -8,6 +8,8 @@ export const mesh_frag = `
|
||||
precision highp float;
|
||||
precision highp int;
|
||||
|
||||
#define bumpEnabled
|
||||
|
||||
#include common
|
||||
#include common_frag_params
|
||||
#include color_frag_params
|
||||
@@ -19,14 +21,10 @@ void main() {
|
||||
#include clip_pixel
|
||||
|
||||
// Workaround for buggy gl_FrontFacing (e.g. on some integrated Intel GPUs)
|
||||
#if defined(enabledStandardDerivatives)
|
||||
vec3 fdx = dFdx(vViewPosition);
|
||||
vec3 fdy = dFdy(vViewPosition);
|
||||
vec3 faceNormal = normalize(cross(fdx,fdy));
|
||||
bool frontFacing = dot(vNormal, faceNormal) > 0.0;
|
||||
#else
|
||||
bool frontFacing = dot(vNormal, vViewPosition) < 0.0;
|
||||
#endif
|
||||
vec3 fdx = dFdx(vViewPosition);
|
||||
vec3 fdy = dFdy(vViewPosition);
|
||||
vec3 faceNormal = normalize(cross(fdx,fdy));
|
||||
bool frontFacing = dot(vNormal, faceNormal) > 0.0;
|
||||
|
||||
#if defined(dFlipSided)
|
||||
interior = frontFacing;
|
||||
@@ -48,7 +46,7 @@ void main() {
|
||||
#ifdef dIgnoreLight
|
||||
gl_FragColor = material;
|
||||
#else
|
||||
#if defined(dFlatShaded) && defined(enabledStandardDerivatives)
|
||||
#if defined(dFlatShaded)
|
||||
vec3 normal = -faceNormal;
|
||||
#else
|
||||
vec3 normal = -normalize(vNormal);
|
||||
|
||||
@@ -14,10 +14,7 @@ precision highp sampler2D;
|
||||
#include common_vert_params
|
||||
#include color_vert_params
|
||||
#include common_clip
|
||||
|
||||
#if defined(dColorType_grid)
|
||||
#include texture3d_from_2d_linear
|
||||
#endif
|
||||
#include texture3d_from_2d_linear
|
||||
|
||||
#ifdef dGeoTexture
|
||||
uniform vec2 uGeoTexDim;
|
||||
|
||||
@@ -36,6 +36,7 @@ void main(){
|
||||
#else
|
||||
gl_PointSize = size * uPixelRatio;
|
||||
#endif
|
||||
gl_PointSize = max(1.0, gl_PointSize);
|
||||
|
||||
gl_Position = uProjection * mvPosition;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user