Compare commits

...

32 Commits

Author SHA1 Message Date
dsehnal
ae795f8ad3 3.32.0 2023-03-20 09:29:41 +01:00
dsehnal
9d3c071689 changelog 2023-03-20 09:26:48 +01:00
David Sehnal
01cb23f566 add setFSModule (#755) 2023-03-20 09:24:45 +01:00
Alexander Rose
fe8a9799ab add exposure parameter (#751)
* add exposure parameter

* add missing uniform

---------

Co-authored-by: David Sehnal <dsehnal@users.noreply.github.com>
2023-03-20 09:16:51 +01:00
David Sehnal
4f18154681 Marking improvements (#750)
* better marking identify

* changelog

* tweak

* type tweak

* simplify ui mouse move handling

---------

Co-authored-by: Alexander Rose <alexander.rose@weirdbyte.de>
2023-03-20 09:15:51 +01:00
Alexander Rose
2114c4a3ad type fixes 2023-03-19 15:12:42 -07:00
Alexander Rose
2ca41b2b51 package updates 2023-03-19 15:11:58 -07:00
Alexander Rose
6605a2019e Merge pull request #753 from giagitom/dpoit-avoid-alpha-0-rendering
Dpoit avoid alpha 0 rendering
2023-03-19 14:10:43 -07:00
giagitom
8b1ed5f183 Including wboit 2023-03-19 21:05:25 +01:00
giagitom
f11a1b788f Updated changelog 2023-03-19 19:54:30 +01:00
giagitom
7928e24c54 Avoid rendering of fully transparent renderables 2023-03-19 19:51:01 +01:00
Alexander Rose
5dbca41da6 fix blurry occlusion in screenshots 2023-03-18 19:01:33 -07:00
Alexander Rose
e636397f90 ensure marking edges are at least one pixel wide 2023-03-15 20:56:08 -07:00
Russell Parker
6d76bf120d Change nodejs-shim conditional to avoid checking document (#740) 2023-03-08 17:43:38 +01:00
Alexander Rose
a50e81551f use ssao-scale for gl viewport/scissor 2023-03-06 22:48:31 -08:00
Alexander Rose
86512bcea1 tweak ssao-blur thresholds 2023-02-26 19:16:37 -08:00
Alexander Rose
975f45eb01 package updates 2023-02-25 15:02:21 -08:00
Alexander Rose
f2399d3179 Merge pull request #737 from molstar/pp-improvements
Post-processing improvements
2023-02-25 14:45:48 -08:00
Alexander Rose
b26d62a067 webgl1 compat 2023-02-25 14:09:13 -08:00
Alexander Rose
926d6cbd46 reduce over-blurring occlusion at larger view distances 2023-02-25 13:52:18 -08:00
Alexander Rose
7ea47d2a99 use pixel-size for max depth difference 2023-02-25 13:31:44 -08:00
Alexander Rose
89ad8cfc15 fix orthographic camera defines not updated 2023-02-25 13:17:03 -08:00
Alexander Rose
302a309aff add occlussion color 2023-02-25 13:03:12 -08:00
dsehnal
c3e62bc2e5 3.31.4 2023-02-24 13:13:06 +01:00
dsehnal
c2ab322bd2 Stop animation loop on dispose 2023-02-24 13:10:35 +01:00
jump2cn
aeab0f235c allow link cylinder/line dashCount set to '0' (#735) 2023-02-23 10:52:56 +01:00
dsehnal
ae2285599f 3.31.3 2023-02-22 20:44:32 +01:00
dsehnal
104ab757d2 Update fs import in data-source.ts 2023-02-22 20:37:34 +01:00
Alexander Rose
de84a8c8c5 tweak minNear param max 2023-02-18 11:42:45 -08:00
Alexander Rose
4fa135daf0 fix near clipping avoidance in impostor shaders 2023-02-18 11:33:36 -08:00
midlik
9870cb4082 Fixed degenerate case (1-point) in PCA (#725)
* Fixed degenerate case (1-point) in PCA - now correctly returns identity matrix

* Changelog
2023-02-17 20:08:21 +01:00
Alexander Rose
b2924761ab update impostor bond visuals on sizeFactor changes 2023-02-12 22:06:18 -08:00
39 changed files with 1955 additions and 1980 deletions

View File

@@ -6,6 +6,32 @@ Note that since we don't clearly distinguish between a public and private interf
## [Unreleased]
## [v3.32.0] - 2023-03-20
- Avoid rendering of fully transparent renderables
- Add occlusion color parameter
- Fix issue with outlines and orthographic camera
- Reduce over-blurring occlusion at larger view distances
- Fix occlusion artefact with non-canvas viewport and pixel-ratio > 1
- Update nodejs-shims conditionals to handle polyfilled document object in NodeJS environment.
- Ensure marking edges are at least one pixel wide
- Add exposure parameter to renderer
- Only trigger marking when mouse is directly over canvas
- Fix blurry occlusion in screenshots
- [Breaking] Add `setFSModule` to `mol-util/data-source` instead of trying to trick WebPack
## [v3.31.4] - 2023-02-24
- Allow link cylinder/line `dashCount` set to '0'
- Stop animation loop when disposing `PluginContext` (thanks @gfrn for identifying the issue)
## [v3.31.3] - 2023-02-22
- Fix impostor bond visuals not correctly updating on `sizeFactor` changes
- Fix degenerate case in PCA
- Fix near clipping avoidance in impostor shaders
- Update `fs` import in `data-source.ts`
## [v3.31.2] - 2023-02-12
- Fix exit code of volume pack executable (pack.ts). Now exits with non-0 status when an error happens

3449
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "3.31.2",
"version": "3.32.0",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -95,51 +95,53 @@
"Gianluca Tomasello <giagitom@gmail.com>",
"Ke Ma <mark.ma@rcsb.org>",
"Jason Pattle <jpattle@exscientia.co.uk>",
"David Williams <dwilliams@nobiastx.com>"
"David Williams <dwilliams@nobiastx.com>",
"Zhenyu Zhang <jump2cn@gmail.com>",
"Russell Parker <russell@benchling.com>"
],
"license": "MIT",
"devDependencies": {
"@graphql-codegen/add": "^4.0.0",
"@graphql-codegen/cli": "^3.0.0",
"@graphql-codegen/add": "^4.0.1",
"@graphql-codegen/cli": "^3.2.2",
"@graphql-codegen/time": "^4.0.0",
"@graphql-codegen/typescript": "^3.0.0",
"@graphql-codegen/typescript": "^3.0.2",
"@graphql-codegen/typescript-graphql-files-modules": "^2.2.1",
"@graphql-codegen/typescript-graphql-request": "^4.5.8",
"@graphql-codegen/typescript-operations": "^3.0.0",
"@graphql-codegen/typescript-operations": "^3.0.2",
"@types/cors": "^2.8.13",
"@types/gl": "^6.0.2",
"@types/jpeg-js": "^0.3.7",
"@types/pngjs": "^6.0.1",
"@types/jest": "^29.4.0",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"@typescript-eslint/eslint-plugin": "^5.50.0",
"@typescript-eslint/parser": "^5.50.0",
"@types/jest": "^29.5.0",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@typescript-eslint/eslint-plugin": "^5.55.0",
"@typescript-eslint/parser": "^5.55.0",
"benchmark": "^2.1.4",
"concurrently": "^7.6.0",
"cpx2": "^4.2.0",
"cpx2": "^4.2.2",
"crypto-browserify": "^3.12.0",
"css-loader": "^6.7.3",
"eslint": "^8.33.0",
"eslint": "^8.36.0",
"extra-watch-webpack-plugin": "^1.0.3",
"file-loader": "^6.2.0",
"fs-extra": "^11.1.0",
"graphql": "^16.6.0",
"http-server": "^14.1.1",
"jest": "^29.4.1",
"mini-css-extract-plugin": "^2.7.2",
"jest": "^29.5.0",
"mini-css-extract-plugin": "^2.7.5",
"path-browserify": "^1.0.1",
"raw-loader": "^4.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"sass": "^1.58.0",
"sass-loader": "^13.2.0",
"simple-git": "^3.16.0",
"sass": "^1.59.3",
"sass-loader": "^13.2.1",
"simple-git": "^3.17.0",
"stream-browserify": "^3.0.0",
"style-loader": "^3.3.1",
"style-loader": "^3.3.2",
"ts-jest": "^29.0.5",
"typescript": "^4.9.5",
"webpack": "^5.75.0",
"typescript": "^5.0.2",
"webpack": "^5.76.2",
"webpack-cli": "^5.0.1"
},
"dependencies": {
@@ -147,20 +149,20 @@
"@types/benchmark": "^2.1.2",
"@types/compression": "1.7.2",
"@types/express": "^4.17.17",
"@types/node": "^16.18.12",
"@types/node": "^16.18.16",
"@types/node-fetch": "^2.6.2",
"@types/swagger-ui-dist": "3.30.1",
"argparse": "^2.0.1",
"body-parser": "^1.20.1",
"body-parser": "^1.20.2",
"compression": "^1.7.4",
"cors": "^2.8.5",
"express": "^4.18.2",
"h264-mp4-encoder": "^1.0.12",
"immer": "^9.0.19",
"immutable": "^4.2.3",
"immutable": "^4.3.0",
"node-fetch": "^2.6.9",
"rxjs": "^7.8.0",
"swagger-ui-dist": "^4.15.5",
"swagger-ui-dist": "^4.18.1",
"tslib": "^2.5.0",
"util.promisify": "^1.1.1",
"xhr2": "^0.2.1"

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -50,6 +50,7 @@ function occlusionStyle(plugin: PluginContext) {
radius: 5,
samples: 32,
resolutionScale: 1,
color: Color(0x000000),
} },
outline: { name: 'on', params: {
scale: 1.0,

View File

@@ -19,8 +19,11 @@ import { StructureRepresentation3D } from '../../mol-plugin-state/transforms/rep
import { HeadlessPluginContext } from '../../mol-plugin/headless-plugin-context';
import { DefaultPluginSpec } from '../../mol-plugin/spec';
import { STYLIZED_POSTPROCESSING } from '../../mol-plugin/util/headless-screenshot';
import { setFSModule } from '../../mol-util/data-source';
setFSModule(fs);
interface Args {
pdbId: string,
outDirectory: string

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -24,7 +24,7 @@ const Canvas3DPresets = {
illustrative: {
canvas3d: <Preset>{
postprocessing: {
occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15, resolutionScale: 1 } },
occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15, resolutionScale: 1, color: Color(0x000000) } },
outline: { name: 'on', params: { scale: 1, threshold: 0.33, color: Color(0x000000), includeTransparent: true, } },
shadow: { name: 'off', params: {} },
},

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Ludovic Autin <ludovic.autin@gmail.com>
@@ -604,6 +604,7 @@ export const LoadCellPackModel = StateAction.build({
bias: 1,
blurKernelSize: 15,
resolutionScale: 1,
color: Color(0x000000),
}
},
shadow: {

View File

@@ -202,7 +202,7 @@ export class ZenodoImportUI extends CollapsableControls<{}, State> {
}));
} else if (t.name === 'trajectory') {
const [topologyUrl, topologyFormat, topologyIsBinary] = t.params.topology.split('|');
const [coordinatesUrl, coordinatesFormat, coordinatesIsBinary] = t.params.coordinates.split('|');
const [coordinatesUrl, coordinatesFormat] = t.params.coordinates.split('|');
await this.plugin.runTask(this.plugin.state.data.applyAction(LoadTrajectory, {
source: {
@@ -216,7 +216,6 @@ export class ZenodoImportUI extends CollapsableControls<{}, State> {
coordinates: {
url: coordinatesUrl,
format: coordinatesFormat as any,
isBinary: coordinatesIsBinary === 'true',
},
}
}

View File

@@ -65,7 +65,7 @@ export const Canvas3DParams = {
cameraClipping: PD.Group({
radius: PD.Numeric(100, { min: 0, max: 99, step: 1 }, { label: 'Clipping', description: 'How much of the scene to show.' }),
far: PD.Boolean(true, { description: 'Hide scene in the distance' }),
minNear: PD.Numeric(5, { min: 0.1, max: 10, step: 0.1 }, { description: 'Note, may cause performance issues rendering impostors when set too small and cause issues with outline rendering when too close to 0.' }),
minNear: PD.Numeric(5, { min: 0.1, max: 100, step: 0.1 }, { description: 'Note, may cause performance issues rendering impostors when set too small and cause issues with outline rendering when too close to 0.' }),
}, { pivot: 'radius' }),
viewport: PD.MappedStatic('canvas', {
canvas: PD.Group({}),
@@ -908,6 +908,7 @@ namespace Canvas3D {
},
dispose: () => {
contextRestoredSub.unsubscribe();
cancelAnimationFrame(animationFrameHandle);
markBuffer = [];

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -197,8 +197,12 @@ export class Canvas3dInteractionHelper {
this.drag(x, y, buttons, button, modifiers);
});
input.move.subscribe(({ x, y, inside, buttons, button, modifiers }) => {
input.move.subscribe(({ x, y, inside, buttons, button, modifiers, onElement }) => {
if (!inside || this.isInteracting) return;
if (!onElement) {
this.leave();
return;
}
// console.log('move');
this.move(x, y, buttons, button, modifiers);
});

View File

@@ -106,7 +106,7 @@ export class MarkingPass {
const { highlightEdgeColor, selectEdgeColor, edgeScale, innerEdgeFactor, ghostEdgeStrength, highlightEdgeStrength, selectEdgeStrength } = props;
const { values: edgeValues } = this.edge;
const _edgeScale = Math.round(edgeScale * this.webgl.pixelRatio);
const _edgeScale = Math.max(1, Math.round(edgeScale * this.webgl.pixelRatio));
if (edgeValues.dEdgeScale.ref.value !== _edgeScale) {
ValueCell.update(edgeValues.dEdgeScale, _edgeScale);
this.edge.update();

View File

@@ -43,9 +43,9 @@ const OutlinesSchema = {
dOrthographic: DefineSpec('number'),
uNear: UniformSpec('f'),
uFar: UniformSpec('f'),
uInvProjection: UniformSpec('m4'),
uMaxPossibleViewZDiff: UniformSpec('f'),
uOutlineThreshold: UniformSpec('f'),
dTransparentOutline: DefineSpec('boolean'),
};
type OutlinesRenderable = ComputeRenderable<Values<typeof OutlinesSchema>>
@@ -63,9 +63,9 @@ function getOutlinesRenderable(ctx: WebGLContext, depthTextureOpaque: Texture, d
dOrthographic: ValueCell.create(0),
uNear: ValueCell.create(1),
uFar: ValueCell.create(10000),
uInvProjection: ValueCell.create(Mat4.identity()),
uMaxPossibleViewZDiff: ValueCell.create(0.5),
uOutlineThreshold: ValueCell.create(0.33),
dTransparentOutline: ValueCell.create(transparentOutline),
};
@@ -189,8 +189,7 @@ const SsaoBlurSchema = {
uBlurDirectionX: UniformSpec('f'),
uBlurDirectionY: UniformSpec('f'),
uMaxPossibleViewZDiff: UniformSpec('f'),
uInvProjection: UniformSpec('m4'),
uNear: UniformSpec('f'),
uFar: UniformSpec('f'),
uBounds: UniformSpec('v4'),
@@ -211,8 +210,7 @@ function getSsaoBlurRenderable(ctx: WebGLContext, ssaoDepthTexture: Texture, dir
uBlurDirectionX: ValueCell.create(direction === 'horizontal' ? 1 : 0),
uBlurDirectionY: ValueCell.create(direction === 'vertical' ? 1 : 0),
uMaxPossibleViewZDiff: ValueCell.create(0.5),
uInvProjection: ValueCell.create(Mat4.identity()),
uNear: ValueCell.create(0.0),
uFar: ValueCell.create(10000.0),
uBounds: ValueCell.create(Vec4()),
@@ -280,11 +278,9 @@ const PostprocessingSchema = {
uFogFar: UniformSpec('f'),
uFogColor: UniformSpec('v3'),
uOutlineColor: UniformSpec('v3'),
uOcclusionColor: UniformSpec('v3'),
uTransparentBackground: UniformSpec('b'),
uMaxPossibleViewZDiff: UniformSpec('f'),
uInvProjection: UniformSpec('m4'),
dOcclusionEnable: DefineSpec('boolean'),
uOcclusionOffset: UniformSpec('v2'),
@@ -292,8 +288,6 @@ const PostprocessingSchema = {
dOutlineEnable: DefineSpec('boolean'),
dOutlineScale: DefineSpec('number'),
uOutlineThreshold: UniformSpec('f'),
dTransparentOutline: DefineSpec('boolean'),
};
type PostprocessingRenderable = ComputeRenderable<Values<typeof PostprocessingSchema>>
@@ -317,11 +311,9 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d
uFogFar: ValueCell.create(10000),
uFogColor: ValueCell.create(Vec3.create(1, 1, 1)),
uOutlineColor: ValueCell.create(Vec3.create(0, 0, 0)),
uOcclusionColor: ValueCell.create(Vec3.create(0, 0, 0)),
uTransparentBackground: ValueCell.create(false),
uMaxPossibleViewZDiff: ValueCell.create(0.5),
uInvProjection: ValueCell.create(Mat4.identity()),
dOcclusionEnable: ValueCell.create(true),
uOcclusionOffset: ValueCell.create(Vec2.create(0, 0)),
@@ -329,8 +321,6 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d
dOutlineEnable: ValueCell.create(false),
dOutlineScale: ValueCell.create(1),
uOutlineThreshold: ValueCell.create(0.33),
dTransparentOutline: ValueCell.create(transparentOutline),
};
@@ -349,6 +339,7 @@ export const PostprocessingParams = {
bias: PD.Numeric(0.8, { min: 0, max: 3, step: 0.1 }),
blurKernelSize: PD.Numeric(15, { min: 1, max: 25, step: 2 }),
resolutionScale: PD.Numeric(1, { min: 0.1, max: 1, step: 0.05 }, { description: 'Adjust resolution of occlusion calculation' }),
color: PD.Color(Color(0x000000)),
}),
off: PD.Group({})
}, { cycle: true, description: 'Darken occluded crevices with the ambient occlusion effect' }),
@@ -543,11 +534,14 @@ export class PostprocessingPass {
ValueCell.updateIfChanged(this.ssaoBlurFirstPassRenderable.values.uFar, camera.far);
ValueCell.updateIfChanged(this.ssaoBlurSecondPassRenderable.values.uFar, camera.far);
ValueCell.update(this.ssaoBlurFirstPassRenderable.values.uInvProjection, invProjection);
ValueCell.update(this.ssaoBlurSecondPassRenderable.values.uInvProjection, invProjection);
if (this.ssaoBlurFirstPassRenderable.values.dOrthographic.ref.value !== orthographic) {
needsUpdateSsaoBlur = true;
ValueCell.update(this.ssaoBlurFirstPassRenderable.values.dOrthographic, orthographic);
ValueCell.update(this.ssaoBlurSecondPassRenderable.values.dOrthographic, orthographic);
}
ValueCell.updateIfChanged(this.ssaoBlurFirstPassRenderable.values.dOrthographic, orthographic);
ValueCell.updateIfChanged(this.ssaoBlurSecondPassRenderable.values.dOrthographic, orthographic);
if (this.nSamples !== props.occlusion.params.samples) {
needsUpdateSsao = true;
@@ -567,8 +561,8 @@ export class PostprocessingPass {
ValueCell.update(this.ssaoBlurFirstPassRenderable.values.uKernel, kernel);
ValueCell.update(this.ssaoBlurSecondPassRenderable.values.uKernel, kernel);
ValueCell.updateIfChanged(this.ssaoBlurFirstPassRenderable.values.dOcclusionKernelSize, this.blurKernelSize);
ValueCell.updateIfChanged(this.ssaoBlurSecondPassRenderable.values.dOcclusionKernelSize, this.blurKernelSize);
ValueCell.update(this.ssaoBlurFirstPassRenderable.values.dOcclusionKernelSize, this.blurKernelSize);
ValueCell.update(this.ssaoBlurSecondPassRenderable.values.dOcclusionKernelSize, this.blurKernelSize);
}
if (this.downsampleFactor !== props.occlusion.params.resolutionScale) {
@@ -595,6 +589,8 @@ export class PostprocessingPass {
ValueCell.update(this.ssaoBlurFirstPassRenderable.values.uTexSize, Vec2.set(this.ssaoBlurFirstPassRenderable.values.uTexSize.ref.value, sw, sh));
ValueCell.update(this.ssaoBlurSecondPassRenderable.values.uTexSize, Vec2.set(this.ssaoBlurSecondPassRenderable.values.uTexSize.ref.value, sw, sh));
}
ValueCell.update(this.renderable.values.uOcclusionColor, Color.toVec3Normalized(this.renderable.values.uOcclusionColor.ref.value, props.occlusion.params.color));
}
if (props.shadow.name === 'on') {
@@ -611,7 +607,10 @@ export class PostprocessingPass {
ValueCell.updateIfChanged(this.shadowsRenderable.values.uNear, camera.near);
ValueCell.updateIfChanged(this.shadowsRenderable.values.uFar, camera.far);
ValueCell.updateIfChanged(this.shadowsRenderable.values.dOrthographic, orthographic);
if (this.shadowsRenderable.values.dOrthographic.ref.value !== orthographic) {
ValueCell.update(this.shadowsRenderable.values.dOrthographic, orthographic);
needsUpdateShadows = true;
}
ValueCell.updateIfChanged(this.shadowsRenderable.values.uMaxDistance, props.shadow.params.maxDistance);
ValueCell.updateIfChanged(this.shadowsRenderable.values.uTolerance, props.shadow.params.tolerance);
@@ -630,30 +629,33 @@ export class PostprocessingPass {
}
if (props.outline.name === 'on') {
let { threshold, includeTransparent } = props.outline.params;
const transparentOutline = includeTransparent ?? true;
// orthographic needs lower threshold
if (camera.state.mode === 'orthographic') threshold /= 5;
const factor = Math.pow(1000, threshold / 10) / 1000;
// use radiusMax for stable outlines when zooming
const maxPossibleViewZDiff = factor * camera.state.radiusMax;
const transparentOutline = props.outline.params.includeTransparent ?? true;
const outlineScale = props.outline.params.scale - 1;
const outlineThreshold = 50 * props.outline.params.threshold;
ValueCell.updateIfChanged(this.outlinesRenderable.values.uNear, camera.near);
ValueCell.updateIfChanged(this.outlinesRenderable.values.uFar, camera.far);
ValueCell.updateIfChanged(this.outlinesRenderable.values.uMaxPossibleViewZDiff, maxPossibleViewZDiff);
if (this.renderable.values.dTransparentOutline.ref.value !== transparentOutline) { needsUpdateOutlines = true; }
ValueCell.updateIfChanged(this.outlinesRenderable.values.dTransparentOutline, transparentOutline);
ValueCell.update(this.outlinesRenderable.values.uInvProjection, invProjection);
if (this.outlinesRenderable.values.dTransparentOutline.ref.value !== transparentOutline) {
needsUpdateOutlines = true;
ValueCell.update(this.outlinesRenderable.values.dTransparentOutline, transparentOutline);
}
if (this.outlinesRenderable.values.dOrthographic.ref.value !== orthographic) {
needsUpdateOutlines = true;
ValueCell.update(this.outlinesRenderable.values.dOrthographic, orthographic);
}
ValueCell.updateIfChanged(this.outlinesRenderable.values.uOutlineThreshold, outlineThreshold);
ValueCell.update(this.renderable.values.uOutlineColor, Color.toVec3Normalized(this.renderable.values.uOutlineColor.ref.value, props.outline.params.color));
ValueCell.updateIfChanged(this.renderable.values.uMaxPossibleViewZDiff, maxPossibleViewZDiff);
ValueCell.update(this.renderable.values.uInvProjection, invProjection);
if (this.renderable.values.dOutlineScale.ref.value !== outlineScale) { needsUpdateMain = true; }
ValueCell.updateIfChanged(this.renderable.values.dOutlineScale, outlineScale);
if (this.renderable.values.dTransparentOutline.ref.value !== transparentOutline) { needsUpdateMain = true; }
ValueCell.updateIfChanged(this.renderable.values.dTransparentOutline, transparentOutline);
if (this.renderable.values.dOutlineScale.ref.value !== outlineScale) {
needsUpdateMain = true;
ValueCell.update(this.renderable.values.dOutlineScale, outlineScale);
}
if (this.renderable.values.dTransparentOutline.ref.value !== transparentOutline) {
needsUpdateMain = true;
ValueCell.update(this.renderable.values.dTransparentOutline, transparentOutline);
}
}
ValueCell.updateIfChanged(this.renderable.values.uFar, camera.far);
@@ -662,15 +664,23 @@ export class PostprocessingPass {
ValueCell.updateIfChanged(this.renderable.values.uFogNear, camera.fogNear);
ValueCell.update(this.renderable.values.uFogColor, Color.toVec3Normalized(this.renderable.values.uFogColor.ref.value, backgroundColor));
ValueCell.updateIfChanged(this.renderable.values.uTransparentBackground, transparentBackground);
if (this.renderable.values.dOrthographic.ref.value !== orthographic) { needsUpdateMain = true; }
ValueCell.updateIfChanged(this.renderable.values.dOrthographic, orthographic);
if (this.renderable.values.dOrthographic.ref.value !== orthographic) {
needsUpdateMain = true;
ValueCell.update(this.renderable.values.dOrthographic, orthographic);
}
if (this.renderable.values.dOutlineEnable.ref.value !== outlinesEnabled) { needsUpdateMain = true; }
ValueCell.updateIfChanged(this.renderable.values.dOutlineEnable, outlinesEnabled);
if (this.renderable.values.dShadowEnable.ref.value !== shadowsEnabled) { needsUpdateMain = true; }
ValueCell.updateIfChanged(this.renderable.values.dShadowEnable, shadowsEnabled);
if (this.renderable.values.dOcclusionEnable.ref.value !== occlusionEnabled) { needsUpdateMain = true; }
ValueCell.updateIfChanged(this.renderable.values.dOcclusionEnable, occlusionEnabled);
if (this.renderable.values.dOutlineEnable.ref.value !== outlinesEnabled) {
needsUpdateMain = true;
ValueCell.update(this.renderable.values.dOutlineEnable, outlinesEnabled);
}
if (this.renderable.values.dShadowEnable.ref.value !== shadowsEnabled) {
needsUpdateMain = true;
ValueCell.update(this.renderable.values.dShadowEnable, shadowsEnabled);
}
if (this.renderable.values.dOcclusionEnable.ref.value !== occlusionEnabled) {
needsUpdateMain = true;
ValueCell.update(this.renderable.values.dOcclusionEnable, occlusionEnabled);
}
if (needsUpdateOutlines) {
this.outlinesRenderable.update();
@@ -699,10 +709,6 @@ export class PostprocessingPass {
state.disable(gl.BLEND);
state.disable(gl.DEPTH_TEST);
state.depthMask(false);
const { x, y, width, height } = camera.viewport;
state.viewport(x, y, width, height);
state.scissor(x, y, width, height);
}
private occlusionOffset: [x: number, y: number] = [0, 0];
@@ -721,20 +727,21 @@ export class PostprocessingPass {
if (isTimingMode) this.webgl.timer.mark('PostprocessingPass.render');
this.updateState(camera, transparentBackground, backgroundColor, props, light);
if (props.outline.name === 'on') {
this.outlinesTarget.bind();
this.outlinesRenderable.render();
}
if (props.shadow.name === 'on') {
this.shadowsTarget.bind();
this.shadowsRenderable.render();
}
const { gl, state } = this.webgl;
const { x, y, width, height } = camera.viewport;
// don't render occlusion if offset is given,
// which will reuse the existing occlusion
if (props.occlusion.name === 'on' && this.occlusionOffset[0] === 0 && this.occlusionOffset[1] === 0) {
if (isTimingMode) this.webgl.timer.mark('SSAO.render');
const sx = Math.floor(x * this.ssaoScale);
const sy = Math.floor(y * this.ssaoScale);
const sw = Math.ceil(width * this.ssaoScale);
const sh = Math.ceil(height * this.ssaoScale);
state.viewport(sx, sy, sw, sh);
state.scissor(sx, sy, sw, sh);
if (this.ssaoScale < 1) {
this.downsampledDepthTarget.bind();
this.downsampleDepthRenderable.render();
@@ -751,14 +758,25 @@ export class PostprocessingPass {
if (isTimingMode) this.webgl.timer.markEnd('SSAO.render');
}
state.viewport(x, y, width, height);
state.scissor(x, y, width, height);
if (props.outline.name === 'on') {
this.outlinesTarget.bind();
this.outlinesRenderable.render();
}
if (props.shadow.name === 'on') {
this.shadowsTarget.bind();
this.shadowsRenderable.render();
}
if (toDrawingBuffer) {
this.webgl.unbindFramebuffer();
} else {
this.target.bind();
}
const { gl, state } = this.webgl;
this.background.update(camera, props.background);
if (this.background.isEnabled(props.background)) {
if (this.transparentBackground) {

View File

@@ -16,7 +16,7 @@ const c = {
MAX_TEXTURE_MAX_ANISOTROPY_EXT: 0x84FF,
MAX_TEXTURE_IMAGE_UNITS_NV: 0x8872
};
} as const;
const gl = {
ACTIVE_ATTRIBUTES: 35721,
@@ -316,7 +316,7 @@ const gl = {
VERTEX_SHADER: 35633,
VIEWPORT: 2978,
ZERO: 0
};
} as const;
type gl = typeof gl
export function createGl(width: number, height: number, contextAttributes: WebGLContextAttributes): WebGLRenderingContext {
@@ -371,66 +371,66 @@ export function createGl(width: number, height: number, contextAttributes: WebGL
case 'EXT_blend_minmax': return {
MAX_EXT: 0,
MIN_EXT: 0
} as EXT_blend_minmax;
} as unknown as EXT_blend_minmax;
case 'EXT_texture_filter_anisotropic': return {
MAX_TEXTURE_MAX_ANISOTROPY_EXT: 0,
TEXTURE_MAX_ANISOTROPY_EXT: 0
} as EXT_texture_filter_anisotropic;
} as unknown as EXT_texture_filter_anisotropic;
case 'EXT_frag_depth': return {} as EXT_frag_depth;
case 'EXT_shader_texture_lod': return {} as EXT_shader_texture_lod;
case 'EXT_shader_texture_lod': return {} as unknown as EXT_shader_texture_lod;
case 'EXT_sRGB': return {
FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT: 0,
SRGB8_ALPHA8_EXT: 0,
SRGB_ALPHA_EXT: 0,
SRGB_EXT: 0
} as EXT_sRGB;
} as unknown as EXT_sRGB;
case 'OES_vertex_array_object': return {
VERTEX_ARRAY_BINDING_OES: 0,
bindVertexArrayOES: function (arrayObject: WebGLVertexArrayObjectOES) { },
createVertexArrayOES: function (): WebGLVertexArrayObjectOES { return {}; },
deleteVertexArrayOES: function (arrayObject: WebGLVertexArrayObjectOES) { },
isVertexArrayOES: function (value: any) { return true; }
} as OES_vertex_array_object;
} as unknown as OES_vertex_array_object;
case 'WEBGL_color_buffer_float': return {
FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT: 0,
RGB32F_EXT: 0,
RGBA32F_EXT: 0,
UNSIGNED_NORMALIZED_EXT: 0
} as WEBGL_color_buffer_float;
} as unknown as WEBGL_color_buffer_float;
case 'WEBGL_compressed_texture_astc': return null;
case 'WEBGL_compressed_texture_s3tc_srgb': return null;
case 'WEBGL_debug_shaders': return {
getTranslatedShaderSource(shader: WebGLShader) { return ''; }
} as WEBGL_debug_shaders;
} as unknown as WEBGL_debug_shaders;
case 'WEBGL_draw_buffers': return null;
case 'WEBGL_lose_context': return {
loseContext: function () { },
restoreContext: function () { },
} as WEBGL_lose_context;
} as unknown as WEBGL_lose_context;
case 'WEBGL_depth_texture': return {
UNSIGNED_INT_24_8_WEBGL: 0
} as WEBGL_depth_texture;
} as unknown as WEBGL_depth_texture;
case 'WEBGL_debug_renderer_info': return {
UNMASKED_RENDERER_WEBGL: 0,
UNMASKED_VENDOR_WEBGL: 0
} as WEBGL_debug_renderer_info;
} as unknown as WEBGL_debug_renderer_info;
case 'WEBGL_compressed_texture_s3tc': return null;
case 'OES_texture_half_float_linear': return {} as OES_texture_half_float_linear;
case 'OES_texture_half_float_linear': return {} as unknown as OES_texture_half_float_linear;
case 'OES_texture_half_float': return {
HALF_FLOAT_OES: 0
} as OES_texture_half_float;
case 'OES_texture_float_linear': return {} as OES_texture_float_linear;
case 'OES_texture_float': return {} as OES_texture_float;
} as unknown as OES_texture_half_float;
case 'OES_texture_float_linear': return {} as unknown as OES_texture_float_linear;
case 'OES_texture_float': return {} as unknown as OES_texture_float;
case 'OES_standard_derivatives': return {
FRAGMENT_SHADER_DERIVATIVE_HINT_OES: 0
} as OES_standard_derivatives;
case 'OES_element_index_uint': return {} as OES_element_index_uint;
} as unknown as OES_standard_derivatives;
case 'OES_element_index_uint': return {} as unknown as OES_element_index_uint;
case 'ANGLE_instanced_arrays': return {
drawArraysInstancedANGLE: function (mode: number, first: number, count: number, primcount: number) {},
drawElementsInstancedANGLE: function (mode: number, count: number, type: number, offset: number, primcount: number) {},
vertexAttribDivisorANGLE: function (index: number, divisor: number) {},
VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE: 0
} as ANGLE_instanced_arrays;
} as unknown as ANGLE_instanced_arrays;
}
return null;
},

View File

@@ -160,6 +160,7 @@ export const GlobalUniformSchema = {
uMarkerAverage: UniformSpec('f'),
uXrayEdgeFalloff: UniformSpec('f'),
uExposure: UniformSpec('f'),
uRenderMask: UniformSpec('i'),
uMarkingDepthTest: UniformSpec('b'),

View File

@@ -104,6 +104,7 @@ export const RendererParams = {
markerPriority: PD.Select(1, [[1, 'Highlight'], [2, 'Select']]),
xrayEdgeFalloff: PD.Numeric(1, { min: 0.0, max: 3.0, step: 0.1 }),
exposure: PD.Numeric(1, { min: 0.0, max: 3.0, step: 0.01 }),
light: PD.ObjectList({
inclination: PD.Numeric(150, { min: 0, max: 180, step: 1 }),
@@ -242,6 +243,7 @@ namespace Renderer {
uMarkerAverage: ValueCell.create(0),
uXrayEdgeFalloff: ValueCell.create(p.xrayEdgeFalloff),
uExposure: ValueCell.create(p.exposure),
};
const globalUniformList = Object.entries(globalUniforms);
@@ -607,7 +609,7 @@ namespace Renderer {
// TODO: simplify, handle in renderable.state???
// uAlpha is updated in "render" so we need to recompute it here
const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
if (alpha < 1 || r.values.transparencyAverage.ref.value > 0 || r.values.dGeometryType.ref.value === 'directVolume' || r.values.dPointStyle?.ref.value === 'fuzzy' || r.values.dGeometryType.ref.value === 'text' || r.values.dXrayShaded?.ref.value) {
if ((alpha < 1 && alpha !== 0) || r.values.transparencyAverage.ref.value > 0 || r.values.dGeometryType.ref.value === 'directVolume' || r.values.dPointStyle?.ref.value === 'fuzzy' || r.values.dGeometryType.ref.value === 'text' || r.values.dXrayShaded?.ref.value) {
renderObject(r, 'colorWboit', Flag.None);
}
}
@@ -655,7 +657,7 @@ namespace Renderer {
// TODO: simplify, handle in renderable.state???
// uAlpha is updated in "render" so we need to recompute it here
const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
if (alpha < 1 || r.values.transparencyAverage.ref.value > 0 || r.values.dPointStyle?.ref.value === 'fuzzy' || !!r.values.uBackgroundColor || r.values.dXrayShaded?.ref.value) {
if ((alpha < 1 && alpha !== 0) || r.values.transparencyAverage.ref.value > 0 || r.values.dPointStyle?.ref.value === 'fuzzy' || r.values.dGeometryType.ref.value === 'text' || r.values.dXrayShaded?.ref.value) {
renderObject(r, 'colorDpoit', Flag.None);
}
}
@@ -787,6 +789,10 @@ namespace Renderer {
p.xrayEdgeFalloff = props.xrayEdgeFalloff;
ValueCell.update(globalUniforms.uXrayEdgeFalloff, p.xrayEdgeFalloff);
}
if (props.exposure !== undefined && props.exposure !== p.exposure) {
p.exposure = props.exposure;
ValueCell.update(globalUniforms.uExposure, p.exposure);
}
if (props.light !== undefined && !deepEqual(props.light, p.light)) {
p.light = props.light;

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*
@@ -65,4 +65,6 @@ export const apply_light_color = `
#ifdef dXrayShaded
gl_FragColor.a *= 1.0 - pow(abs(dot(normal, vec3(0.0, 0.0, 1.0))), uXrayEdgeFalloff);
#endif
gl_FragColor.rgb *= uExposure;
`;

View File

@@ -72,6 +72,7 @@ uniform vec3 uInteriorColor;
bool interior;
uniform float uXrayEdgeFalloff;
uniform float uExposure;
uniform mat4 uProjection;

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -71,8 +71,10 @@ void main() {
vViewPosition = mvPosition.xyz;
gl_Position = uProjection * mvPosition;
mvPosition.z -= 2.0 * (length(vEnd - vStart) + vSize); // avoid clipping
gl_Position.z = (uProjection * mvPosition).z;
if (gl_Position.z < -gl_Position.w) {
mvPosition.z -= 2.0 * (length(vEnd - vStart) + vSize); // avoid clipping
gl_Position.z = (uProjection * mvPosition).z;
}
#include clip_instance
}

View File

@@ -75,6 +75,7 @@ uniform vec3 uFogColor;
uniform float uAlpha;
uniform bool uTransparentBackground;
uniform float uXrayEdgeFalloff;
uniform float uExposure;
uniform int uRenderMask;

View File

@@ -16,8 +16,9 @@ uniform vec2 uTexSize;
uniform float uNear;
uniform float uFar;
uniform mat4 uInvProjection;
uniform float uMaxPossibleViewZDiff;
uniform float uOutlineThreshold;
#include common
@@ -49,17 +50,25 @@ bool isBackground(const in float depth) {
return depth == 1.0;
}
float getPixelSize(const in vec2 coords, const in float depth) {
vec3 viewPos0 = screenSpaceToViewSpace(vec3(coords, depth), uInvProjection);
vec3 viewPos1 = screenSpaceToViewSpace(vec3(coords + vec2(1.0, 0.0) / uTexSize, depth), uInvProjection);
return distance(viewPos0, viewPos1);
}
void main(void) {
float backgroundViewZ = uFar + 3.0 * uMaxPossibleViewZDiff;
float backgroundViewZ = 2.0 * uFar;
vec2 coords = gl_FragCoord.xy / uTexSize;
vec2 invTexSize = 1.0 / uTexSize;
float selfDepthOpaque = getDepthOpaque(coords);
float selfViewZOpaque = isBackground(selfDepthOpaque) ? backgroundViewZ : getViewZ(selfDepthOpaque);
float pixelSizeOpaque = getPixelSize(coords, selfDepthOpaque) * uOutlineThreshold;
float selfDepthTransparent = getDepthTransparent(coords);
float selfViewZTransparent = isBackground(selfDepthTransparent) ? backgroundViewZ : getViewZ(selfDepthTransparent);
float pixelSizeTransparent = getPixelSize(coords, selfDepthTransparent) * uOutlineThreshold;
float outline = 1.0;
float bestDepth = 1.0;
@@ -73,14 +82,14 @@ void main(void) {
float sampleDepthTransparent = getDepthTransparent(sampleCoords);
float sampleViewZOpaque = isBackground(sampleDepthOpaque) ? backgroundViewZ : getViewZ(sampleDepthOpaque);
if (abs(selfViewZOpaque - sampleViewZOpaque) > uMaxPossibleViewZDiff && selfDepthOpaque > sampleDepthOpaque && sampleDepthOpaque <= bestDepth) {
if (abs(selfViewZOpaque - sampleViewZOpaque) > pixelSizeOpaque && selfDepthOpaque > sampleDepthOpaque && sampleDepthOpaque <= bestDepth) {
outline = 0.0;
bestDepth = sampleDepthOpaque;
}
if (sampleDepthTransparent < sampleDepthOpaque) {
float sampleViewZTransparent = isBackground(sampleDepthTransparent) ? backgroundViewZ : getViewZ(sampleDepthTransparent);
if (abs(selfViewZTransparent - sampleViewZTransparent) > uMaxPossibleViewZDiff && selfDepthTransparent > sampleDepthTransparent && sampleDepthTransparent <= bestDepth) {
if (abs(selfViewZTransparent - sampleViewZTransparent) > pixelSizeTransparent && selfDepthTransparent > sampleDepthTransparent && sampleDepthTransparent <= bestDepth) {
outline = 0.0;
bestDepth = sampleDepthTransparent;
transparentFlag = 1.0;

View File

@@ -24,16 +24,10 @@ uniform float uFogNear;
uniform float uFogFar;
uniform vec3 uFogColor;
uniform vec3 uOutlineColor;
uniform vec3 uOcclusionColor;
uniform bool uTransparentBackground;
uniform vec2 uOcclusionOffset;
uniform float uMaxPossibleViewZDiff;
uniform mat4 uInvProjection;
const float outlineDistanceFactor = 5.0;
const vec3 occlusionColor = vec3(0.0);
#include common
float getViewZ(const in float depth) {
@@ -64,21 +58,14 @@ bool isBackground(const in float depth) {
return depth == 1.0;
}
float getPixelSize(const in vec2 coords, const in float depth) {
vec3 viewPos0 = screenSpaceToViewSpace(vec3(coords, depth), uInvProjection);
vec3 viewPos1 = screenSpaceToViewSpace(vec3(coords + vec2(1.0, 0.0) / uTexSize, depth), uInvProjection);
return distance(viewPos0, viewPos1);
}
float getOutline(const in vec2 coords, const in float opaqueDepth, out float closestTexel) {
float backgroundViewZ = uFar + 3.0 * uMaxPossibleViewZDiff;
float backgroundViewZ = 2.0 * uFar;
vec2 invTexSize = 1.0 / uTexSize;
float transparentDepth = getDepthTransparent(coords);
float opaqueSelfViewZ = isBackground(opaqueDepth) ? backgroundViewZ : getViewZ(opaqueDepth);
float transparentSelfViewZ = isBackground(transparentDepth) ? backgroundViewZ : getViewZ(transparentDepth);
float selfDepth = min(opaqueDepth, transparentDepth);
float pixelSize = getPixelSize(coords, selfDepth);
float outline = 1.0;
closestTexel = 1.0;
@@ -96,7 +83,7 @@ float getOutline(const in vec2 coords, const in float opaqueDepth, out float clo
float sampleOutlineViewZ = isBackground(sampleOutlineDepth) ? backgroundViewZ : getViewZ(sampleOutlineDepth);
float selfViewZ = sampleOutlineCombined.a == 0.0 ? opaqueSelfViewZ : transparentSelfViewZ;
if (sampleOutline == 0.0 && sampleOutlineDepth < closestTexel && abs(selfViewZ - sampleOutlineViewZ) > uMaxPossibleViewZDiff + (pixelSize * outlineDistanceFactor)) {
if (sampleOutline == 0.0 && sampleOutlineDepth < closestTexel) {
outline = 0.0;
closestTexel = sampleOutlineDepth;
}
@@ -130,9 +117,9 @@ void main(void) {
fogFactor = smoothstep(uFogNear, uFogFar, viewDist);
float occlusionFactor = getSsao(coords + uOcclusionOffset);
if (!uTransparentBackground) {
color.rgb = mix(mix(occlusionColor, uFogColor, fogFactor), color.rgb, occlusionFactor);
color.rgb = mix(mix(uOcclusionColor, uFogColor, fogFactor), color.rgb, occlusionFactor);
} else {
color.rgb = mix(occlusionColor * (1.0 - fogFactor), color.rgb, occlusionFactor);
color.rgb = mix(uOcclusionColor * (1.0 - fogFactor), color.rgb, occlusionFactor);
}
}
#endif

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -96,8 +96,10 @@ void main(void){
vModelPosition = (uModel * aTransform * position4).xyz; // for clipping in frag shader
mvPosition.z -= 2.0 * vRadius; // avoid clipping
gl_Position.z = (uProjection * vec4(mvPosition.xyz, 1.0)).z;
if (gl_Position.z < -gl_Position.w) {
mvPosition.z -= 2.0 * vRadius; // avoid clipping
gl_Position.z = (uProjection * vec4(mvPosition.xyz, 1.0)).z;
}
#include clip_instance
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -19,8 +19,7 @@ uniform float uKernel[dOcclusionKernelSize];
uniform float uBlurDirectionX;
uniform float uBlurDirectionY;
uniform float uMaxPossibleViewZDiff;
uniform mat4 uInvProjection;
uniform float uNear;
uniform float uFar;
@@ -42,6 +41,12 @@ bool outsideBounds(const in vec2 p) {
return p.x < uBounds.x || p.y < uBounds.y || p.x > uBounds.z || p.y > uBounds.w;
}
float getPixelSize(const in vec2 coords, const in float depth) {
vec3 viewPos0 = screenSpaceToViewSpace(vec3(coords, depth), uInvProjection);
vec3 viewPos1 = screenSpaceToViewSpace(vec3(coords + vec2(1.0, 0.0) / uTexSize, depth), uInvProjection);
return distance(viewPos0, viewPos1);
}
void main(void) {
vec2 coords = gl_FragCoord.xy / uTexSize;
@@ -60,6 +65,8 @@ void main(void) {
}
float selfViewZ = getViewZ(selfDepth);
float pixelSize = getPixelSize(coords, selfDepth);
float maxDiffViewZ = pixelSize * 10.0;
vec2 offset = vec2(uBlurDirectionX, uBlurDirectionY) / uTexSize;
@@ -67,6 +74,8 @@ void main(void) {
float kernelSum = 0.0;
// only if kernelSize is odd
for (int i = -dOcclusionKernelSize / 2; i <= dOcclusionKernelSize / 2; i++) {
if (abs(float(i)) > 1.0 && abs(float(i)) * pixelSize > 0.8) continue;
vec2 sampleCoords = coords + float(i) * offset;
if (outsideBounds(sampleCoords)) {
continue;
@@ -79,9 +88,9 @@ void main(void) {
continue;
}
if (abs(float(i)) > 1.0) { // abs is not defined for int in webgl1
if (abs(float(i)) > 1.0) {
float sampleViewZ = getViewZ(sampleDepth);
if (abs(selfViewZ - sampleViewZ) > uMaxPossibleViewZDiff) {
if (abs(selfViewZ - sampleViewZ) > maxDiffViewZ) {
continue;
}
}

View File

@@ -27,7 +27,7 @@ namespace PrincipalAxes {
export function calculateMomentsAxes(positions: NumberArray): Axes3D {
if (positions.length === 3) {
return Axes3D.create(Vec3.fromArray(Vec3(), positions, 0), Vec3.create(1, 0, 0), Vec3.create(0, 1, 0), Vec3.create(0, 1, 0));
return Axes3D.create(Vec3.fromArray(Vec3(), positions, 0), Vec3.create(1, 0, 0), Vec3.create(0, 1, 0), Vec3.create(0, 0, 1));
}
const points = Matrix.fromArray(positions, 3, positions.length / 3);
@@ -143,4 +143,4 @@ namespace PrincipalAxes {
return Axes3D.create(origin, dirA, dirB, dirC);
}
}
}

View File

@@ -282,7 +282,7 @@ const ParseCif = PluginStateTransform.BuiltIn({
})({
apply({ a }) {
return Task.create('Parse CIF', async ctx => {
const parsed = await (SO.Data.String.is(a) ? CIF.parse(a.data) : CIF.parseBinary(a.data)).runInContext(ctx);
const parsed = await (typeof a.data === 'string' ? CIF.parse(a.data) : CIF.parseBinary(a.data)).runInContext(ctx);
if (parsed.isError) throw new Error(parsed.message);
return new SO.Format.Cif(parsed.result);
});

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -240,14 +240,9 @@ export class SelectionViewportControls extends PluginUIComponent {
this.subscribe(this.plugin.behaviors.interaction.selectionMode, () => this.forceUpdate());
}
onMouseMove = (e: React.MouseEvent) => {
// ignore mouse moves when no button is held
if (e.buttons === 0) e.stopPropagation();
};
render() {
if (!this.plugin.selectionMode) return null;
return <div className='msp-selection-viewport-controls' onMouseMove={this.onMouseMove}>
return <div className='msp-selection-viewport-controls'>
<StructureSelectionActionsControls />
</div>;
}

View File

@@ -626,9 +626,9 @@ export class SliderBase extends React.Component<SliderBaseProps, SliderBaseState
const value = bounds[handle];
let direction = 0;
if (bounds[handle + 1] - value < threshold!) {
if (bounds[handle + 1] - value < +threshold!) {
direction = +1;
} else if (value - bounds[handle - 1] < threshold!) {
} else if (value - bounds[handle - 1] < +threshold!) {
direction = -1;
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2022-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -60,7 +60,7 @@ export class QuickStyles extends PurePluginUIComponent {
},
occlusion: {
name: 'on',
params: { bias: 0.8, blurKernelSize: 15, radius: 5, samples: 32, resolutionScale: 1 }
params: { bias: 0.8, blurKernelSize: 15, radius: 5, samples: 32, resolutionScale: 1, color: Color(0x000000) }
},
shadow: { name: 'off', params: {} },
}
@@ -85,7 +85,7 @@ export class QuickStyles extends PurePluginUIComponent {
name: 'on',
params: pp.occlusion.name === 'on'
? pp.occlusion.params
: { bias: 0.8, blurKernelSize: 15, radius: 5, samples: 32, resolutionScale: 1 }
: { bias: 0.8, blurKernelSize: 15, radius: 5, samples: 32, resolutionScale: 1, color: Color(0x000000) }
},
shadow: { name: 'off', params: {} },
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2023 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>
@@ -76,13 +76,8 @@ export class ViewportControls extends PluginUIComponent<ViewportControlsProps, V
return <IconButton svg={icon} toggleState={isOn} onClick={onClick} title={title} style={{ background: 'transparent' }} />;
}
onMouseMove = (e: React.MouseEvent) => {
// ignore mouse moves when no button is held
if (e.buttons === 0) e.stopPropagation();
};
render() {
return <div className={'msp-viewport-controls'} onMouseMove={this.onMouseMove}>
return <div className={'msp-viewport-controls'}>
<div className='msp-viewport-controls-buttons'>
<div>
<div className='msp-semi-transparent-background' />

View File

@@ -362,6 +362,7 @@ export class PluginContext {
}
this.subs = [];
this.animationLoop.stop();
this.commands.dispose();
this.canvas3d?.dispose();
this.canvas3dContext?.dispose(options);

View File

@@ -210,6 +210,7 @@ export const STYLIZED_POSTPROCESSING: Partial<PostprocessingProps> = {
bias: 0.8,
blurKernelSize: 15,
resolutionScale: 1,
color: ColorNames.black,
}
}, outline: {
name: 'on' as const, params: {

View File

@@ -119,7 +119,7 @@ class ViewportScreenshotHelper extends PluginComponent {
postprocessing: {
...c.props.postprocessing,
occlusion: aoProps.name === 'on'
? { name: 'on', params: { ...aoProps.params, samples: 128, resolutionScale: 1 } }
? { name: 'on', params: { ...aoProps.params, samples: 128, resolutionScale: c.webgl.pixelRatio } }
: aoProps
},
marking: { ...c.props.marking }
@@ -143,7 +143,7 @@ class ViewportScreenshotHelper extends PluginComponent {
postprocessing: {
...c.props.postprocessing,
occlusion: aoProps.name === 'on'
? { name: 'on', params: { ...aoProps.params, samples: 128, resolutionScale: 1 } }
? { name: 'on', params: { ...aoProps.params, samples: 128, resolutionScale: c.webgl.pixelRatio } }
: aoProps
},
marking: { ...c.props.marking }

View File

@@ -218,6 +218,7 @@ export function InterUnitBondCylinderImpostorVisual(materialId: number): Complex
eachLocation: eachInterBond,
setUpdateState: (state: VisualUpdateState, newProps: PD.Values<InterUnitBondCylinderParams>, currentProps: PD.Values<InterUnitBondCylinderParams>, newTheme: Theme, currentTheme: Theme, newStructure: Structure, currentStructure: Structure) => {
state.createGeometry = (
newProps.sizeFactor !== currentProps.sizeFactor ||
newProps.sizeAspectRatio !== currentProps.sizeAspectRatio ||
newProps.linkScale !== currentProps.linkScale ||
newProps.linkSpacing !== currentProps.linkSpacing ||

View File

@@ -235,6 +235,7 @@ export function IntraUnitBondCylinderImpostorVisual(materialId: number): UnitsVi
eachLocation: eachIntraBond,
setUpdateState: (state: VisualUpdateState, newProps: PD.Values<IntraUnitBondCylinderParams>, currentProps: PD.Values<IntraUnitBondCylinderParams>, newTheme: Theme, currentTheme: Theme, newStructureGroup: StructureGroup, currentStructureGroup: StructureGroup) => {
state.createGeometry = (
newProps.sizeFactor !== currentProps.sizeFactor ||
newProps.sizeAspectRatio !== currentProps.sizeAspectRatio ||
newProps.linkScale !== currentProps.linkScale ||
newProps.linkSpacing !== currentProps.linkSpacing ||

View File

@@ -2,6 +2,7 @@
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Zhenyu Zhang <jump2cn@gmail.com>
*/
import { Vec3 } from '../../../../mol-math/linear-algebra';
@@ -25,7 +26,7 @@ export const LinkCylinderParams = {
aromaticScale: PD.Numeric(0.3, { min: 0, max: 1, step: 0.01 }),
aromaticSpacing: PD.Numeric(1.5, { min: 0, max: 3, step: 0.01 }),
aromaticDashCount: PD.Numeric(2, { min: 2, max: 6, step: 2 }),
dashCount: PD.Numeric(4, { min: 2, max: 10, step: 2 }),
dashCount: PD.Numeric(4, { min: 0, max: 10, step: 2 }),
dashScale: PD.Numeric(0.8, { min: 0, max: 2, step: 0.1 }),
dashCap: PD.Boolean(true),
stubCap: PD.Boolean(true),
@@ -38,7 +39,7 @@ export const LinkLineParams = {
linkScale: PD.Numeric(0.5, { min: 0, max: 1, step: 0.1 }),
linkSpacing: PD.Numeric(0.1, { min: 0, max: 2, step: 0.01 }),
aromaticDashCount: PD.Numeric(2, { min: 2, max: 6, step: 2 }),
dashCount: PD.Numeric(4, { min: 2, max: 10, step: 2 }),
dashCount: PD.Numeric(4, { min: 0, max: 10, step: 2 }),
};
export const DefaultLinkLineProps = PD.getDefaultValues(LinkLineParams);
export type LinkLineProps = typeof DefaultLinkLineProps
@@ -164,7 +165,11 @@ export function createLinkCylinderMesh(ctx: VisualContext, linkBuilder: LinkBuil
cylinderProps.radiusTop = cylinderProps.radiusBottom = linkRadius * dashScale;
cylinderProps.topCap = cylinderProps.bottomCap = dashCap;
addFixedCountDashedCylinder(builderState, va, vb, 0.5, segmentCount, cylinderProps);
if (segmentCount > 1) {
addFixedCountDashedCylinder(builderState, va, vb, 0.5, segmentCount, cylinderProps);
} else {
addCylinder(builderState, va, vb, 0.5, cylinderProps);
}
} else if (linkStyle === LinkStyle.Double || linkStyle === LinkStyle.OffsetDouble || linkStyle === LinkStyle.Triple || linkStyle === LinkStyle.OffsetTriple || linkStyle === LinkStyle.Aromatic || linkStyle === LinkStyle.MirroredAromatic) {
const order = (linkStyle === LinkStyle.Double || linkStyle === LinkStyle.OffsetDouble) ? 2 :
(linkStyle === LinkStyle.Triple || linkStyle === LinkStyle.OffsetTriple) ? 3 : 1.5;
@@ -303,9 +308,14 @@ export function createLinkCylinderImpostors(ctx: VisualContext, linkBuilder: Lin
v3scale(vm, v3add(vm, va, vb), 0.5);
builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], 1, linkCap, linkStub, edgeIndex);
} else if (linkStyle === LinkStyle.Dashed) {
v3scale(tmpV12, v3sub(tmpV12, vb, va), lengthScale);
v3sub(vb, vb, tmpV12);
builder.addFixedCountDashes(va, vb, segmentCount, dashScale, dashCap, dashCap, edgeIndex);
if (segmentCount > 1) {
v3scale(tmpV12, v3sub(tmpV12, vb, va), lengthScale);
v3sub(vb, vb, tmpV12);
builder.addFixedCountDashes(va, vb, segmentCount, dashScale, dashCap, dashCap, edgeIndex);
} else {
v3scale(vm, v3add(vm, va, vb), 0.5);
builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], dashScale, dashCap, dashCap, edgeIndex);
}
} else if (linkStyle === LinkStyle.Double || linkStyle === LinkStyle.OffsetDouble || linkStyle === LinkStyle.Triple || linkStyle === LinkStyle.OffsetTriple || linkStyle === LinkStyle.Aromatic || linkStyle === LinkStyle.MirroredAromatic) {
const order = (linkStyle === LinkStyle.Double || linkStyle === LinkStyle.OffsetDouble) ? 2 :
(linkStyle === LinkStyle.Triple || linkStyle === LinkStyle.OffsetTriple) ? 3 : 1.5;
@@ -423,9 +433,14 @@ export function createLinkLines(ctx: VisualContext, linkBuilder: LinkBuilderProp
v3scale(vm, v3add(vm, va, vb), 0.5);
builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], edgeIndex);
} else if (linkStyle === LinkStyle.Dashed) {
v3scale(tmpV12, v3sub(tmpV12, vb, va), lengthScale);
v3sub(vb, vb, tmpV12);
builder.addFixedCountDashes(va, vb, segmentCount, edgeIndex);
if (segmentCount > 1) {
v3scale(tmpV12, v3sub(tmpV12, vb, va), lengthScale);
v3sub(vb, vb, tmpV12);
builder.addFixedCountDashes(va, vb, segmentCount, edgeIndex);
} else {
v3scale(vm, v3add(vm, va, vb), 0.5);
builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], edgeIndex);
}
} else if (linkStyle === LinkStyle.Double || linkStyle === LinkStyle.OffsetDouble || linkStyle === LinkStyle.Triple || linkStyle === LinkStyle.OffsetTriple || linkStyle === LinkStyle.Aromatic || linkStyle === LinkStyle.MirroredAromatic) {
const order = linkStyle === LinkStyle.Double || linkStyle === LinkStyle.OffsetDouble ? 2 :
linkStyle === LinkStyle.Triple || linkStyle === LinkStyle.OffsetTriple ? 3 : 1.5;

View File

@@ -35,7 +35,7 @@ namespace StateObject {
export function create<Data, T extends Type>(type: T) {
return class O implements StateObject<Data, T> {
static type = type;
static is(obj?: StateObject): obj is O { return !!obj && type === obj.type; }
static is(obj?: StateObject): obj is StateObject<Data, T> { return !!obj && type === obj.type; }
id = UUID.create22();
type = type;
label: string;

View File

@@ -300,13 +300,17 @@ function ajaxGetInternal<T extends DataType>(title: string | undefined, url: str
});
}
// NOTE: lazy imports cannot be used here because WebPack complains since this
// is part of the "browser" build.
// NOTE: a workaround for using this in Node.js
let _fs: (typeof import ('fs')) | undefined = undefined;
function getFS() {
if (_fs) return _fs!;
_fs = require('fs');
return _fs!;
if (!_fs) {
throw new Error('When running in Node.js and reading from files, call mol-util/data-source\'s setFSModule function first.');
}
return _fs;
}
export function setFSModule(fs: typeof import ('fs')) {
_fs = fs;
}
/** Alternative implementation of ajaxGetInternal (because xhr2 does not support file:// protocol) */

View File

@@ -163,6 +163,9 @@ export type MoveInput = {
pageX: number,
pageY: number,
inside: boolean,
// Move is subscribed to window element
// This indicates that the event originated from the element the InputObserver was created on
onElement: boolean
} & BaseInput
export type PinchInput = {
@@ -202,6 +205,7 @@ type PointerEvent = {
clientY: number
pageX: number
pageY: number
target: EventTarget | null
preventDefault?: () => void
}
@@ -446,7 +450,8 @@ namespace InputObserver {
clientX: (t0.clientX + t1.clientX) / 2,
clientY: (t0.clientY + t1.clientY) / 2,
pageX: (t0.pageX + t1.pageX) / 2,
pageY: (t0.pageY + t1.pageY) / 2
pageY: (t0.pageY + t1.pageY) / 2,
target: ev.target
};
}
@@ -592,7 +597,7 @@ namespace InputObserver {
const { pageX, pageY } = ev;
const [x, y] = pointerEnd;
const inside = insideBounds(pointerEnd);
move.next({ x, y, pageX, pageY, buttons, button, modifiers: getModifierKeys(), inside });
move.next({ x, y, pageX, pageY, buttons, button, modifiers: getModifierKeys(), inside, onElement: ev.target === element });
if (dragging === DraggingState.Stopped) return;

View File

@@ -2,6 +2,7 @@
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Adam Midlik <midlik@gmail.com>
* @author Russell Parker <russell@benchling.com>
*
* Implements some browser-only global variables for Node.js environment.
* These workarounds will also work in browsers as usual.
@@ -19,7 +20,7 @@ export const File_ = getFile();
function getXMLHttpRequest(): typeof XMLHttpRequest {
if (typeof document === 'undefined') {
if (typeof XMLHttpRequest === 'undefined' || RUNNING_IN_NODEJS) {
return require('xhr2');
} else {
return XMLHttpRequest;
@@ -27,7 +28,7 @@ function getXMLHttpRequest(): typeof XMLHttpRequest {
}
function getFile(): typeof File {
if (typeof document === 'undefined') {
if (typeof File === 'undefined' || RUNNING_IN_NODEJS) {
class File_NodeJs implements File {
private readonly blob: Blob;
// Blob fields