diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 966b67347d36bc37436d3ba78525db8a5a4275fc..93c043ab7c334b348ea377d93146bf0506bebd6b 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -6,7 +6,6 @@ "recommendations": [ "dbaeumer.vscode-eslint", "firsttris.vscode-jest-runner", - "msjsdiag.debugger-for-chrome", "slevesque.shader", "stpn.vscode-graphql", "wayou.vscode-todo-highlight" diff --git a/CHANGELOG.md b/CHANGELOG.md index f84e15e008a90dfe4113daf23cdcc28070439560..392ab304cb54dfa7d57759179f5265f6cdc37460 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,16 @@ Note that since we don't clearly distinguish between a public and private interf ## [Unreleased] +- Add mipmap-based blur for skybox backgrounds + +## [v3.19.0] - 2022-10-01 + +- Fix "empty textures" error on empty canvas +- Optimize BinaryCIF integer packing encoder +- Fix dual depth peeling when post-processing is off or when rendering direct-volumes +- Add ``cameraClipping.minNear`` parameter +- Fix black artifacts on specular highlights with transparent background + ## [v3.18.0] - 2022-09-17 - Integration of Dual depth peeling - OIT method diff --git a/package-lock.json b/package-lock.json index bd0ed999031f54ab9c6e8f15b0328ce9ff3d3f0f..c01c64d911611417ee7fd174b9cdc513280f37e4 100644 Binary files a/package-lock.json and b/package-lock.json differ diff --git a/package.json b/package.json index a33ddcb41ffd541e0b8b57ad6598088fc1a30f85..e95f6e4d35cb37a90db21990dd0858cb5b54135f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "molstar", - "version": "3.18.0", + "version": "3.19.0", "description": "A comprehensive macromolecular library.", "homepage": "https://github.com/molstar/molstar#readme", "repository": { @@ -97,43 +97,43 @@ "license": "MIT", "devDependencies": { "@graphql-codegen/add": "^3.2.1", - "@graphql-codegen/cli": "^2.12.0", + "@graphql-codegen/cli": "^2.13.1", "@graphql-codegen/time": "^3.2.1", "@graphql-codegen/typescript": "^2.7.3", "@graphql-codegen/typescript-graphql-files-modules": "^2.2.1", - "@graphql-codegen/typescript-graphql-request": "^4.5.4", + "@graphql-codegen/typescript-graphql-request": "^4.5.5", "@graphql-codegen/typescript-operations": "^2.5.3", "@types/cors": "^2.8.12", "@types/gl": "^4.1.1", - "@types/jest": "^29.0.3", - "@types/react": "^18.0.20", + "@types/jest": "^29.1.1", + "@types/react": "^18.0.21", "@types/react-dom": "^18.0.6", - "@typescript-eslint/eslint-plugin": "^5.37.0", - "@typescript-eslint/parser": "^5.37.0", + "@typescript-eslint/eslint-plugin": "^5.38.1", + "@typescript-eslint/parser": "^5.38.1", "benchmark": "^2.1.4", "concurrently": "^7.4.0", "cpx2": "^4.2.0", "crypto-browserify": "^3.12.0", "css-loader": "^6.7.1", - "eslint": "^8.23.1", + "eslint": "^8.24.0", "extra-watch-webpack-plugin": "^1.0.3", "file-loader": "^6.2.0", "fs-extra": "^10.1.0", "graphql": "^16.6.0", "http-server": "^14.1.1", - "jest": "^29.0.3", + "jest": "^29.1.2", "mini-css-extract-plugin": "^2.6.1", "path-browserify": "^1.0.1", "raw-loader": "^4.0.2", "react": "^18.2.0", "react-dom": "^18.2.0", - "sass": "^1.54.9", + "sass": "^1.55.0", "sass-loader": "^13.0.2", "simple-git": "^3.14.1", "stream-browserify": "^3.0.0", "style-loader": "^3.3.1", - "ts-jest": "^29.0.1", - "typescript": "^4.8.3", + "ts-jest": "^29.0.3", + "typescript": "^4.8.4", "webpack": "^5.74.0", "webpack-cli": "^4.10.0" }, @@ -142,7 +142,7 @@ "@types/benchmark": "^2.1.2", "@types/compression": "1.7.2", "@types/express": "^4.17.14", - "@types/node": "^16.11.59", + "@types/node": "^16.11.62", "@types/node-fetch": "^2.6.2", "@types/swagger-ui-dist": "3.30.1", "argparse": "^2.0.1", @@ -154,8 +154,8 @@ "immer": "^9.0.15", "immutable": "^4.1.0", "node-fetch": "^2.6.7", - "rxjs": "^7.5.6", - "swagger-ui-dist": "^4.14.0", + "rxjs": "^7.5.7", + "swagger-ui-dist": "^4.14.2", "tslib": "^2.4.0", "util.promisify": "^1.1.1", "xhr2": "^0.2.1" diff --git a/src/extensions/backgrounds/index.ts b/src/extensions/backgrounds/index.ts index c87c8899f367a38bf377f6d266586faf0fe32ae0..c5bbdc99e1f013f3658ffa660cc0df4889db5e44 100644 --- a/src/extensions/backgrounds/index.ts +++ b/src/extensions/backgrounds/index.ts @@ -72,6 +72,7 @@ export const Backgrounds = PluginBehavior.create<{ }>({ lightness: 0, saturation: 0, opacity: 1, + blur: 0.3, } } }, 'Purple Nebula Skybox'], diff --git a/src/mol-canvas3d/camera.ts b/src/mol-canvas3d/camera.ts index 2b82c50df22dd208c232a961817a14dc06d11247..169478586e24ba8d072321be640a24eccfb887e2 100644 --- a/src/mol-canvas3d/camera.ts +++ b/src/mol-canvas3d/camera.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> * @author Alexander Rose <alexander.rose@weirdbyte.de> @@ -260,7 +260,8 @@ namespace Camera { radius: 0, radiusMax: 10, fog: 50, - clipFar: true + clipFar: true, + minNear: 5, }; } @@ -276,6 +277,7 @@ namespace Camera { radiusMax: number fog: number clipFar: boolean + minNear: number } export function copySnapshot(out: Snapshot, source?: Partial<Snapshot>) { @@ -292,6 +294,7 @@ namespace Camera { if (typeof source.radiusMax !== 'undefined') out.radiusMax = source.radiusMax; if (typeof source.fog !== 'undefined') out.fog = source.fog; if (typeof source.clipFar !== 'undefined') out.clipFar = source.clipFar; + if (typeof source.minNear !== 'undefined') out.minNear = source.minNear; return out; } @@ -303,6 +306,7 @@ namespace Camera { && a.radiusMax === b.radiusMax && a.fog === b.fog && a.clipFar === b.clipFar + && a.minNear === b.minNear && Vec3.exactEquals(a.position, b.position) && Vec3.exactEquals(a.up, b.up) && Vec3.exactEquals(a.target, b.target); @@ -370,7 +374,7 @@ function updatePers(camera: Camera) { } function updateClip(camera: Camera) { - let { radius, radiusMax, mode, fog, clipFar } = camera.state; + let { radius, radiusMax, mode, fog, clipFar, minNear } = camera.state; if (radius < 0.01) radius = 0.01; const normalizedFar = clipFar ? radius : radiusMax; @@ -384,12 +388,12 @@ function updateClip(camera: Camera) { if (mode === 'perspective') { // set at least to 5 to avoid slow sphere impostor rendering - near = Math.max(Math.min(radiusMax, 5), near); - far = Math.max(5, far); + near = Math.max(Math.min(radiusMax, minNear), near); + far = Math.max(minNear, far); } else { // not too close to 0 as it causes issues with outline rendering - near = Math.max(Math.min(radiusMax, 5), near); - far = Math.max(5, far); + near = Math.max(Math.min(radiusMax, minNear), near); + far = Math.max(minNear, far); } if (near === far) { diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts index 24223dca18e018e7a326bfc59225cbb60879278d..ed05d3a357ba32df5d8d88e57981a2b654d5c205 100644 --- a/src/mol-canvas3d/canvas3d.ts +++ b/src/mol-canvas3d/canvas3d.ts @@ -65,6 +65,7 @@ export const Canvas3DParams = { cameraClipping: PD.Group({ radius: PD.Numeric(100, { min: 0, max: 99, step: 1 }, { label: 'Clipping', description: 'How much of the scene to show.' }), far: PD.Boolean(true, { description: 'Hide scene in the distance' }), + minNear: PD.Numeric(5, { min: 0.1, max: 10, step: 0.1 }, { description: 'Note, may cause performance issues rendering impostors when set too small and cause issues with outline rendering when too close to 0.' }), }, { pivot: 'radius' }), viewport: PD.MappedStatic('canvas', { canvas: PD.Group({}), @@ -322,6 +323,7 @@ namespace Canvas3D { mode: p.camera.mode, fog: p.cameraFog.name === 'on' ? p.cameraFog.params.intensity : 0, clipFar: p.cameraClipping.far, + minNear: p.cameraClipping.minNear, fov: degToRad(p.camera.fov), }, { x, y, width, height }, { pixelScale: attribs.pixelScale }); const stereoCamera = new StereoCamera(camera, p.camera.stereo.params); @@ -687,7 +689,7 @@ namespace Canvas3D { cameraFog: camera.state.fog > 0 ? { name: 'on' as const, params: { intensity: camera.state.fog } } : { name: 'off' as const, params: {} }, - cameraClipping: { far: camera.state.clipFar, radius }, + cameraClipping: { far: camera.state.clipFar, radius, minNear: camera.state.minNear }, cameraResetDurationMs: p.cameraResetDurationMs, sceneRadiusFactor: p.sceneRadiusFactor, transparentBackground: p.transparentBackground, @@ -814,6 +816,9 @@ namespace Canvas3D { if (props.cameraClipping.far !== undefined && props.cameraClipping.far !== camera.state.clipFar) { cameraState.clipFar = props.cameraClipping.far; } + if (props.cameraClipping.minNear !== undefined && props.cameraClipping.minNear !== camera.state.minNear) { + cameraState.minNear = props.cameraClipping.minNear; + } if (props.cameraClipping.radius !== undefined) { const radius = (getSceneRadius() / 100) * (100 - props.cameraClipping.radius); if (radius > 0 && radius !== cameraState.radius) { diff --git a/src/mol-canvas3d/passes/background.ts b/src/mol-canvas3d/passes/background.ts index d4bfb3e59aa3fb02cfdc4e958b61618dbc500235..14d194b6baa418f1e381cf47b182735590df2be2 100644 --- a/src/mol-canvas3d/passes/background.ts +++ b/src/mol-canvas3d/passes/background.ts @@ -49,6 +49,7 @@ const SkyboxParams = { pz: PD.File({ label: 'Positive Z / Front', accept: 'image/*' }), }, { isExpanded: true, label: 'Files' }), }), + blur: PD.Numeric(0, { min: 0.0, max: 1.0, step: 0.01 }, { description: 'Note, this only works in WebGL2 or when "EXT_shader_texture_lod" is available.' }), ...SharedParams, }; type SkyboxProps = PD.Values<typeof SkyboxParams> @@ -170,6 +171,7 @@ export class BackgroundPass { Mat4.invert(m, m); ValueCell.update(this.renderable.values.uViewDirectionProjectionInverse, m); + ValueCell.updateIfChanged(this.renderable.values.uBlur, props.blur); ValueCell.updateIfChanged(this.renderable.values.uOpacity, props.opacity); ValueCell.updateIfChanged(this.renderable.values.uSaturation, props.saturation); ValueCell.updateIfChanged(this.renderable.values.uLightness, props.lightness); @@ -367,7 +369,7 @@ function getSkyboxTexture(ctx: WebGLContext, assetManager: AssetManager, faces: const cubeAssets = getCubeAssets(assetManager, faces); const cubeFaces = getCubeFaces(assetManager, cubeAssets); const assets = [cubeAssets.nx, cubeAssets.ny, cubeAssets.nz, cubeAssets.px, cubeAssets.py, cubeAssets.pz]; - const texture = ctx.resources.cubeTexture(cubeFaces, false, onload); + const texture = ctx.resources.cubeTexture(cubeFaces, true, onload); return { texture, assets }; } @@ -424,12 +426,15 @@ const BackgroundSchema = { uGradientColorA: UniformSpec('v3'), uGradientColorB: UniformSpec('v3'), uGradientRatio: UniformSpec('f'), + uBlur: UniformSpec('f'), uOpacity: UniformSpec('f'), uSaturation: UniformSpec('f'), uLightness: UniformSpec('f'), dVariant: DefineSpec('string', ['skybox', 'image', 'verticalGradient', 'horizontalGradient', 'radialGradient']), }; -const SkyboxShaderCode = ShaderCode('background', background_vert, background_frag); +const SkyboxShaderCode = ShaderCode('background', background_vert, background_frag, { + shaderTextureLod: 'optional' +}); type BackgroundRenderable = ComputeRenderable<Values<typeof BackgroundSchema>> function getBackgroundRenderable(ctx: WebGLContext, width: number, height: number): BackgroundRenderable { @@ -448,6 +453,7 @@ function getBackgroundRenderable(ctx: WebGLContext, width: number, height: numbe uGradientColorA: ValueCell.create(Vec3()), uGradientColorB: ValueCell.create(Vec3()), uGradientRatio: ValueCell.create(0.5), + uBlur: ValueCell.create(0), uOpacity: ValueCell.create(1), uSaturation: ValueCell.create(0), uLightness: ValueCell.create(0), diff --git a/src/mol-canvas3d/passes/draw.ts b/src/mol-canvas3d/passes/draw.ts index b286e584036a1500a4b4b09c49658fccec204c42..34282e8408d16a0c59251178c6ab14843914d707 100644 --- a/src/mol-canvas3d/passes/draw.ts +++ b/src/mol-canvas3d/passes/draw.ts @@ -153,6 +153,8 @@ export class DrawPass { this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps); } + this.depthTextureOpaque.detachFramebuffer(this.colorTarget.framebuffer, 'depth'); + // render transparent primitives if (scene.opacityAverage < 1) { const target = PostprocessingPass.isEnabled(postprocessingProps) diff --git a/src/mol-canvas3d/passes/passes.ts b/src/mol-canvas3d/passes/passes.ts index c52b1488f064302eeb7fcbc1cb6b2d056e8321bc..8974562c2e936241bf5a07a859ba40d6ade39d74 100644 --- a/src/mol-canvas3d/passes/passes.ts +++ b/src/mol-canvas3d/passes/passes.ts @@ -24,7 +24,10 @@ export class Passes { updateSize() { const { gl } = this.webgl; - this.draw.setSize(gl.drawingBufferWidth, gl.drawingBufferHeight); + // Avoid setting dimensions to 0x0 because it causes "empty textures are not allowed" error. + const width = Math.max(gl.drawingBufferWidth, 2); + const height = Math.max(gl.drawingBufferHeight, 2); + this.draw.setSize(width, height); this.pick.syncSize(); this.multiSample.syncSize(); } diff --git a/src/mol-canvas3d/passes/smaa.ts b/src/mol-canvas3d/passes/smaa.ts index 4ac7296fa717dcb72e3c790fd027275c6a5177b5..79752e4da69232a69081ab6795baf71c7488ba42 100644 --- a/src/mol-canvas3d/passes/smaa.ts +++ b/src/mol-canvas3d/passes/smaa.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -11,7 +11,7 @@ import { ShaderCode } from '../../mol-gl/shader-code'; import { WebGLContext } from '../../mol-gl/webgl/context'; import { createComputeRenderItem } from '../../mol-gl/webgl/render-item'; import { RenderTarget } from '../../mol-gl/webgl/render-target'; -import { createTexture, loadImageTexture, Texture } from '../../mol-gl/webgl/texture'; +import { loadImageTexture, Texture } from '../../mol-gl/webgl/texture'; import { Vec2, Vec4 } from '../../mol-math/linear-algebra'; import { ValueCell } from '../../mol-util'; import { ParamDefinition as PD } from '../../mol-util/param-definition'; @@ -74,6 +74,7 @@ export class SmaaPass { state.viewport(x, y, width, height); state.scissor(x, y, width, height); + state.colorMask(true, true, true, true); state.clearColor(0, 0, 0, 1); gl.clear(gl.COLOR_BUFFER_BIT); @@ -191,8 +192,8 @@ function getWeightsRenderable(ctx: WebGLContext, edgesTexture: Texture): Weights const width = edgesTexture.getWidth(); const height = edgesTexture.getHeight(); - const areaTexture = createTexture(ctx.gl, ctx.extensions, 'image-uint8', 'rgb', 'ubyte', 'linear'); - const searchTexture = createTexture(ctx.gl, ctx.extensions, 'image-uint8', 'rgba', 'ubyte', 'nearest'); + const areaTexture = ctx.resources.texture('image-uint8', 'rgb', 'ubyte', 'linear'); + const searchTexture = ctx.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest'); const values: Values<typeof WeightsSchema> = { ...QuadValues, diff --git a/src/mol-geo/geometry/text/font-atlas.ts b/src/mol-geo/geometry/text/font-atlas.ts index 609a6985a50ab450799a602ed1ae91caaa95beb3..834b8ecbd49a1f278644496d67946c9554a0501e 100644 --- a/src/mol-geo/geometry/text/font-atlas.ts +++ b/src/mol-geo/geometry/text/font-atlas.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -88,7 +88,7 @@ export class FontAtlas { this.scratchCanvas.width = this.maxWidth; this.scratchCanvas.height = this.lineHeight; - this.scratchContext = this.scratchCanvas.getContext('2d')!; + this.scratchContext = this.scratchCanvas.getContext('2d', { willReadFrequently: true })!; this.scratchContext.font = `${p.fontStyle} ${p.fontVariant} ${p.fontWeight} ${fontSize}px ${p.fontFamily}`; this.scratchContext.fillStyle = 'black'; this.scratchContext.textBaseline = 'middle'; diff --git a/src/mol-geo/geometry/texture-mesh/color-smoothing.ts b/src/mol-geo/geometry/texture-mesh/color-smoothing.ts index 6a9983a8f45c4556617cc63cb650f28bc6442b34..8633a1a0577876f532dc4d7c5ae0efe8a8cac1b2 100644 --- a/src/mol-geo/geometry/texture-mesh/color-smoothing.ts +++ b/src/mol-geo/geometry/texture-mesh/color-smoothing.ts @@ -387,6 +387,8 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu const type = isInstanceType ? 'volumeInstance' : 'volume'; if (isTimingMode) webgl.timer.markEnd('calcTextureMeshColorSmoothing'); + // printTextureImage(readTexture(webgl, texture), { scale: 0.75 }); + return { texture, gridDim, gridTexDim: Vec2.create(width, height), gridTransform, type }; } diff --git a/src/mol-gl/compute/histogram-pyramid/reduction.ts b/src/mol-gl/compute/histogram-pyramid/reduction.ts index b95ad7c44933a9398687654794311b74b52e8ddf..43aaddf292b62a701c48f4cdb273beda069cc255 100644 --- a/src/mol-gl/compute/histogram-pyramid/reduction.ts +++ b/src/mol-gl/compute/histogram-pyramid/reduction.ts @@ -197,7 +197,7 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture, gl.finish(); if (isTimingMode) ctx.timer.markEnd('createHistogramPyramid'); - // printTexture(ctx, pyramidTex, 2) + // printTextureImage(readTexture(ctx, pyramidTex), { scale: 0.75 }); // diff --git a/src/mol-gl/compute/marching-cubes/active-voxels.ts b/src/mol-gl/compute/marching-cubes/active-voxels.ts index c460512d509d791b3ebfc5eba5d07afefa7f32ef..e4fe0173a728020bf95d0a2642a7c36ce9ba35b3 100644 --- a/src/mol-gl/compute/marching-cubes/active-voxels.ts +++ b/src/mol-gl/compute/marching-cubes/active-voxels.ts @@ -115,6 +115,7 @@ export function calcActiveVoxels(ctx: WebGLContext, volumeData: Texture, gridDim // console.log('gridScale', gridScale, 'gridTexDim', gridTexDim, 'gridDim', gridDim); // console.log('volumeData', volumeData); // console.log('at', readTexture(ctx, activeVoxelsTex)); + // printTextureImage(readTexture(ctx, activeVoxelsTex), { scale: 0.75 }); gl.finish(); if (isTimingMode) ctx.timer.markEnd('calcActiveVoxels'); diff --git a/src/mol-gl/compute/marching-cubes/isosurface.ts b/src/mol-gl/compute/marching-cubes/isosurface.ts index 0215937e512ad4839c8b5dc9fb9b16fe1cf044e1..62af9563c3315e72f2ed814250a2d4cb007ff6a7 100644 --- a/src/mol-gl/compute/marching-cubes/isosurface.ts +++ b/src/mol-gl/compute/marching-cubes/isosurface.ts @@ -199,6 +199,10 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex gl.finish(); if (isTimingMode) ctx.timer.markEnd('createIsosurfaceBuffers'); + // printTextureImage(readTexture(ctx, vertexTexture, new Float32Array(width * height * 4)), { scale: 0.75 }); + // printTextureImage(readTexture(ctx, groupTexture, new Uint8Array(width * height * 4)), { scale: 0.75 }); + // printTextureImage(readTexture(ctx, normalTexture, new Float32Array(width * height * 4)), { scale: 0.75 }); + return { vertexTexture, groupTexture, normalTexture, vertexCount: count }; } diff --git a/src/mol-gl/compute/util.ts b/src/mol-gl/compute/util.ts index bfef65236a7a6ee14392f4f0f50f2fc1da56cac0..044826ae0210215a3cdb0672ce5bfb5b134c5888 100644 --- a/src/mol-gl/compute/util.ts +++ b/src/mol-gl/compute/util.ts @@ -75,9 +75,9 @@ export function getSharedCopyRenderable(ctx: WebGLContext, texture: Texture) { const ReadTextureName = 'read-texture'; const ReadAlphaTextureName = 'read-alpha-texture'; -export function readTexture(ctx: WebGLContext, texture: Texture) { +export function readTexture<T extends Uint8Array | Float32Array | Int32Array = Uint8Array>(ctx: WebGLContext, texture: Texture, array?: T) { const { gl, resources } = ctx; - if (texture.type !== gl.UNSIGNED_BYTE) throw new Error('unsupported texture type'); + if (!array && texture.type !== gl.UNSIGNED_BYTE) throw new Error('unsupported texture type'); if (!ctx.namedFramebuffers[ReadTextureName]) { ctx.namedFramebuffers[ReadTextureName] = resources.framebuffer(); @@ -86,7 +86,7 @@ export function readTexture(ctx: WebGLContext, texture: Texture) { const width = texture.getWidth(); const height = texture.getHeight(); - const array = new Uint8Array(width * height * 4); + if (!array) array = new Uint8Array(width * height * 4) as T; framebuffer.bind(); texture.attachFramebuffer(framebuffer, 0); ctx.readPixels(0, 0, width, height, array); diff --git a/src/mol-gl/renderable/util.ts b/src/mol-gl/renderable/util.ts index 87601da6a1dd366dada2eb728ad87ef75a3fb3aa..cb54c9a737dd471d6b3cf3e6b3571111618abbf6 100644 --- a/src/mol-gl/renderable/util.ts +++ b/src/mol-gl/renderable/util.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -8,6 +8,7 @@ import { Sphere3D } from '../../mol-math/geometry'; import { Vec3, Mat4 } from '../../mol-math/linear-algebra'; import { BoundaryHelper } from '../../mol-math/geometry/boundary-helper'; import { TextureFilter } from '../webgl/texture'; +import { arrayMinMax } from '../../mol-util/array'; export function calculateTextureInfo(n: number, itemSize: number) { n = Math.max(n, 2); // observed issues with 1 pixel textures @@ -42,7 +43,8 @@ export function createTextureImage<T extends Uint8Array | Float32Array>(n: numbe const DefaultPrintImageOptions = { scale: 1, pixelated: false, - id: 'molstar.debug.image' + id: 'molstar.debug.image', + normalize: false, }; export type PrintImageOptions = typeof DefaultPrintImageOptions @@ -58,7 +60,17 @@ export function printTextureImage(textureImage: TextureImage<any>, options: Part } } } else if (itemSize === 4) { - data.set(array); + if (options.normalize) { + const [min, max] = arrayMinMax(array); + for (let i = 0, il = width * height * 4; i < il; i += 4) { + data[i] = ((array[i] - min) / (max - min)) * 255; + data[i + 1] = ((array[i + 1] - min) / (max - min)) * 255; + data[i + 2] = ((array[i + 2] - min) / (max - min)) * 255; + data[i + 3] = 255; + } + } else { + data.set(array); + } } else { console.warn(`itemSize '${itemSize}' not supported`); } diff --git a/src/mol-gl/shader/background.frag.ts b/src/mol-gl/shader/background.frag.ts index a764a9ad8237ec635bb53ad45c7bb7e08f297c4f..835314ff237890841d3d5c57ffccd73eed72911b 100644 --- a/src/mol-gl/shader/background.frag.ts +++ b/src/mol-gl/shader/background.frag.ts @@ -6,6 +6,7 @@ precision mediump sampler2D; #if defined(dVariant_skybox) uniform samplerCube tSkybox; uniform mat4 uViewDirectionProjectionInverse; + uniform float uBlur; uniform float uOpacity; uniform float uSaturation; uniform float uLightness; @@ -49,7 +50,11 @@ vec3 lightenColor(vec3 c, float amount) { void main() { #if defined(dVariant_skybox) vec4 t = uViewDirectionProjectionInverse * vPosition; - gl_FragColor = textureCube(tSkybox, normalize(t.xyz / t.w)); + #ifdef enabledShaderTextureLod + gl_FragColor = textureCubeLodEXT(tSkybox, normalize(t.xyz / t.w), uBlur * 8.0); + #else + gl_FragColor = textureCube(tSkybox, normalize(t.xyz / t.w)); + #endif gl_FragColor.a = uOpacity; gl_FragColor.rgb = lightenColor(saturateColor(gl_FragColor.rgb, uSaturation), uLightness); #elif defined(dVariant_image) diff --git a/src/mol-gl/shader/chunks/apply-light-color.glsl.ts b/src/mol-gl/shader/chunks/apply-light-color.glsl.ts index c0cd9b64b4a379ded35896aaa5d9f1027bac27f5..07374df935bb5c5a14df4977dba32b7f5cfdda38 100644 --- a/src/mol-gl/shader/chunks/apply-light-color.glsl.ts +++ b/src/mol-gl/shader/chunks/apply-light-color.glsl.ts @@ -57,6 +57,7 @@ export const apply_light_color = ` RE_IndirectSpecular_Physical(radiance, iblIrradiance, clearcoatRadiance, geometry, physicalMaterial, reflectedLight); vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular; + outgoingLight = clamp(outgoingLight, 0.0, 1.0); // prevents black artifacts on specular highlight with transparent background gl_FragColor = vec4(outgoingLight, color.a); #endif diff --git a/src/mol-gl/shader/chunks/dpoit-write.glsl.ts b/src/mol-gl/shader/chunks/dpoit-write.glsl.ts index 23061cf8eed6ba42548cfd72c2f7f411c8a81777..2475d16ce86559a441669bb9b26c127dbf986480 100644 --- a/src/mol-gl/shader/chunks/dpoit-write.glsl.ts +++ b/src/mol-gl/shader/chunks/dpoit-write.glsl.ts @@ -37,7 +37,7 @@ export const dpoit_write = ` // back color is separately blend afterwards each pass gl_FragData[1] = vec4(0.0); - float nearestDepth = - lastDepth.x; + float nearestDepth = -lastDepth.x; float furthestDepth = lastDepth.y; float alphaMultiplier = 1.0 - lastFrontColor.a; diff --git a/src/mol-io/common/binary-cif/array-encoder.ts b/src/mol-io/common/binary-cif/array-encoder.ts index 08dfee8f6d315a0c995cdc0939f7d76648714365..7ca14219f7777ca448e5b5ae04615e7f07205023 100644 --- a/src/mol-io/common/binary-cif/array-encoder.ts +++ b/src/mol-io/common/binary-cif/array-encoder.ts @@ -264,28 +264,35 @@ export namespace ArrayEncoding { return false; } - function packingSize(data: Int32Array, upperLimit: number) { + function packingSizeUnsigned(data: Int32Array, upperLimit: number) { + let size = 0; + for (let i = 0, n = data.length; i < n; i++) { + size += (data[i] / upperLimit) | 0; + } + size += data.length; + return size; + } + + + function packingSizeSigned(data: Int32Array, upperLimit: number) { const lowerLimit = -upperLimit - 1; let size = 0; for (let i = 0, n = data.length; i < n; i++) { const value = data[i]; - if (value === 0) { - size += 1; - } else if (value > 0) { - size += Math.ceil(value / upperLimit); - if (value % upperLimit === 0) size += 1; + if (value >= 0) { + size += (value / upperLimit) | 0; } else { - size += Math.ceil(value / lowerLimit); - if (value % lowerLimit === 0) size += 1; + size += (value / lowerLimit) | 0; } } + size += data.length; return size; } function determinePacking(data: Int32Array): { isSigned: boolean, size: number, bytesPerElement: number } { const signed = isSigned(data); - const size8 = signed ? packingSize(data, 0x7F) : packingSize(data, 0xFF); - const size16 = signed ? packingSize(data, 0x7FFF) : packingSize(data, 0xFFFF); + const size8 = signed ? packingSizeSigned(data, 0x7F) : packingSizeUnsigned(data, 0xFF); + const size16 = signed ? packingSizeSigned(data, 0x7FFF) : packingSizeUnsigned(data, 0xFFFF); if (data.length * 4 < size16 * 2) { // 4 byte packing is the most effective diff --git a/src/mol-math/geometry/gaussian-density/gpu.ts b/src/mol-math/geometry/gaussian-density/gpu.ts index 05a26567d0017bbabde11f392d26bca3ccb07afe..a9621f6e21d8c74371fec6a6a7d03d28009315ae 100644 --- a/src/mol-math/geometry/gaussian-density/gpu.ts +++ b/src/mol-math/geometry/gaussian-density/gpu.ts @@ -204,7 +204,7 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat render(texture, false); } - // printTexture(webgl, minDistTex, 0.75); + // printTextureImage(readTexture(webgl, minDistTex), { scale: 0.75 }); return { texture, scale, bbox: expandedBox, gridDim: dim, gridTexDim, gridTexScale, radiusFactor, resolution, maxRadius }; } diff --git a/src/mol-plugin-ui/viewport/simple-settings.tsx b/src/mol-plugin-ui/viewport/simple-settings.tsx index 77772e62b3047fbb12b8c3a77897456380b734ff..a3f7eb6d939a26fb0db47839521823779989c94f 100644 --- a/src/mol-plugin-ui/viewport/simple-settings.tsx +++ b/src/mol-plugin-ui/viewport/simple-settings.tsx @@ -135,6 +135,7 @@ const SimpleSettingsMapping = ParamMapping({ canvas.cameraClipping = { radius: s.clipping.radius, far: s.clipping.far, + minNear: s.clipping.minNear, }; props.layout = s.layout;