mirror of
https://github.com/molstar/molstar.git
synced 2026-06-05 22:31:26 +08:00
Compare commits
42 Commits
v2.4.1
...
v3.0.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d0f0ceebf | ||
|
|
14abcddfcf | ||
|
|
704cc96a9f | ||
|
|
b51f610173 | ||
|
|
470280ea1a | ||
|
|
dafb5a8299 | ||
|
|
4a1af03744 | ||
|
|
bb176f1efb | ||
|
|
a53bcde973 | ||
|
|
1a8dc2c637 | ||
|
|
f96211ff91 | ||
|
|
77f9c02785 | ||
|
|
7910b65fdc | ||
|
|
eb4fc4588d | ||
|
|
5430674071 | ||
|
|
17e67e3b79 | ||
|
|
e87a0d72e4 | ||
|
|
67d3c65907 | ||
|
|
564a5486c9 | ||
|
|
9ce11c4c32 | ||
|
|
5e97b551a5 | ||
|
|
77536e75af | ||
|
|
6f12f714d2 | ||
|
|
1f67077400 | ||
|
|
d1c4cf69cb | ||
|
|
803c5eaa15 | ||
|
|
970fd5d9c3 | ||
|
|
3a6ab55266 | ||
|
|
6318717a15 | ||
|
|
aaec452bc2 | ||
|
|
bce959195a | ||
|
|
4287e09a9a | ||
|
|
fdc006f833 | ||
|
|
c704b7505c | ||
|
|
ceaf238322 | ||
|
|
b7224ce5c7 | ||
|
|
3104ee5742 | ||
|
|
1e4d1e45f9 | ||
|
|
b5ccdfdd53 | ||
|
|
d0c0d8e703 | ||
|
|
ebf64404be | ||
|
|
e5dcc8e54f |
@@ -1 +1,5 @@
|
||||
lib/tsconfig.commonjs.tsbuildinfo
|
||||
tests
|
||||
perf-tests
|
||||
_spec
|
||||
*.tsbuildinfo
|
||||
*.js.map
|
||||
17
CHANGELOG.md
17
CHANGELOG.md
@@ -6,6 +6,23 @@ Note that since we don't clearly distinguish between a public and private interf
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [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
|
||||
|
||||
11
README.md
11
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
|
||||
|
||||
|
||||
144
package-lock.json
generated
144
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "2.4.1",
|
||||
"version": "3.0.0-dev.3",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "molstar",
|
||||
"version": "2.4.1",
|
||||
"version": "3.0.0-dev.3",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/argparse": "^2.0.10",
|
||||
@@ -56,8 +56,8 @@
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/gl": "^4.1.0",
|
||||
"@types/jest": "^27.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.5.0",
|
||||
"@typescript-eslint/parser": "^5.5.0",
|
||||
"benchmark": "^2.1.4",
|
||||
"concurrently": "^6.4.0",
|
||||
"cpx2": "^4.0.0",
|
||||
@@ -2885,13 +2885,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.4.0.tgz",
|
||||
"integrity": "sha512-9/yPSBlwzsetCsGEn9j24D8vGQgJkOTr4oMLas/w886ZtzKIs1iyoqFrwsX2fqYEeUwsdBpC21gcjRGo57u0eg==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.5.0.tgz",
|
||||
"integrity": "sha512-4bV6fulqbuaO9UMXU0Ia0o6z6if+kmMRW8rMRyfqXj/eGrZZRGedS4n0adeGNnjr8LKAM495hrQ7Tea52UWmQA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/experimental-utils": "5.4.0",
|
||||
"@typescript-eslint/scope-manager": "5.4.0",
|
||||
"@typescript-eslint/experimental-utils": "5.5.0",
|
||||
"@typescript-eslint/scope-manager": "5.5.0",
|
||||
"debug": "^4.3.2",
|
||||
"functional-red-black-tree": "^1.0.1",
|
||||
"ignore": "^5.1.8",
|
||||
@@ -2917,15 +2917,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/experimental-utils": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.4.0.tgz",
|
||||
"integrity": "sha512-Nz2JDIQUdmIGd6p33A+naQmwfkU5KVTLb/5lTk+tLVTDacZKoGQisj8UCxk7onJcrgjIvr8xWqkYI+DbI3TfXg==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.5.0.tgz",
|
||||
"integrity": "sha512-kjWeeVU+4lQ1SLYErRKV5yDXbWDPkpbzTUUlfAUifPYvpX0qZlrcCZ96/6oWxt3QxtK5WVhXz+KsnwW9cIW+3A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"@typescript-eslint/scope-manager": "5.4.0",
|
||||
"@typescript-eslint/types": "5.4.0",
|
||||
"@typescript-eslint/typescript-estree": "5.4.0",
|
||||
"@typescript-eslint/scope-manager": "5.5.0",
|
||||
"@typescript-eslint/types": "5.5.0",
|
||||
"@typescript-eslint/typescript-estree": "5.5.0",
|
||||
"eslint-scope": "^5.1.1",
|
||||
"eslint-utils": "^3.0.0"
|
||||
},
|
||||
@@ -2941,14 +2941,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.4.0.tgz",
|
||||
"integrity": "sha512-JoB41EmxiYpaEsRwpZEYAJ9XQURPFer8hpkIW9GiaspVLX8oqbqNM8P4EP8HOZg96yaALiLEVWllA2E8vwsIKw==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.5.0.tgz",
|
||||
"integrity": "sha512-JsXBU+kgQOAgzUn2jPrLA+Rd0Y1dswOlX3hp8MuRO1hQDs6xgHtbCXEiAu7bz5hyVURxbXcA2draasMbNqrhmg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "5.4.0",
|
||||
"@typescript-eslint/types": "5.4.0",
|
||||
"@typescript-eslint/typescript-estree": "5.4.0",
|
||||
"@typescript-eslint/scope-manager": "5.5.0",
|
||||
"@typescript-eslint/types": "5.5.0",
|
||||
"@typescript-eslint/typescript-estree": "5.5.0",
|
||||
"debug": "^4.3.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2968,13 +2968,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.4.0.tgz",
|
||||
"integrity": "sha512-pRxFjYwoi8R+n+sibjgF9iUiAELU9ihPBtHzocyW8v8D8G8KeQvXTsW7+CBYIyTYsmhtNk50QPGLE3vrvhM5KA==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.5.0.tgz",
|
||||
"integrity": "sha512-0/r656RmRLo7CbN4Mdd+xZyPJ/fPCKhYdU6mnZx+8msAD8nJSP8EyCFkzbd6vNVZzZvWlMYrSNekqGrCBqFQhg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.4.0",
|
||||
"@typescript-eslint/visitor-keys": "5.4.0"
|
||||
"@typescript-eslint/types": "5.5.0",
|
||||
"@typescript-eslint/visitor-keys": "5.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
@@ -2985,9 +2985,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.4.0.tgz",
|
||||
"integrity": "sha512-GjXNpmn+n1LvnttarX+sPD6+S7giO+9LxDIGlRl4wK3a7qMWALOHYuVSZpPTfEIklYjaWuMtfKdeByx0AcaThA==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.5.0.tgz",
|
||||
"integrity": "sha512-OaYTqkW3GnuHxqsxxJ6KypIKd5Uw7bFiQJZRyNi1jbMJnK3Hc/DR4KwB6KJj6PBRkJJoaNwzMNv9vtTk87JhOg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
@@ -2998,13 +2998,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.4.0.tgz",
|
||||
"integrity": "sha512-nhlNoBdhKuwiLMx6GrybPT3SFILm5Gij2YBdPEPFlYNFAXUJWX6QRgvi/lwVoadaQEFsizohs6aFRMqsXI2ewA==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.5.0.tgz",
|
||||
"integrity": "sha512-pVn8btYUiYrjonhMAO0yG8lm7RApzy2L4RC7Td/mC/qFkyf6vRbGyZozoA94+w6D2Y2GRqpMoCWcwx/EUOzyoQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.4.0",
|
||||
"@typescript-eslint/visitor-keys": "5.4.0",
|
||||
"@typescript-eslint/types": "5.5.0",
|
||||
"@typescript-eslint/visitor-keys": "5.5.0",
|
||||
"debug": "^4.3.2",
|
||||
"globby": "^11.0.4",
|
||||
"is-glob": "^4.0.3",
|
||||
@@ -3025,12 +3025,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.4.0.tgz",
|
||||
"integrity": "sha512-PVbax7MeE7tdLfW5SA0fs8NGVVr+buMPrcj+CWYWPXsZCH8qZ1THufDzbXm1xrZ2b2PA1iENJ0sRq5fuUtvsJg==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.5.0.tgz",
|
||||
"integrity": "sha512-4GzJ1kRtsWzHhdM40tv0ZKHNSbkDhF0Woi/TDwVJX6UICwJItvP7ZTXbjTkCdrors7ww0sYe0t+cIKDAJwZ7Kw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.4.0",
|
||||
"@typescript-eslint/types": "5.5.0",
|
||||
"eslint-visitor-keys": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -16064,13 +16064,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.4.0.tgz",
|
||||
"integrity": "sha512-9/yPSBlwzsetCsGEn9j24D8vGQgJkOTr4oMLas/w886ZtzKIs1iyoqFrwsX2fqYEeUwsdBpC21gcjRGo57u0eg==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.5.0.tgz",
|
||||
"integrity": "sha512-4bV6fulqbuaO9UMXU0Ia0o6z6if+kmMRW8rMRyfqXj/eGrZZRGedS4n0adeGNnjr8LKAM495hrQ7Tea52UWmQA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/experimental-utils": "5.4.0",
|
||||
"@typescript-eslint/scope-manager": "5.4.0",
|
||||
"@typescript-eslint/experimental-utils": "5.5.0",
|
||||
"@typescript-eslint/scope-manager": "5.5.0",
|
||||
"debug": "^4.3.2",
|
||||
"functional-red-black-tree": "^1.0.1",
|
||||
"ignore": "^5.1.8",
|
||||
@@ -16080,55 +16080,55 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/experimental-utils": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.4.0.tgz",
|
||||
"integrity": "sha512-Nz2JDIQUdmIGd6p33A+naQmwfkU5KVTLb/5lTk+tLVTDacZKoGQisj8UCxk7onJcrgjIvr8xWqkYI+DbI3TfXg==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.5.0.tgz",
|
||||
"integrity": "sha512-kjWeeVU+4lQ1SLYErRKV5yDXbWDPkpbzTUUlfAUifPYvpX0qZlrcCZ96/6oWxt3QxtK5WVhXz+KsnwW9cIW+3A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"@typescript-eslint/scope-manager": "5.4.0",
|
||||
"@typescript-eslint/types": "5.4.0",
|
||||
"@typescript-eslint/typescript-estree": "5.4.0",
|
||||
"@typescript-eslint/scope-manager": "5.5.0",
|
||||
"@typescript-eslint/types": "5.5.0",
|
||||
"@typescript-eslint/typescript-estree": "5.5.0",
|
||||
"eslint-scope": "^5.1.1",
|
||||
"eslint-utils": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.4.0.tgz",
|
||||
"integrity": "sha512-JoB41EmxiYpaEsRwpZEYAJ9XQURPFer8hpkIW9GiaspVLX8oqbqNM8P4EP8HOZg96yaALiLEVWllA2E8vwsIKw==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.5.0.tgz",
|
||||
"integrity": "sha512-JsXBU+kgQOAgzUn2jPrLA+Rd0Y1dswOlX3hp8MuRO1hQDs6xgHtbCXEiAu7bz5hyVURxbXcA2draasMbNqrhmg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/scope-manager": "5.4.0",
|
||||
"@typescript-eslint/types": "5.4.0",
|
||||
"@typescript-eslint/typescript-estree": "5.4.0",
|
||||
"@typescript-eslint/scope-manager": "5.5.0",
|
||||
"@typescript-eslint/types": "5.5.0",
|
||||
"@typescript-eslint/typescript-estree": "5.5.0",
|
||||
"debug": "^4.3.2"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/scope-manager": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.4.0.tgz",
|
||||
"integrity": "sha512-pRxFjYwoi8R+n+sibjgF9iUiAELU9ihPBtHzocyW8v8D8G8KeQvXTsW7+CBYIyTYsmhtNk50QPGLE3vrvhM5KA==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.5.0.tgz",
|
||||
"integrity": "sha512-0/r656RmRLo7CbN4Mdd+xZyPJ/fPCKhYdU6mnZx+8msAD8nJSP8EyCFkzbd6vNVZzZvWlMYrSNekqGrCBqFQhg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.4.0",
|
||||
"@typescript-eslint/visitor-keys": "5.4.0"
|
||||
"@typescript-eslint/types": "5.5.0",
|
||||
"@typescript-eslint/visitor-keys": "5.5.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/types": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.4.0.tgz",
|
||||
"integrity": "sha512-GjXNpmn+n1LvnttarX+sPD6+S7giO+9LxDIGlRl4wK3a7qMWALOHYuVSZpPTfEIklYjaWuMtfKdeByx0AcaThA==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.5.0.tgz",
|
||||
"integrity": "sha512-OaYTqkW3GnuHxqsxxJ6KypIKd5Uw7bFiQJZRyNi1jbMJnK3Hc/DR4KwB6KJj6PBRkJJoaNwzMNv9vtTk87JhOg==",
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/typescript-estree": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.4.0.tgz",
|
||||
"integrity": "sha512-nhlNoBdhKuwiLMx6GrybPT3SFILm5Gij2YBdPEPFlYNFAXUJWX6QRgvi/lwVoadaQEFsizohs6aFRMqsXI2ewA==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.5.0.tgz",
|
||||
"integrity": "sha512-pVn8btYUiYrjonhMAO0yG8lm7RApzy2L4RC7Td/mC/qFkyf6vRbGyZozoA94+w6D2Y2GRqpMoCWcwx/EUOzyoQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.4.0",
|
||||
"@typescript-eslint/visitor-keys": "5.4.0",
|
||||
"@typescript-eslint/types": "5.5.0",
|
||||
"@typescript-eslint/visitor-keys": "5.5.0",
|
||||
"debug": "^4.3.2",
|
||||
"globby": "^11.0.4",
|
||||
"is-glob": "^4.0.3",
|
||||
@@ -16137,12 +16137,12 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/visitor-keys": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.4.0.tgz",
|
||||
"integrity": "sha512-PVbax7MeE7tdLfW5SA0fs8NGVVr+buMPrcj+CWYWPXsZCH8qZ1THufDzbXm1xrZ2b2PA1iENJ0sRq5fuUtvsJg==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.5.0.tgz",
|
||||
"integrity": "sha512-4GzJ1kRtsWzHhdM40tv0ZKHNSbkDhF0Woi/TDwVJX6UICwJItvP7ZTXbjTkCdrors7ww0sYe0t+cIKDAJwZ7Kw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.4.0",
|
||||
"@typescript-eslint/types": "5.5.0",
|
||||
"eslint-visitor-keys": "^3.0.0"
|
||||
}
|
||||
},
|
||||
|
||||
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "2.4.1",
|
||||
"version": "3.0.0-dev.3",
|
||||
"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": [
|
||||
@@ -98,8 +100,8 @@
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/gl": "^4.1.0",
|
||||
"@types/jest": "^27.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.5.0",
|
||||
"@typescript-eslint/parser": "^5.5.0",
|
||||
"benchmark": "^2.1.4",
|
||||
"concurrently": "^6.4.0",
|
||||
"cpx2": "^4.0.0",
|
||||
|
||||
19
scripts/clean.js
Normal file
19
scripts/clean.js
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 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');
|
||||
|
||||
const toClean = [
|
||||
path.resolve(__dirname, '../build'),
|
||||
path.resolve(__dirname, '../lib'),
|
||||
path.resolve(__dirname, '../tsconfig.tsbuildinfo'),
|
||||
];
|
||||
|
||||
toClean.forEach(ph => {
|
||||
if (fs.existsSync(ph))
|
||||
fs.rm(ph, { recursive: true }, err => { if (err) console.warn(`Failed to delete ${ph}: ${err}`); });
|
||||
});
|
||||
@@ -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,
|
||||
@@ -77,7 +76,7 @@ const PresetParams = {
|
||||
...StructureRepresentationPresetProvider.CommonParams,
|
||||
};
|
||||
|
||||
|
||||
const CustomMaterial = Material({ roughness: 0.2, metalness: 0 });
|
||||
|
||||
export const StructurePreset = StructureRepresentationPresetProvider({
|
||||
id: 'preset-structure',
|
||||
@@ -94,8 +93,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 +120,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 +148,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 +176,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 +205,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 });
|
||||
|
||||
@@ -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.');
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
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';
|
||||
@@ -44,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[] = [];
|
||||
@@ -157,6 +158,21 @@ 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;
|
||||
|
||||
@@ -168,6 +184,10 @@ export class GlbExporter extends MeshExporter<GlbData> {
|
||||
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') {
|
||||
@@ -228,7 +248,7 @@ export class GlbExporter extends MeshExporter<GlbData> {
|
||||
COLOR_0: colorAccessorIndex!
|
||||
},
|
||||
indices: indexAccessorIndex,
|
||||
material: 0
|
||||
material
|
||||
}]
|
||||
});
|
||||
}
|
||||
@@ -262,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] => {
|
||||
@@ -317,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);
|
||||
|
||||
@@ -145,7 +145,7 @@ export class ObjExporter extends MeshExporter<ObjData> {
|
||||
const color = ObjExporter.getColor(v, geoData, interpolatedColors, interpolatedOverpaint);
|
||||
Color.toArray(color, quantizedColors, i);
|
||||
}
|
||||
ObjExporter.quantizeColors(quantizedColors, mesh!.vertexCount);
|
||||
ObjExporter.quantizeColors(quantizedColors, vertexCount);
|
||||
|
||||
// face
|
||||
for (let i = 0; i < drawCount; i += 3) {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* @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';
|
||||
@@ -32,17 +31,16 @@ 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)!;
|
||||
|
||||
const materialKey = this.materialMap.size;
|
||||
this.materialMap.set(hash, materialKey);
|
||||
|
||||
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).map(v => Math.round(v * 1000) / 1000);
|
||||
this.materials.push(`
|
||||
def Material "material${materialKey}"
|
||||
@@ -53,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) {
|
||||
@@ -75,6 +74,8 @@ def Material "material${materialKey}"
|
||||
const uAlpha = values.uAlpha.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') {
|
||||
@@ -148,7 +149,7 @@ def Material "material${materialKey}"
|
||||
const color = UsdzExporter.getColor(v, geoData, interpolatedColors, interpolatedOverpaint);
|
||||
Color.toArray(color, quantizedColors, i);
|
||||
}
|
||||
UsdzExporter.quantizeColors(quantizedColors, mesh!.vertexCount);
|
||||
UsdzExporter.quantizeColors(quantizedColors, vertexCount);
|
||||
|
||||
// material
|
||||
const faceIndicesByMaterial = new Map<number, number[]>();
|
||||
@@ -158,9 +159,7 @@ def Material "material${materialKey}"
|
||||
const transparency = UsdzExporter.getTransparency(i, geoData, interpolatedTransparency);
|
||||
const alpha = Math.round(uAlpha * (1 - transparency) * 10) / 10; // quantized
|
||||
|
||||
this.addMaterial(color, alpha);
|
||||
|
||||
const materialKey = UsdzExporter.getMaterialKey(color, alpha);
|
||||
const materialKey = this.addMaterial(color, alpha, metalness, roughness);
|
||||
let faceIndices = faceIndicesByMaterial.get(materialKey);
|
||||
if (faceIndices === undefined) {
|
||||
faceIndices = [];
|
||||
@@ -232,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
|
||||
|
||||
@@ -16,6 +16,7 @@ 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': {},
|
||||
@@ -69,18 +70,20 @@ export function getColorSmoothingProps(smoothColors: PD.Values<ColorSmoothingPar
|
||||
//
|
||||
|
||||
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 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) {
|
||||
@@ -100,11 +103,16 @@ 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),
|
||||
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);
|
||||
}
|
||||
|
||||
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',
|
||||
@@ -200,6 +201,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 +226,7 @@ export namespace Cylinders {
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...material,
|
||||
...clipping,
|
||||
...transform,
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -246,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;
|
||||
@@ -270,6 +272,7 @@ export namespace DirectVolume {
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...material,
|
||||
...clipping,
|
||||
...transform,
|
||||
...BaseGeometry.createValues(props, counts),
|
||||
@@ -319,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);
|
||||
|
||||
@@ -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,
|
||||
@@ -145,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 };
|
||||
@@ -157,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 {
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -356,3 +356,37 @@ export function applyMeshTransparencySmoothing(values: MeshValues, resolution: n
|
||||
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: 3
|
||||
}, 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',
|
||||
@@ -665,6 +666,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 +686,7 @@ export namespace Mesh {
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...material,
|
||||
...clipping,
|
||||
...transform,
|
||||
|
||||
|
||||
@@ -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 {
|
||||
@@ -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',
|
||||
@@ -170,6 +171,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 +193,7 @@ export namespace Spheres {
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...material,
|
||||
...clipping,
|
||||
...transform,
|
||||
|
||||
|
||||
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 * 3);
|
||||
array[i * 3 + 2] = 255;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function clearSubstance(array: Uint8Array, start: number, end: number) {
|
||||
array.fill(0, start * 3, end * 3);
|
||||
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,
|
||||
|
||||
|
||||
@@ -500,3 +500,39 @@ export function applyTextureMeshTransparencySmoothing(values: TextureMeshValues,
|
||||
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[ColorSmoothingRgbName]) {
|
||||
webgl.namedTextures[ColorSmoothingRgbName] = webgl.resources.texture('image-uint8', 'rgb', 'ubyte', 'nearest');
|
||||
}
|
||||
const colorData = webgl.namedTextures[ColorSmoothingRgbName];
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ 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',
|
||||
@@ -135,6 +136,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 };
|
||||
@@ -156,6 +158,7 @@ export namespace TextureMesh {
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...substance,
|
||||
...clipping,
|
||||
...transform,
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ 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.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);
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -247,6 +241,19 @@ export const TransparencySchema = {
|
||||
export type TransparencySchema = typeof TransparencySchema
|
||||
export type TransparencyValues = Values<TransparencySchema>
|
||||
|
||||
export const SubstanceSchema = {
|
||||
uSubstanceTexDim: UniformSpec('v2'),
|
||||
tSubstance: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'),
|
||||
dSubstance: DefineSpec('boolean'),
|
||||
|
||||
uSubstanceGridDim: UniformSpec('v3'),
|
||||
uSubstanceGridTransform: UniformSpec('v4'),
|
||||
tSubstanceGrid: TextureSpec('texture', 'rgb', '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']),
|
||||
@@ -263,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
|
||||
@@ -276,6 +286,9 @@ export const BaseSchema = {
|
||||
* final alpha, calculated as `values.alpha * state.alpha`
|
||||
*/
|
||||
uAlpha: UniformSpec('f', 'material'),
|
||||
uMetalness: UniformSpec('f', 'material'),
|
||||
uRoughness: UniformSpec('f', 'material'),
|
||||
|
||||
uVertexCount: UniformSpec('i'),
|
||||
uInstanceCount: UniformSpec('i'),
|
||||
uGroupCount: UniformSpec('i'),
|
||||
|
||||
@@ -70,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 }),
|
||||
@@ -85,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'])),
|
||||
@@ -116,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 = {
|
||||
@@ -195,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();
|
||||
@@ -220,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),
|
||||
@@ -257,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),
|
||||
|
||||
@@ -304,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);
|
||||
@@ -686,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)) {
|
||||
|
||||
@@ -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,10 +1,10 @@
|
||||
/**
|
||||
* 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 = `
|
||||
@@ -14,21 +14,21 @@ export const apply_light_color = `
|
||||
// - vec3 normal
|
||||
// - float uMetalness
|
||||
// - float uRoughness
|
||||
// - float uReflectivity
|
||||
// - float uLightIntensity
|
||||
// - float uAmbientIntensity
|
||||
// - vec3 uLightColor
|
||||
// - vec3 uAmbientColor
|
||||
|
||||
// outputs
|
||||
// - sets gl_FragColor
|
||||
|
||||
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);
|
||||
physicalMaterial.roughness = max(roughness, 0.0525);
|
||||
physicalMaterial.specularColor = mix(vec3(0.04), color.rgb, metalness);
|
||||
physicalMaterial.specularF90 = 1.0;
|
||||
|
||||
GeometricContext geometry;
|
||||
geometry.position = -vViewPosition;
|
||||
@@ -36,19 +36,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
|
||||
`;
|
||||
@@ -41,6 +41,20 @@ export const assign_color_varying = `
|
||||
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).rgb;
|
||||
#elif defined(dSubstanceType_vertexInstance)
|
||||
vSubstance = readFromTexture(tSubstance, int(aInstance) * uVertexCount + VertexID, uSubstanceTexDim).rgb;
|
||||
#elif defined(dSubstanceType_volumeInstance)
|
||||
vec3 sgridPos = (uSubstanceGridTransform.w * (vModelPosition - uSubstanceGridTransform.xyz)) / uSubstanceGridDim;
|
||||
vSubstance = texture3dFrom2dLinear(tSubstanceGrid, sgridPos, uSubstanceGridDim, uSubstanceTexDim).rgb;
|
||||
#endif
|
||||
|
||||
// pre-mix to avoid artifacts due to empty substance
|
||||
vSubstance.rg = mix(vec2(uMetalness, uRoughness), vSubstance.rg, vSubstance.b);
|
||||
#endif
|
||||
#elif defined(dRenderVariant_pick)
|
||||
#if defined(dRenderVariant_pickObject)
|
||||
vColor = vec4(encodeFloatRGB(float(uObjectId)), 1.0);
|
||||
|
||||
@@ -20,6 +20,13 @@ export const assign_material_color = `
|
||||
#if defined(dOverpaint)
|
||||
material.rgb = mix(material.rgb, vOverpaint.rgb, vOverpaint.a);
|
||||
#endif
|
||||
|
||||
float metalness = uMetalness;
|
||||
float roughness = uRoughness;
|
||||
#ifdef dSubstance
|
||||
metalness = mix(metalness, vSubstance.r, vSubstance.b);
|
||||
roughness = mix(roughness, vSubstance.g, vSubstance.b);
|
||||
#endif
|
||||
#elif defined(dRenderVariant_pick)
|
||||
vec4 material = vColor;
|
||||
#elif defined(dRenderVariant_depth)
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
export const color_frag_params = `
|
||||
uniform float uMetalness;
|
||||
uniform float uRoughness;
|
||||
|
||||
#if defined(dRenderVariant_color)
|
||||
#if defined(dColorType_uniform)
|
||||
uniform vec3 uColor;
|
||||
@@ -9,6 +12,10 @@ export const color_frag_params = `
|
||||
#ifdef dOverpaint
|
||||
varying vec4 vOverpaint;
|
||||
#endif
|
||||
|
||||
#ifdef dSubstance
|
||||
varying vec3 vSubstance;
|
||||
#endif
|
||||
#elif defined(dRenderVariant_pick)
|
||||
#if __VERSION__ == 100
|
||||
varying vec4 vColor;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
export const color_vert_params = `
|
||||
uniform float uMetalness;
|
||||
uniform float uRoughness;
|
||||
|
||||
#if defined(dRenderVariant_color)
|
||||
#if defined(dColorType_uniform)
|
||||
uniform vec3 uColor;
|
||||
@@ -30,6 +33,20 @@ export const color_vert_params = `
|
||||
uniform sampler2D tOverpaintGrid;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef dSubstance
|
||||
#if defined(dSubstanceType_groupInstance) || defined(dSubstanceType_vertexInstance)
|
||||
varying vec3 vSubstance;
|
||||
uniform vec2 uSubstanceTexDim;
|
||||
uniform sampler2D tSubstance;
|
||||
#elif defined(dSubstanceType_volumeInstance)
|
||||
varying vec3 vSubstance;
|
||||
uniform vec2 uSubstanceTexDim;
|
||||
uniform vec3 uSubstanceGridDim;
|
||||
uniform vec4 uSubstanceGridTransform;
|
||||
uniform sampler2D tSubstanceGrid;
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(dRenderVariant_pick)
|
||||
#if __VERSION__ == 100
|
||||
varying vec4 vColor;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
/**
|
||||
* 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;
|
||||
uniform vec3 uLightDirection[dLightCount];
|
||||
uniform vec3 uLightColor[dLightCount];
|
||||
uniform vec3 uAmbientColor;
|
||||
|
||||
struct PhysicalMaterial {
|
||||
vec3 diffuseColor;
|
||||
float specularRoughness;
|
||||
float roughness;
|
||||
vec3 specularColor;
|
||||
float specularF90;
|
||||
};
|
||||
|
||||
struct IncidentLight {
|
||||
@@ -29,6 +28,7 @@ struct ReflectedLight {
|
||||
vec3 directDiffuse;
|
||||
vec3 directSpecular;
|
||||
vec3 indirectDiffuse;
|
||||
vec3 indirectSpecular;
|
||||
};
|
||||
|
||||
struct GeometricContext {
|
||||
@@ -37,20 +37,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 +68,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;
|
||||
}
|
||||
`;
|
||||
@@ -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;
|
||||
@@ -115,6 +118,13 @@ uniform mat4 uCartnToUnit;
|
||||
uniform sampler2D tOverpaint;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef dSubstance
|
||||
#if defined(dSubstanceType_groupInstance) || defined(dSubstanceType_vertexInstance)
|
||||
uniform vec2 uSubstanceTexDim;
|
||||
uniform sampler2D tSubstance;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(dGridTexType_2d)
|
||||
@@ -194,6 +204,9 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
|
||||
|
||||
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);
|
||||
@@ -304,7 +317,7 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
|
||||
#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)).rgb;
|
||||
overpaint = texture3dFrom1dTrilinear(tOverpaint, isoPos, uGridDim, uOverpaintTexDim, vInstance * float(uVertexCount));
|
||||
#endif
|
||||
|
||||
color = mix(color, overpaint.rgb, overpaint.a);
|
||||
@@ -345,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
|
||||
|
||||
|
||||
@@ -19,14 +19,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 +44,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);
|
||||
|
||||
@@ -36,13 +36,11 @@ export function createExtensions(gl: GLRenderingContext): WebGLExtensions {
|
||||
if (elementIndexUint === null) {
|
||||
throw new Error('Could not find support for "element_index_uint"');
|
||||
}
|
||||
|
||||
const standardDerivatives = getStandardDerivatives(gl);
|
||||
if (isDebugMode && standardDerivatives === null) {
|
||||
// - non-support handled downstream (flat shading option is ignored)
|
||||
// - can't be a required extension because it is not supported by `headless-gl`
|
||||
console.log('Could not find support for "standard_derivatives"');
|
||||
if (standardDerivatives === null) {
|
||||
throw new Error('Could not find support for "standard_derivatives"');
|
||||
}
|
||||
|
||||
const textureFloat = getTextureFloat(gl);
|
||||
if (isDebugMode && textureFloat === null) {
|
||||
console.log('Could not find support for "texture_float"');
|
||||
|
||||
@@ -504,6 +504,19 @@ namespace Vec3 {
|
||||
return dot(tmp_dh_cb, tmp_dh_cross) > 0 ? _angle : -_angle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param inclination in radians [0, PI]
|
||||
* @param azimuth in radians [0, 2 * PI]
|
||||
* @param radius [0, +Inf]
|
||||
*/
|
||||
export function directionFromSpherical(out: Vec3, inclination: number, azimuth: number, radius: number): Vec3 {
|
||||
return Vec3.set(out,
|
||||
radius * Math.cos(azimuth) * Math.sin(inclination),
|
||||
radius * Math.sin(azimuth) * Math.sin(inclination),
|
||||
radius * Math.cos(inclination)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the vectors have exactly the same elements in the same position (when compared with ===)
|
||||
*/
|
||||
|
||||
76
src/mol-plugin-state/helpers/structure-substance.ts
Normal file
76
src/mol-plugin-state/helpers/structure-substance.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>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { Structure, StructureElement } from '../../mol-model/structure';
|
||||
import { PluginStateObject } from '../../mol-plugin-state/objects';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { StateBuilder, StateObjectCell, StateSelection, StateTransform } from '../../mol-state';
|
||||
import { Substance } from '../../mol-theme/substance';
|
||||
import { StructureComponentRef } from '../manager/structure/hierarchy-state';
|
||||
import { EmptyLoci, isEmptyLoci, Loci } from '../../mol-model/loci';
|
||||
import { Material } from '../../mol-util/material';
|
||||
|
||||
type SubstanceEachReprCallback = (update: StateBuilder.Root, repr: StateObjectCell<PluginStateObject.Molecule.Structure.Representation3D, StateTransform<typeof StateTransforms.Representation.StructureRepresentation3D>>, substance?: StateObjectCell<any, StateTransform<typeof StateTransforms.Representation.SubstanceStructureRepresentation3DFromBundle>>) => Promise<void>
|
||||
const SubstanceManagerTag = 'substance-controls';
|
||||
|
||||
export async function setStructureSubstance(plugin: PluginContext, components: StructureComponentRef[], material: Material | undefined, lociGetter: (structure: Structure) => Promise<StructureElement.Loci | EmptyLoci>, types?: string[]) {
|
||||
await eachRepr(plugin, components, async (update, repr, substanceCell) => {
|
||||
if (types && types.length > 0 && !types.includes(repr.params!.values.type.name)) return;
|
||||
|
||||
const structure = repr.obj!.data.sourceData;
|
||||
// always use the root structure to get the loci so the substance
|
||||
// stays applicable as long as the root structure does not change
|
||||
const loci = await lociGetter(structure.root);
|
||||
if (Loci.isEmpty(loci) || isEmptyLoci(loci)) return;
|
||||
|
||||
const layer = {
|
||||
bundle: StructureElement.Bundle.fromLoci(loci),
|
||||
material: material ?? Material(),
|
||||
clear: !material
|
||||
};
|
||||
|
||||
if (substanceCell) {
|
||||
const bundleLayers = [...substanceCell.params!.values.layers, layer];
|
||||
const filtered = getFilteredBundle(bundleLayers, structure);
|
||||
update.to(substanceCell).update(Substance.toBundle(filtered));
|
||||
} else {
|
||||
const filtered = getFilteredBundle([layer], structure);
|
||||
update.to(repr.transform.ref)
|
||||
.apply(StateTransforms.Representation.SubstanceStructureRepresentation3DFromBundle, Substance.toBundle(filtered), { tags: SubstanceManagerTag });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function clearStructureSubstance(plugin: PluginContext, components: StructureComponentRef[], types?: string[]) {
|
||||
await eachRepr(plugin, components, async (update, repr, substanceCell) => {
|
||||
if (types && types.length > 0 && !types.includes(repr.params!.values.type.name)) return;
|
||||
if (substanceCell) {
|
||||
update.delete(substanceCell.transform.ref);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function eachRepr(plugin: PluginContext, components: StructureComponentRef[], callback: SubstanceEachReprCallback) {
|
||||
const state = plugin.state.data;
|
||||
const update = state.build();
|
||||
for (const c of components) {
|
||||
for (const r of c.representations) {
|
||||
const substance = state.select(StateSelection.Generators.ofTransformer(StateTransforms.Representation.SubstanceStructureRepresentation3DFromBundle, r.cell.transform.ref).withTag(SubstanceManagerTag));
|
||||
await callback(update, r.cell, substance[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return update.commit({ doNotUpdateCurrent: true });
|
||||
}
|
||||
|
||||
/** filter substance layers for given structure */
|
||||
function getFilteredBundle(layers: Substance.BundleLayer[], structure: Structure) {
|
||||
const substance = Substance.ofBundle(layers, structure.root);
|
||||
const merged = Substance.merge(substance);
|
||||
return Substance.filter(merged, structure);
|
||||
}
|
||||
@@ -30,6 +30,8 @@ import { Clipping } from '../../../mol-theme/clipping';
|
||||
import { setStructureClipping } from '../../helpers/structure-clipping';
|
||||
import { setStructureTransparency } from '../../helpers/structure-transparency';
|
||||
import { StructureFocusRepresentation } from '../../../mol-plugin/behavior/dynamic/selection/structure-focus-representation';
|
||||
import { setStructureSubstance } from '../../helpers/structure-substance';
|
||||
import { Material } from '../../../mol-util/material';
|
||||
|
||||
export { StructureComponentManager };
|
||||
|
||||
@@ -67,22 +69,24 @@ class StructureComponentManager extends StatefulPluginComponent<StructureCompone
|
||||
await update.commit();
|
||||
await this.plugin.state.updateBehavior(StructureFocusRepresentation, p => {
|
||||
p.ignoreHydrogens = !options.showHydrogens;
|
||||
p.material = options.materialStyle;
|
||||
});
|
||||
if (interactionChanged) await this.updateInterationProps();
|
||||
});
|
||||
}
|
||||
|
||||
private updateReprParams(update: StateBuilder.Root, component: StructureComponentRef) {
|
||||
const { showHydrogens, visualQuality: quality } = this.state.options;
|
||||
const { showHydrogens, visualQuality: quality, materialStyle: material } = this.state.options;
|
||||
const ignoreHydrogens = !showHydrogens;
|
||||
for (const r of component.representations) {
|
||||
if (r.cell.transform.transformer !== StructureRepresentation3D) continue;
|
||||
|
||||
const params = r.cell.transform.params as StateTransformer.Params<StructureRepresentation3D>;
|
||||
if (!!params.type.params.ignoreHydrogens !== ignoreHydrogens || params.type.params.quality !== quality) {
|
||||
if (!!params.type.params.ignoreHydrogens !== ignoreHydrogens || params.type.params.quality !== quality || params.type.params.material !== material) {
|
||||
update.to(r.cell).update(old => {
|
||||
old.type.params.ignoreHydrogens = ignoreHydrogens;
|
||||
old.type.params.quality = quality;
|
||||
old.type.params.material = material;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -301,9 +305,9 @@ class StructureComponentManager extends StatefulPluginComponent<StructureCompone
|
||||
addRepresentation(components: ReadonlyArray<StructureComponentRef>, type: string) {
|
||||
if (components.length === 0) return;
|
||||
|
||||
const { showHydrogens, visualQuality: quality } = this.state.options;
|
||||
const { showHydrogens, visualQuality: quality, materialStyle: material } = this.state.options;
|
||||
const ignoreHydrogens = !showHydrogens;
|
||||
const typeParams = { ignoreHydrogens, quality };
|
||||
const typeParams = { ignoreHydrogens, quality, material };
|
||||
|
||||
return this.plugin.dataTransaction(async () => {
|
||||
for (const component of components) {
|
||||
@@ -338,9 +342,9 @@ class StructureComponentManager extends StatefulPluginComponent<StructureCompone
|
||||
const xs = structures || this.currentStructures;
|
||||
if (xs.length === 0) return;
|
||||
|
||||
const { showHydrogens, visualQuality: quality } = this.state.options;
|
||||
const { showHydrogens, visualQuality: quality, materialStyle: material } = this.state.options;
|
||||
const ignoreHydrogens = !showHydrogens;
|
||||
const typeParams = { ignoreHydrogens, quality };
|
||||
const typeParams = { ignoreHydrogens, quality, material };
|
||||
|
||||
const componentKey = UUID.create22();
|
||||
for (const s of xs) {
|
||||
@@ -372,14 +376,19 @@ class StructureComponentManager extends StatefulPluginComponent<StructureCompone
|
||||
|
||||
const getLoci = async (s: Structure) => StructureSelection.toLociWithSourceUnits(await params.selection.getSelection(this.plugin, ctx, s));
|
||||
for (const s of xs) {
|
||||
if (params.action.name === 'reset') {
|
||||
await setStructureOverpaint(this.plugin, s.components, -1, getLoci, params.representations);
|
||||
} else if (params.action.name === 'color') {
|
||||
if (params.action.name === 'color') {
|
||||
const p = params.action.params;
|
||||
await setStructureOverpaint(this.plugin, s.components, p.color, getLoci, params.representations);
|
||||
} else if (params.action.name === 'resetColor') {
|
||||
await setStructureOverpaint(this.plugin, s.components, -1, getLoci, params.representations);
|
||||
} else if (params.action.name === 'transparency') {
|
||||
const p = params.action.params;
|
||||
await setStructureTransparency(this.plugin, s.components, p.value, getLoci, params.representations);
|
||||
} else if (params.action.name === 'material') {
|
||||
const p = params.action.params;
|
||||
await setStructureSubstance(this.plugin, s.components, p.material, getLoci, params.representations);
|
||||
} else if (params.action.name === 'resetMaterial') {
|
||||
await setStructureSubstance(this.plugin, s.components, void 0, getLoci, params.representations);
|
||||
} else if (params.action.name === 'clipping') {
|
||||
const p = params.action.params;
|
||||
await setStructureClipping(this.plugin, s.components, Clipping.Groups.fromNames(p.excludeGroups), getLoci, params.representations);
|
||||
@@ -445,6 +454,7 @@ namespace StructureComponentManager {
|
||||
export const OptionsParams = {
|
||||
showHydrogens: PD.Boolean(true, { description: 'Toggle display of hydrogen atoms in representations' }),
|
||||
visualQuality: PD.Select('auto', VisualQualityOptions, { description: 'Control the visual/rendering quality of representations' }),
|
||||
materialStyle: Material.getParam(),
|
||||
interactions: PD.Group(InteractionsProvider.defaultParams, { label: 'Non-covalent Interactions' }),
|
||||
};
|
||||
export type Options = PD.Values<typeof OptionsParams>
|
||||
@@ -477,10 +487,14 @@ namespace StructureComponentManager {
|
||||
color: PD.Group({
|
||||
color: PD.Color(ColorNames.blue, { isExpanded: true }),
|
||||
}, { isFlat: true }),
|
||||
reset: PD.EmptyGroup({ label: 'Reset Color' }),
|
||||
resetColor: PD.EmptyGroup({ label: 'Reset Color' }),
|
||||
transparency: PD.Group({
|
||||
value: PD.Numeric(0.5, { min: 0, max: 1, step: 0.01 }),
|
||||
}, { isFlat: true }),
|
||||
material: PD.Group({
|
||||
material: Material.getParam({ isFlat: true }),
|
||||
}, { isFlat: true }),
|
||||
resetMaterial: PD.EmptyGroup({ label: 'Reset Material' }),
|
||||
clipping: PD.Group({
|
||||
excludeGroups: PD.MultiSelect([] as Clipping.Groups.Names[], PD.objectToOptions(Clipping.Groups.Names)),
|
||||
}, { isFlat: true }),
|
||||
|
||||
@@ -41,6 +41,8 @@ import { getBoxMesh } from './shape';
|
||||
import { Shape } from '../../mol-model/shape';
|
||||
import { Box3D } from '../../mol-math/geometry';
|
||||
import { PlaneParams, PlaneRepresentation } from '../../mol-repr/shape/loci/plane';
|
||||
import { Substance } from '../../mol-theme/substance';
|
||||
import { Material } from '../../mol-util/material';
|
||||
|
||||
export { StructureRepresentation3D };
|
||||
export { ExplodeStructureRepresentation3D };
|
||||
@@ -50,6 +52,8 @@ export { OverpaintStructureRepresentation3DFromScript };
|
||||
export { OverpaintStructureRepresentation3DFromBundle };
|
||||
export { TransparencyStructureRepresentation3DFromScript };
|
||||
export { TransparencyStructureRepresentation3DFromBundle };
|
||||
export { SubstanceStructureRepresentation3DFromScript };
|
||||
export { SubstanceStructureRepresentation3DFromBundle };
|
||||
export { ClippingStructureRepresentation3DFromScript };
|
||||
export { ClippingStructureRepresentation3DFromBundle };
|
||||
export { VolumeRepresentation3D };
|
||||
@@ -529,6 +533,121 @@ const TransparencyStructureRepresentation3DFromBundle = PluginStateTransform.Bui
|
||||
}
|
||||
});
|
||||
|
||||
type SubstanceStructureRepresentation3DFromScript = typeof SubstanceStructureRepresentation3DFromScript
|
||||
const SubstanceStructureRepresentation3DFromScript = PluginStateTransform.BuiltIn({
|
||||
name: 'substance-structure-representation-3d-from-script',
|
||||
display: 'Substance 3D Representation',
|
||||
from: SO.Molecule.Structure.Representation3D,
|
||||
to: SO.Molecule.Structure.Representation3DState,
|
||||
params: () => ({
|
||||
layers: PD.ObjectList({
|
||||
script: PD.Script(Script('(sel.atom.all)', 'mol-script')),
|
||||
material: Material.getParam(),
|
||||
clear: PD.Boolean(false)
|
||||
}, e => `${e.clear ? 'Clear' : Material.toString(e.material)}`, {
|
||||
defaultValue: [{
|
||||
script: Script('(sel.atom.all)', 'mol-script'),
|
||||
material: Material({ roughness: 1 }),
|
||||
clear: false
|
||||
}]
|
||||
}),
|
||||
})
|
||||
})({
|
||||
canAutoUpdate() {
|
||||
return true;
|
||||
},
|
||||
apply({ a, params }) {
|
||||
const structure = a.data.sourceData;
|
||||
const geometryVersion = a.data.repr.geometryVersion;
|
||||
const substance = Substance.ofScript(params.layers, structure);
|
||||
|
||||
return new SO.Molecule.Structure.Representation3DState({
|
||||
state: { substance },
|
||||
initialState: { substance: Substance.Empty },
|
||||
info: { structure, geometryVersion },
|
||||
repr: a.data.repr
|
||||
}, { label: `Substance (${substance.layers.length} Layers)` });
|
||||
},
|
||||
update({ a, b, newParams, oldParams }) {
|
||||
const info = b.data.info as { structure: Structure, geometryVersion: number };
|
||||
const newStructure = a.data.sourceData;
|
||||
if (newStructure !== info.structure) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
|
||||
|
||||
const newGeometryVersion = a.data.repr.geometryVersion;
|
||||
// smoothing needs to be re-calculated when geometry changes
|
||||
if (newGeometryVersion !== info.geometryVersion && hasColorSmoothingProp(a.data.repr.props)) return StateTransformer.UpdateResult.Unchanged;
|
||||
|
||||
const oldSubstance = b.data.state.substance!;
|
||||
const newSubstance = Substance.ofScript(newParams.layers, newStructure);
|
||||
if (Substance.areEqual(oldSubstance, newSubstance)) return StateTransformer.UpdateResult.Unchanged;
|
||||
|
||||
info.geometryVersion = newGeometryVersion;
|
||||
b.data.state.substance = newSubstance;
|
||||
b.data.repr = a.data.repr;
|
||||
b.label = `Substance (${newSubstance.layers.length} Layers)`;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
}
|
||||
});
|
||||
|
||||
type SubstanceStructureRepresentation3DFromBundle = typeof SubstanceStructureRepresentation3DFromBundle
|
||||
const SubstanceStructureRepresentation3DFromBundle = PluginStateTransform.BuiltIn({
|
||||
name: 'substance-structure-representation-3d-from-bundle',
|
||||
display: 'Substance 3D Representation',
|
||||
from: SO.Molecule.Structure.Representation3D,
|
||||
to: SO.Molecule.Structure.Representation3DState,
|
||||
params: () => ({
|
||||
layers: PD.ObjectList({
|
||||
bundle: PD.Value<StructureElement.Bundle>(StructureElement.Bundle.Empty),
|
||||
material: Material.getParam(),
|
||||
clear: PD.Boolean(false)
|
||||
}, e => `${e.clear ? 'Clear' : Material.toString(e.material)}`, {
|
||||
defaultValue: [{
|
||||
bundle: StructureElement.Bundle.Empty,
|
||||
material: Material({ roughness: 1 }),
|
||||
clear: false
|
||||
}],
|
||||
isHidden: true
|
||||
}),
|
||||
})
|
||||
})({
|
||||
canAutoUpdate() {
|
||||
return true;
|
||||
},
|
||||
apply({ a, params }) {
|
||||
const structure = a.data.sourceData;
|
||||
const geometryVersion = a.data.repr.geometryVersion;
|
||||
const substance = Substance.ofBundle(params.layers, structure);
|
||||
|
||||
return new SO.Molecule.Structure.Representation3DState({
|
||||
state: { substance },
|
||||
initialState: { substance: Substance.Empty },
|
||||
info: { structure, geometryVersion },
|
||||
repr: a.data.repr
|
||||
}, { label: `Substance (${substance.layers.length} Layers)` });
|
||||
},
|
||||
update({ a, b, newParams, oldParams }) {
|
||||
const info = b.data.info as { structure: Structure, geometryVersion: number };
|
||||
const newStructure = a.data.sourceData;
|
||||
if (newStructure !== info.structure) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
|
||||
|
||||
const newGeometryVersion = a.data.repr.geometryVersion;
|
||||
// smoothing needs to be re-calculated when geometry changes
|
||||
if (newGeometryVersion !== info.geometryVersion && hasColorSmoothingProp(a.data.repr.props)) return StateTransformer.UpdateResult.Unchanged;
|
||||
|
||||
const oldSubstance = b.data.state.substance!;
|
||||
const newSubstance = Substance.ofBundle(newParams.layers, newStructure);
|
||||
if (Substance.areEqual(oldSubstance, newSubstance)) return StateTransformer.UpdateResult.Unchanged;
|
||||
|
||||
info.geometryVersion = newGeometryVersion;
|
||||
b.data.state.substance = newSubstance;
|
||||
b.data.repr = a.data.repr;
|
||||
b.label = `Substance (${newSubstance.layers.length} Layers)`;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
}
|
||||
});
|
||||
|
||||
type ClippingStructureRepresentation3DFromScript = typeof ClippingStructureRepresentation3DFromScript
|
||||
const ClippingStructureRepresentation3DFromScript = PluginStateTransform.BuiltIn({
|
||||
name: 'clipping-structure-representation-3d-from-script',
|
||||
|
||||
@@ -19,7 +19,7 @@ import { PluginUIComponent } from '../base';
|
||||
import { ActionMenu } from './action-menu';
|
||||
import { ColorOptions, ColorValueOption, CombinedColorControl } from './color';
|
||||
import { Button, ControlGroup, ControlRow, ExpandGroup, IconButton, TextInput, ToggleButton } from './common';
|
||||
import { Icon, HelpOutlineSvg, CheckSvg, ClearSvg, BookmarksOutlinedSvg, MoreHorizSvg, ArrowDropDownSvg, ArrowRightSvg, ArrowDownwardSvg, ArrowUpwardSvg, DeleteOutlinedSvg } from './icons';
|
||||
import { Icon, HelpOutlineSvg, CheckSvg, ClearSvg, BookmarksOutlinedSvg, MoreHorizSvg, ArrowDropDownSvg, ArrowRightSvg, ArrowDownwardSvg, ArrowUpwardSvg, DeleteOutlinedSvg, TuneSvg } from './icons';
|
||||
import { legendFor } from './legend';
|
||||
import { LineGraphComponent } from './line-graph/line-graph-component';
|
||||
import { Slider, Slider2 } from './slider';
|
||||
@@ -1079,8 +1079,8 @@ export class MultiSelectControl extends React.PureComponent<ParamProps<PD.MultiS
|
||||
}
|
||||
}
|
||||
|
||||
export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>> & { inMapped?: boolean }, { isExpanded: boolean, showHelp: boolean }> {
|
||||
state = { isExpanded: !!this.props.param.isExpanded, showHelp: false }
|
||||
export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>> & { inMapped?: boolean }, { isExpanded: boolean, showPresets: boolean, showHelp: boolean }> {
|
||||
state = { isExpanded: !!this.props.param.isExpanded, showPresets: false, showHelp: false }
|
||||
|
||||
change(value: any) {
|
||||
this.props.onChange({ name: this.props.name, param: this.props.param, value });
|
||||
@@ -1091,6 +1091,29 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>
|
||||
}
|
||||
|
||||
toggleExpanded = () => this.setState({ isExpanded: !this.state.isExpanded });
|
||||
toggleShowPresets = () => this.setState({ showPresets: !this.state.showPresets });
|
||||
|
||||
presetItems = memoizeLatest((param: PD.Group<any>) => ActionMenu.createItemsFromSelectOptions(param.presets ?? []));
|
||||
|
||||
onSelectPreset: ActionMenu.OnSelect = item => {
|
||||
this.setState({ showPresets: false });
|
||||
this.change(item?.value);
|
||||
}
|
||||
|
||||
presets() {
|
||||
if (!this.props.param.presets) return null;
|
||||
|
||||
const label = this.props.param.label || camelCaseToWords(this.props.name);
|
||||
return <div className='msp-control-group-wrapper'>
|
||||
<div className='msp-control-group-header'>
|
||||
<button className='msp-btn msp-form-control msp-btn-block' onClick={this.toggleShowPresets}>
|
||||
<Icon svg={TuneSvg} />
|
||||
{label} Presets
|
||||
</button>
|
||||
</div>
|
||||
{this.state.showPresets && <ActionMenu items={this.presetItems(this.props.param)} onSelect={this.onSelectPreset} />}
|
||||
</div>;
|
||||
}
|
||||
|
||||
pivoted() {
|
||||
const key = this.props.param.pivot as string;
|
||||
@@ -1116,6 +1139,7 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>
|
||||
{ctrl}
|
||||
<IconButton svg={MoreHorizSvg} onClick={this.toggleExpanded} toggleState={this.state.isExpanded} title={`More Options`} />
|
||||
<div className='msp-control-offset'>
|
||||
{this.presets()}
|
||||
<ParameterControls params={filtered} onEnter={this.props.onEnter} values={this.props.value} onChange={this.onChangeParam} isDisabled={this.props.isDisabled} />
|
||||
</div>
|
||||
</div>;
|
||||
@@ -1149,6 +1173,7 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>
|
||||
</button>
|
||||
</div>
|
||||
{this.state.isExpanded && <div className='msp-control-offset'>
|
||||
{this.presets()}
|
||||
{controls}
|
||||
</div>}
|
||||
</div>;
|
||||
|
||||
@@ -56,11 +56,10 @@ const SimpleSettingsParams = {
|
||||
transparent: PD.Boolean(false)
|
||||
}, { pivot: 'color' }),
|
||||
lighting: PD.Group({
|
||||
renderStyle: Canvas3DParams.renderer.params.style,
|
||||
occlusion: Canvas3DParams.postprocessing.params.occlusion,
|
||||
outline: Canvas3DParams.postprocessing.params.outline,
|
||||
fog: Canvas3DParams.cameraFog,
|
||||
}, { pivot: 'renderStyle' }),
|
||||
}, { isFlat: true }),
|
||||
clipping: PD.Group<any>({
|
||||
...Canvas3DParams.cameraClipping.params,
|
||||
...(Canvas3DParams.renderer.params.clip as any).params as any
|
||||
@@ -105,10 +104,9 @@ const SimpleSettingsMapping = ParamMapping({
|
||||
transparent: canvas.transparentBackground
|
||||
},
|
||||
lighting: {
|
||||
renderStyle: renderer.style,
|
||||
occlusion: canvas.postprocessing.occlusion,
|
||||
outline: canvas.postprocessing.outline,
|
||||
fog: canvas.cameraFog
|
||||
fog: canvas.cameraFog,
|
||||
},
|
||||
clipping: {
|
||||
...canvas.cameraClipping,
|
||||
@@ -123,7 +121,6 @@ const SimpleSettingsMapping = ParamMapping({
|
||||
canvas.camera = s.camera;
|
||||
canvas.transparentBackground = s.background.transparent;
|
||||
canvas.renderer.backgroundColor = s.background.color;
|
||||
canvas.renderer.style = s.lighting.renderStyle;
|
||||
canvas.postprocessing.occlusion = s.lighting.occlusion;
|
||||
canvas.postprocessing.outline = s.lighting.outline;
|
||||
canvas.cameraFog = s.lighting.fog;
|
||||
|
||||
@@ -18,6 +18,7 @@ import { SizeTheme } from '../../../../mol-theme/size';
|
||||
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
|
||||
import { PluginCommands } from '../../../commands';
|
||||
import { PluginContext } from '../../../context';
|
||||
import { Material } from '../../../../mol-util/material';
|
||||
|
||||
const StructureFocusRepresentationParams = (plugin: PluginContext) => {
|
||||
const reprParams = StateTransforms.Representation.StructureRepresentation3D.definition.params!(void 0, plugin) as PD.Params;
|
||||
@@ -41,7 +42,8 @@ const StructureFocusRepresentationParams = (plugin: PluginContext) => {
|
||||
}),
|
||||
components: PD.MultiSelect(FocusComponents, PD.arrayToOptions(FocusComponents)),
|
||||
excludeTargetFromSurroundings: PD.Boolean(false, { label: 'Exclude Target', description: 'Exclude the focus "target" from the surroudings component.' }),
|
||||
ignoreHydrogens: PD.Boolean(false)
|
||||
ignoreHydrogens: PD.Boolean(false),
|
||||
material: Material.getParam(),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -67,7 +69,7 @@ class StructureFocusRepresentationBehavior extends PluginBehavior.WithSubscriber
|
||||
...this.params.targetParams,
|
||||
type: {
|
||||
name: reprParams.type.name,
|
||||
params: { ...reprParams.type.params, ignoreHydrogens: this.params.ignoreHydrogens }
|
||||
params: { ...reprParams.type.params, ignoreHydrogens: this.params.ignoreHydrogens, material: this.params.material }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@ export const DefaultPluginSpec = (): PluginSpec => ({
|
||||
PluginSpec.Action(StateTransforms.Representation.UnwindStructureAssemblyRepresentation3D),
|
||||
PluginSpec.Action(StateTransforms.Representation.OverpaintStructureRepresentation3DFromScript),
|
||||
PluginSpec.Action(StateTransforms.Representation.TransparencyStructureRepresentation3DFromScript),
|
||||
PluginSpec.Action(StateTransforms.Representation.SubstanceStructureRepresentation3DFromScript),
|
||||
|
||||
PluginSpec.Action(AssignColorVolume),
|
||||
PluginSpec.Action(StateTransforms.Volume.VolumeFromCcp4),
|
||||
|
||||
@@ -20,6 +20,7 @@ import { merge } from 'rxjs';
|
||||
import { PluginContext } from './context';
|
||||
import { PluginComponent } from '../mol-plugin-state/component';
|
||||
import { PluginConfig } from './config';
|
||||
import { StructureComponentManager } from '../mol-plugin-state/manager/structure/component';
|
||||
|
||||
export { PluginState };
|
||||
|
||||
@@ -64,6 +65,9 @@ class PluginState extends PluginComponent {
|
||||
canvas3d: p.canvas3d ? { props: this.plugin.canvas3d?.props } : void 0,
|
||||
interactivity: p.interactivity ? { props: this.plugin.managers.interactivity.props } : void 0,
|
||||
structureFocus: this.plugin.managers.structure.focus.getSnapshot(),
|
||||
structureComponentManager: p.componentManager ? {
|
||||
options: this.plugin.managers.structure.component.state.options
|
||||
} : void 0,
|
||||
durationInMs: p?.durationInMs
|
||||
};
|
||||
}
|
||||
@@ -71,6 +75,8 @@ class PluginState extends PluginComponent {
|
||||
async setSnapshot(snapshot: PluginState.Snapshot) {
|
||||
await this.animation.stop();
|
||||
|
||||
// this needs to go 1st since these changes are already baked into the behavior and data state
|
||||
if (snapshot.structureComponentManager?.options) await this.plugin.managers.structure.component.setOptions(snapshot.structureComponentManager?.options);
|
||||
if (snapshot.behaviour) await this.plugin.runTask(this.behaviors.setSnapshot(snapshot.behaviour));
|
||||
if (snapshot.data) await this.plugin.runTask(this.data.setSnapshot(snapshot.data));
|
||||
if (snapshot.canvas3d?.props) {
|
||||
@@ -139,6 +145,7 @@ namespace PluginState {
|
||||
durationInMs: PD.Numeric(1500, { min: 100, max: 15000, step: 100 }, { label: 'Duration in ms' }),
|
||||
data: PD.Boolean(true),
|
||||
behavior: PD.Boolean(false),
|
||||
componentManager: PD.Boolean(true),
|
||||
animation: PD.Boolean(true),
|
||||
startAnimation: PD.Boolean(false),
|
||||
canvas3d: PD.Boolean(true),
|
||||
@@ -172,6 +179,9 @@ namespace PluginState {
|
||||
props?: InteractivityManager.Props
|
||||
},
|
||||
structureFocus?: StructureFocusSnapshot,
|
||||
structureComponentManager?: {
|
||||
options?: StructureComponentManager.Options
|
||||
},
|
||||
durationInMs?: number
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import { CustomProperty } from '../mol-model-props/common/custom-property';
|
||||
import { Clipping } from '../mol-theme/clipping';
|
||||
import { SetUtils } from '../mol-util/set';
|
||||
import { cantorPairing } from '../mol-data/util';
|
||||
import { Substance } from '../mol-theme/substance';
|
||||
|
||||
export type RepresentationProps = { [k: string]: any }
|
||||
|
||||
@@ -186,6 +187,8 @@ namespace Representation {
|
||||
overpaint: Overpaint
|
||||
/** Per group transparency applied to the representation's renderobjects */
|
||||
transparency: Transparency
|
||||
/** Per group material applied to the representation's renderobjects */
|
||||
substance: Substance
|
||||
/** Bit mask of per group clipping applied to the representation's renderobjects */
|
||||
clipping: Clipping
|
||||
/** Controls if the representation's renderobjects are synced automatically with GPU or not */
|
||||
@@ -196,7 +199,7 @@ namespace Representation {
|
||||
markerActions: MarkerActions
|
||||
}
|
||||
export function createState(): State {
|
||||
return { visible: true, alphaFactor: 1, pickable: true, colorOnly: false, syncManually: false, transform: Mat4.identity(), overpaint: Overpaint.Empty, transparency: Transparency.Empty, clipping: Clipping.Empty, markerActions: MarkerActions.All };
|
||||
return { visible: true, alphaFactor: 1, pickable: true, colorOnly: false, syncManually: false, transform: Mat4.identity(), overpaint: Overpaint.Empty, transparency: Transparency.Empty, substance: Substance.Empty, clipping: Clipping.Empty, markerActions: MarkerActions.All };
|
||||
}
|
||||
export function updateState(state: State, update: Partial<State>) {
|
||||
if (update.visible !== undefined) state.visible = update.visible;
|
||||
@@ -205,6 +208,7 @@ namespace Representation {
|
||||
if (update.colorOnly !== undefined) state.colorOnly = update.colorOnly;
|
||||
if (update.overpaint !== undefined) state.overpaint = update.overpaint;
|
||||
if (update.transparency !== undefined) state.transparency = update.transparency;
|
||||
if (update.substance !== undefined) state.substance = update.substance;
|
||||
if (update.clipping !== undefined) state.clipping = update.clipping;
|
||||
if (update.syncManually !== undefined) state.syncManually = update.syncManually;
|
||||
if (update.transform !== undefined) Mat4.copy(state.transform, update.transform);
|
||||
@@ -410,6 +414,9 @@ namespace Representation {
|
||||
if (state.transparency !== undefined) {
|
||||
// TODO
|
||||
}
|
||||
if (state.substance !== undefined) {
|
||||
// TODO
|
||||
}
|
||||
if (state.transform !== undefined) Visual.setTransform(renderObject, state.transform);
|
||||
|
||||
Representation.updateState(currentState, state);
|
||||
|
||||
@@ -216,6 +216,9 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa
|
||||
if (state.transparency !== undefined) {
|
||||
Visual.setTransparency(_renderObject, state.transparency, lociApply, true);
|
||||
}
|
||||
if (state.substance !== undefined) {
|
||||
Visual.setSubstance(_renderObject, state.substance, lociApply, true);
|
||||
}
|
||||
if (state.transform !== undefined) Visual.setTransform(_renderObject, state.transform);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import { StructureParams } from './params';
|
||||
import { Clipping } from '../../mol-theme/clipping';
|
||||
import { Transparency } from '../../mol-theme/transparency';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { Substance } from '../../mol-theme/substance';
|
||||
|
||||
export function ComplexRepresentation<P extends StructureParams>(label: string, ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, P>, visualCtor: (materialId: number, structure: Structure, props: PD.Values<P>, webgl?: WebGLContext) => ComplexVisual<P>): StructureRepresentation<P> {
|
||||
let version = 0;
|
||||
@@ -113,6 +114,11 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
|
||||
const remappedTransparency = Transparency.remap(state.transparency, _structure);
|
||||
visual.setTransparency(remappedTransparency, webgl);
|
||||
}
|
||||
if (state.substance !== undefined && visual) {
|
||||
// Remap loci from equivalent structure to the current structure
|
||||
const remappedSubstance = Substance.remap(state.substance, _structure);
|
||||
visual.setSubstance(remappedSubstance, webgl);
|
||||
}
|
||||
if (state.clipping !== undefined && visual) {
|
||||
// Remap loci from equivalent structure to the current structure
|
||||
const remappedClipping = Clipping.remap(state.clipping, _structure);
|
||||
|
||||
@@ -36,6 +36,7 @@ import { Clipping } from '../../mol-theme/clipping';
|
||||
import { TextureMesh } from '../../mol-geo/geometry/texture-mesh/texture-mesh';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { isPromiseLike } from '../../mol-util/type-helpers';
|
||||
import { Substance } from '../../mol-theme/substance';
|
||||
|
||||
export interface ComplexVisual<P extends StructureParams> extends Visual<Structure, P> { }
|
||||
|
||||
@@ -266,6 +267,10 @@ export function ComplexVisual<G extends Geometry, P extends StructureParams & Ge
|
||||
const smoothing = { geometry, props: currentProps, webgl };
|
||||
Visual.setTransparency(renderObject, transparency, lociApply, true, smoothing);
|
||||
},
|
||||
setSubstance(substance: Substance, webgl?: WebGLContext) {
|
||||
const smoothing = { geometry, props: currentProps, webgl };
|
||||
Visual.setSubstance(renderObject, substance, lociApply, true, smoothing);
|
||||
},
|
||||
setClipping(clipping: Clipping) {
|
||||
Visual.setClipping(renderObject, clipping, lociApply, true);
|
||||
},
|
||||
|
||||
@@ -25,6 +25,7 @@ import { StructureParams } from './params';
|
||||
import { Clipping } from '../../mol-theme/clipping';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { StructureGroup } from './visual/util/common';
|
||||
import { Substance } from '../../mol-theme/substance';
|
||||
|
||||
export interface UnitsVisual<P extends StructureParams> extends Visual<StructureGroup, P> { }
|
||||
|
||||
@@ -218,13 +219,14 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
|
||||
}
|
||||
|
||||
function setVisualState(visual: UnitsVisual<P>, group: Unit.SymmetryGroup, state: Partial<StructureRepresentationState>) {
|
||||
const { visible, alphaFactor, pickable, overpaint, transparency, clipping, transform, unitTransforms } = state;
|
||||
const { visible, alphaFactor, pickable, overpaint, transparency, substance, clipping, transform, unitTransforms } = state;
|
||||
|
||||
if (visible !== undefined) visual.setVisibility(visible);
|
||||
if (alphaFactor !== undefined) visual.setAlphaFactor(alphaFactor);
|
||||
if (pickable !== undefined) visual.setPickable(pickable);
|
||||
if (overpaint !== undefined) visual.setOverpaint(overpaint, webgl);
|
||||
if (transparency !== undefined) visual.setTransparency(transparency, webgl);
|
||||
if (substance !== undefined) visual.setSubstance(substance, webgl);
|
||||
if (clipping !== undefined) visual.setClipping(clipping);
|
||||
if (transform !== undefined) visual.setTransform(transform);
|
||||
if (unitTransforms !== undefined) {
|
||||
@@ -238,7 +240,7 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
|
||||
}
|
||||
|
||||
function setState(state: Partial<StructureRepresentationState>) {
|
||||
const { visible, alphaFactor, pickable, overpaint, transparency, clipping, transform, unitTransforms, syncManually, markerActions } = state;
|
||||
const { visible, alphaFactor, pickable, overpaint, transparency, substance, clipping, transform, unitTransforms, syncManually, markerActions } = state;
|
||||
const newState: Partial<StructureRepresentationState> = {};
|
||||
|
||||
if (visible !== _state.visible) newState.visible = visible;
|
||||
@@ -250,6 +252,9 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
|
||||
if (transparency !== undefined && _structure) {
|
||||
newState.transparency = Transparency.remap(transparency, _structure);
|
||||
}
|
||||
if (substance !== undefined && _structure) {
|
||||
newState.substance = Substance.remap(substance, _structure);
|
||||
}
|
||||
if (clipping !== undefined && _structure) {
|
||||
newState.clipping = Clipping.remap(clipping, _structure);
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import { StructureParams, StructureMeshParams, StructureSpheresParams, Structure
|
||||
import { Clipping } from '../../mol-theme/clipping';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { isPromiseLike } from '../../mol-util/type-helpers';
|
||||
import { Substance } from '../../mol-theme/substance';
|
||||
|
||||
export interface UnitsVisual<P extends RepresentationProps = {}> extends Visual<StructureGroup, P> { }
|
||||
|
||||
@@ -331,6 +332,10 @@ export function UnitsVisual<G extends Geometry, P extends StructureParams & Geom
|
||||
const smoothing = { geometry, props: currentProps, webgl };
|
||||
Visual.setTransparency(renderObject, transparency, lociApply, true, smoothing);
|
||||
},
|
||||
setSubstance(substance: Substance, webgl?: WebGLContext) {
|
||||
const smoothing = { geometry, props: currentProps, webgl };
|
||||
Visual.setSubstance(renderObject, substance, lociApply, true, smoothing);
|
||||
},
|
||||
setClipping(clipping: Clipping) {
|
||||
Visual.setClipping(renderObject, clipping, lociApply, true);
|
||||
},
|
||||
|
||||
@@ -27,8 +27,10 @@ import { getMarkersAverage } from '../mol-geo/geometry/marker-data';
|
||||
import { Texture } from '../mol-gl/webgl/texture';
|
||||
import { Geometry } from '../mol-geo/geometry/geometry';
|
||||
import { getColorSmoothingProps, hasColorSmoothingProp } from '../mol-geo/geometry/base';
|
||||
import { applyMeshOverpaintSmoothing, applyMeshTransparencySmoothing } from '../mol-geo/geometry/mesh/color-smoothing';
|
||||
import { applyTextureMeshOverpaintSmoothing, applyTextureMeshTransparencySmoothing } from '../mol-geo/geometry/texture-mesh/color-smoothing';
|
||||
import { applyMeshOverpaintSmoothing, applyMeshSubstanceSmoothing, applyMeshTransparencySmoothing } from '../mol-geo/geometry/mesh/color-smoothing';
|
||||
import { applyTextureMeshOverpaintSmoothing, applyTextureMeshSubstanceSmoothing, applyTextureMeshTransparencySmoothing } from '../mol-geo/geometry/texture-mesh/color-smoothing';
|
||||
import { Substance } from '../mol-theme/substance';
|
||||
import { applySubstanceMaterial, clearSubstance, createSubstance } from '../mol-geo/geometry/substance-data';
|
||||
|
||||
export interface VisualContext {
|
||||
readonly runtime: RuntimeContext
|
||||
@@ -51,6 +53,7 @@ interface Visual<D, P extends PD.Params> {
|
||||
setTransform: (matrix?: Mat4, instanceMatrices?: Float32Array | null) => void
|
||||
setOverpaint: (overpaint: Overpaint, webgl?: WebGLContext) => void
|
||||
setTransparency: (transparency: Transparency, webgl?: WebGLContext) => void
|
||||
setSubstance: (substance: Substance, webgl?: WebGLContext) => void
|
||||
setClipping: (clipping: Clipping) => void
|
||||
destroy: () => void
|
||||
mustRecreate?: (data: D, props: PD.Values<P>, webgl?: WebGLContext) => boolean
|
||||
@@ -143,6 +146,7 @@ namespace Visual {
|
||||
resolution?: number
|
||||
overpaintTexture?: Texture
|
||||
transparencyTexture?: Texture
|
||||
substanceTexture?: Texture
|
||||
}
|
||||
|
||||
type SmoothingContext = {
|
||||
@@ -248,6 +252,55 @@ namespace Visual {
|
||||
}
|
||||
}
|
||||
|
||||
export function setSubstance(renderObject: GraphicsRenderObject | undefined, substance: Substance, lociApply: LociApply, clear: boolean, smoothing?: SmoothingContext) {
|
||||
if (!renderObject) return;
|
||||
|
||||
const { tSubstance, dSubstanceType, uGroupCount, instanceCount } = renderObject.values;
|
||||
const count = uGroupCount.ref.value * instanceCount.ref.value;
|
||||
|
||||
// ensure texture has right size
|
||||
createSubstance(substance.layers.length ? count : 0, renderObject.values);
|
||||
const { array } = tSubstance.ref.value;
|
||||
|
||||
// clear all if requested
|
||||
if (clear) clearSubstance(array, 0, count);
|
||||
|
||||
for (let i = 0, il = substance.layers.length; i < il; ++i) {
|
||||
const { loci, material, clear } = substance.layers[i];
|
||||
const apply = (interval: Interval) => {
|
||||
const start = Interval.start(interval);
|
||||
const end = Interval.end(interval);
|
||||
return clear
|
||||
? clearSubstance(array, start, end)
|
||||
: applySubstanceMaterial(array, start, end, material);
|
||||
};
|
||||
lociApply(loci, apply, false);
|
||||
}
|
||||
ValueCell.update(tSubstance, tSubstance.ref.value);
|
||||
ValueCell.updateIfChanged(dSubstanceType, 'groupInstance');
|
||||
|
||||
if (substance.layers.length === 0) return;
|
||||
|
||||
if (smoothing && hasColorSmoothingProp(smoothing.props)) {
|
||||
const { geometry, props, webgl } = smoothing;
|
||||
if (geometry.kind === 'mesh') {
|
||||
const { resolution, substanceTexture } = geometry.meta as SurfaceMeta;
|
||||
const csp = getColorSmoothingProps(props.smoothColors, true, resolution);
|
||||
if (csp) {
|
||||
applyMeshSubstanceSmoothing(renderObject.values as any, csp.resolution, csp.stride, webgl, substanceTexture);
|
||||
(geometry.meta as SurfaceMeta).substanceTexture = renderObject.values.tSubstanceGrid.ref.value;
|
||||
}
|
||||
} else if (webgl && geometry.kind === 'texture-mesh') {
|
||||
const { resolution, substanceTexture } = geometry.meta as SurfaceMeta;
|
||||
const csp = getColorSmoothingProps(props.smoothColors, true, resolution);
|
||||
if (csp) {
|
||||
applyTextureMeshSubstanceSmoothing(renderObject.values as any, csp.resolution, csp.stride, webgl, substanceTexture);
|
||||
(geometry.meta as SurfaceMeta).substanceTexture = renderObject.values.tSubstanceGrid.ref.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function setClipping(renderObject: GraphicsRenderObject | undefined, clipping: Clipping, lociApply: LociApply, clear: boolean) {
|
||||
if (!renderObject) return;
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import { SizeValues } from '../../mol-gl/renderable/schema';
|
||||
import { Clipping } from '../../mol-theme/clipping';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { isPromiseLike } from '../../mol-util/type-helpers';
|
||||
import { Substance } from '../../mol-theme/substance';
|
||||
|
||||
export interface VolumeVisual<P extends VolumeParams> extends Visual<Volume, P> { }
|
||||
|
||||
@@ -211,6 +212,9 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet
|
||||
setTransparency(transparency: Transparency) {
|
||||
return Visual.setTransparency(renderObject, transparency, lociApply, true);
|
||||
},
|
||||
setSubstance(substance: Substance) {
|
||||
return Visual.setSubstance(renderObject, substance, lociApply, true);
|
||||
},
|
||||
setClipping(clipping: Clipping) {
|
||||
return Visual.setClipping(renderObject, clipping, lociApply, true);
|
||||
},
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Overpaint {
|
||||
const layers: Overpaint.Layer[] = [];
|
||||
map.forEach((loci, colorOrClear) => {
|
||||
const clear = colorOrClear === -1;
|
||||
const color = colorOrClear === -1 ? Color(0) : colorOrClear;
|
||||
const color = clear ? Color(0) : colorOrClear;
|
||||
layers.push({ loci, color, clear });
|
||||
});
|
||||
return { layers };
|
||||
|
||||
135
src/mol-theme/substance.ts
Normal file
135
src/mol-theme/substance.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Loci } from '../mol-model/loci';
|
||||
import { Structure, StructureElement } from '../mol-model/structure';
|
||||
import { Script } from '../mol-script/script';
|
||||
import { Material } from '../mol-util/material';
|
||||
import { shallowEqual } from '../mol-util/object';
|
||||
|
||||
export { Substance };
|
||||
|
||||
type Substance = { readonly layers: ReadonlyArray<Substance.Layer> }
|
||||
|
||||
function Substance(layers: ReadonlyArray<Substance.Layer>): Substance {
|
||||
return { layers };
|
||||
}
|
||||
|
||||
namespace Substance {
|
||||
export type Layer = { readonly loci: StructureElement.Loci, readonly material: Material, readonly clear: boolean }
|
||||
export const Empty: Substance = { layers: [] };
|
||||
|
||||
export function areEqual(sA: Substance, sB: Substance) {
|
||||
if (sA.layers.length === 0 && sB.layers.length === 0) return true;
|
||||
if (sA.layers.length !== sB.layers.length) return false;
|
||||
for (let i = 0, il = sA.layers.length; i < il; ++i) {
|
||||
if (sA.layers[i].clear !== sB.layers[i].clear) return false;
|
||||
if (!shallowEqual(sA.layers[i].material, sB.layers[i].material)) return false;
|
||||
if (!Loci.areEqual(sA.layers[i].loci, sB.layers[i].loci)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isEmpty(overpaint: Substance) {
|
||||
return overpaint.layers.length === 0;
|
||||
}
|
||||
|
||||
export function remap(substance: Substance, structure: Structure) {
|
||||
const layers: Substance.Layer[] = [];
|
||||
for (const layer of substance.layers) {
|
||||
let { loci, material, clear } = layer;
|
||||
loci = StructureElement.Loci.remap(loci, structure);
|
||||
if (!StructureElement.Loci.isEmpty(loci)) {
|
||||
layers.push({ loci, material, clear });
|
||||
}
|
||||
}
|
||||
return { layers };
|
||||
}
|
||||
|
||||
export function merge(substance: Substance): Substance {
|
||||
if (isEmpty(substance)) return substance;
|
||||
const { structure } = substance.layers[0].loci;
|
||||
let clearLoci: StructureElement.Loci | undefined = void 0;
|
||||
const map = new Map<Material, StructureElement.Loci>();
|
||||
let shadowed = StructureElement.Loci.none(structure);
|
||||
for (let i = 0, il = substance.layers.length; i < il; ++i) {
|
||||
let { loci, material, clear } = substance.layers[il - i - 1]; // process from end
|
||||
loci = StructureElement.Loci.subtract(loci, shadowed);
|
||||
shadowed = StructureElement.Loci.union(loci, shadowed);
|
||||
if (!StructureElement.Loci.isEmpty(loci)) {
|
||||
if (clear) {
|
||||
clearLoci = clearLoci
|
||||
? StructureElement.Loci.union(loci, clearLoci)
|
||||
: loci;
|
||||
} else {
|
||||
if (map.has(material)) {
|
||||
loci = StructureElement.Loci.union(loci, map.get(material)!);
|
||||
}
|
||||
map.set(material, loci);
|
||||
}
|
||||
}
|
||||
}
|
||||
const layers: Substance.Layer[] = [];
|
||||
if (clearLoci) {
|
||||
layers.push({ loci: clearLoci, material: Material(), clear: true });
|
||||
}
|
||||
map.forEach((loci, material) => {
|
||||
layers.push({ loci, material, clear: false });
|
||||
});
|
||||
return { layers };
|
||||
}
|
||||
|
||||
export function filter(substance: Substance, filter: Structure): Substance {
|
||||
if (isEmpty(substance)) return substance;
|
||||
const { structure } = substance.layers[0].loci;
|
||||
const layers: Substance.Layer[] = [];
|
||||
for (const layer of substance.layers) {
|
||||
let { loci, material, clear } = layer;
|
||||
// filter by first map to the `filter` structure and
|
||||
// then map back to the original structure of the substance loci
|
||||
const filtered = StructureElement.Loci.remap(loci, filter);
|
||||
loci = StructureElement.Loci.remap(filtered, structure);
|
||||
if (!StructureElement.Loci.isEmpty(loci)) {
|
||||
layers.push({ loci, material, clear });
|
||||
}
|
||||
}
|
||||
return { layers };
|
||||
}
|
||||
|
||||
export type ScriptLayer = { script: Script, material: Material, clear: boolean }
|
||||
export function ofScript(scriptLayers: ScriptLayer[], structure: Structure): Substance {
|
||||
const layers: Substance.Layer[] = [];
|
||||
for (let i = 0, il = scriptLayers.length; i < il; ++i) {
|
||||
const { script, material, clear } = scriptLayers[i];
|
||||
const loci = Script.toLoci(script, structure);
|
||||
if (!StructureElement.Loci.isEmpty(loci)) {
|
||||
layers.push({ loci, material, clear });
|
||||
}
|
||||
}
|
||||
return { layers };
|
||||
}
|
||||
|
||||
export type BundleLayer = { bundle: StructureElement.Bundle, material: Material, clear: boolean }
|
||||
export function ofBundle(bundleLayers: BundleLayer[], structure: Structure): Substance {
|
||||
const layers: Substance.Layer[] = [];
|
||||
for (let i = 0, il = bundleLayers.length; i < il; ++i) {
|
||||
const { bundle, material, clear } = bundleLayers[i];
|
||||
const loci = StructureElement.Bundle.toLoci(bundle, structure.root);
|
||||
layers.push({ loci, material, clear });
|
||||
}
|
||||
return { layers };
|
||||
}
|
||||
|
||||
export function toBundle(overpaint: Substance) {
|
||||
const layers: BundleLayer[] = [];
|
||||
for (let i = 0, il = overpaint.layers.length; i < il; ++i) {
|
||||
const { loci, material, clear } = overpaint.layers[i];
|
||||
const bundle = StructureElement.Bundle.fromLoci(loci);
|
||||
layers.push({ bundle, material, clear });
|
||||
}
|
||||
return { layers };
|
||||
}
|
||||
}
|
||||
48
src/mol-util/material.ts
Normal file
48
src/mol-util/material.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { NumberArray } from './type-helpers';
|
||||
import { ParamDefinition as PD } from './param-definition';
|
||||
|
||||
export interface Material {
|
||||
/** Normalized to [0, 1] range */
|
||||
metalness: number,
|
||||
/** Normalized to [0, 1] range */
|
||||
roughness: number
|
||||
}
|
||||
|
||||
export function Material(values?: Partial<Material>) {
|
||||
return { ...Material.Zero, ...values };
|
||||
}
|
||||
|
||||
export namespace Material {
|
||||
export const Zero: Material = { metalness: 0, roughness: 0 };
|
||||
|
||||
export function toArray(material: Material, array: NumberArray, offset: number) {
|
||||
array[offset] = material.metalness * 255;
|
||||
array[offset + 1] = material.roughness * 255;
|
||||
return array;
|
||||
}
|
||||
|
||||
export function toString({ metalness, roughness }: Material) {
|
||||
return `M ${metalness.toFixed(2)} | R ${roughness.toFixed(2)}`;
|
||||
}
|
||||
|
||||
export function getParam(info?: { isExpanded?: boolean, isFlat?: boolean }) {
|
||||
return PD.Group({
|
||||
metalness: PD.Numeric(0, { min: 0, max: 1, step: 0.01 }),
|
||||
roughness: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }),
|
||||
}, {
|
||||
...info,
|
||||
presets: [
|
||||
[{ metalness: 0, roughness: 1 }, 'Matte'],
|
||||
[{ metalness: 0, roughness: 0.2 }, 'Plastic'],
|
||||
[{ metalness: 0, roughness: 0.6 }, 'Glossy'],
|
||||
[{ metalness: 1.0, roughness: 0.6 }, 'Metallic'],
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -68,4 +68,8 @@ export function getPrecision(v: number) {
|
||||
|
||||
export function toPrecision(v: number, precision: number) {
|
||||
return parseFloat(v.toPrecision(precision));
|
||||
}
|
||||
|
||||
export function toFixed(v: number, fractionDigits: number) {
|
||||
return parseFloat(v.toFixed(fractionDigits));
|
||||
}
|
||||
@@ -225,12 +225,14 @@ export namespace ParamDefinition {
|
||||
export interface Group<T> extends Base<T> {
|
||||
type: 'group',
|
||||
params: Params,
|
||||
presets?: Select<T>['options'],
|
||||
isExpanded?: boolean,
|
||||
isFlat?: boolean,
|
||||
pivot?: keyof T
|
||||
}
|
||||
export function Group<T>(params: For<T>, info?: Info & { isExpanded?: boolean, isFlat?: boolean, customDefault?: any, pivot?: keyof T }): Group<Normalize<T>> {
|
||||
export function Group<T>(params: For<T>, info?: Info & { isExpanded?: boolean, isFlat?: boolean, customDefault?: any, pivot?: keyof T, presets?: Select<T>['options'] }): Group<Normalize<T>> {
|
||||
const ret = setInfo<Group<Normalize<T>>>({ type: 'group', defaultValue: info?.customDefault || getDefaultValues(params as any as Params) as any, params: params as any as Params }, info);
|
||||
if (info?.presets) ret.presets = info.presets;
|
||||
if (info?.isExpanded) ret.isExpanded = info.isExpanded;
|
||||
if (info?.isFlat) ret.isFlat = info.isFlat;
|
||||
if (info?.pivot) ret.pivot = info.pivot as any;
|
||||
|
||||
Reference in New Issue
Block a user