mirror of
https://github.com/molstar/molstar.git
synced 2026-06-05 22:31:26 +08:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73ac445a44 | ||
|
|
1a1d1d9d30 | ||
|
|
062aff76da | ||
|
|
7d0d24b66d | ||
|
|
6655672d11 | ||
|
|
6e573ae410 | ||
|
|
1c48c02473 | ||
|
|
78be3320ce | ||
|
|
c8018800cc | ||
|
|
bb795aca98 | ||
|
|
2cb1279f4c | ||
|
|
b876c6f618 | ||
|
|
3a7dfc055e | ||
|
|
928e521ac7 | ||
|
|
e5e9598e4b | ||
|
|
e6e1809592 | ||
|
|
812f97ddb7 | ||
|
|
c6b814b31b | ||
|
|
98566fa389 | ||
|
|
4318c89bdb | ||
|
|
b41a97ce6a | ||
|
|
862c384dc0 | ||
|
|
26cc7e94c2 | ||
|
|
c6fe6ddcba |
13
CHANGELOG.md
13
CHANGELOG.md
@@ -6,6 +6,19 @@ Note that since we don't clearly distinguish between a public and private interf
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v2.4.0] - 2021-11-25
|
||||
|
||||
- Fix secondary-structure property handling
|
||||
- StructureElement.Property was incorrectly resolving type & key
|
||||
- StructureSelectionQuery helpers 'helix' & 'beta' were not ensuring property availability
|
||||
- Re-enable VAO with better workaround (bind null elements buffer before deleting)
|
||||
- Add ``Representation.geometryVersion`` (increments whenever the geometry of any of its visuals changes)
|
||||
- Add support for grid-based smoothing of Overpaint and Transparency visual state for surfaces
|
||||
|
||||
## [v2.3.9] - 2021-11-20
|
||||
|
||||
- Workaround: switch off VAO support for now
|
||||
|
||||
## [v2.3.8] - 2021-11-20
|
||||
|
||||
- Fix double canvas context creation (in plugin context)
|
||||
|
||||
949
package-lock.json
generated
949
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
52
package.json
52
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "2.3.8",
|
||||
"version": "2.4.0",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
@@ -89,40 +89,40 @@
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/add": "^3.1.0",
|
||||
"@graphql-codegen/cli": "^2.2.0",
|
||||
"@graphql-codegen/cli": "^2.3.0",
|
||||
"@graphql-codegen/time": "^3.1.0",
|
||||
"@graphql-codegen/typescript": "^2.2.2",
|
||||
"@graphql-codegen/typescript": "^2.4.1",
|
||||
"@graphql-codegen/typescript-graphql-files-modules": "^2.1.0",
|
||||
"@graphql-codegen/typescript-graphql-request": "^4.1.4",
|
||||
"@graphql-codegen/typescript-operations": "^2.1.6",
|
||||
"@graphql-codegen/typescript-graphql-request": "^4.3.1",
|
||||
"@graphql-codegen/typescript-operations": "^2.2.1",
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/gl": "^4.1.0",
|
||||
"@types/jest": "^27.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^4.32.0",
|
||||
"@typescript-eslint/parser": "^4.32.0",
|
||||
"@types/jest": "^27.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"benchmark": "^2.1.4",
|
||||
"concurrently": "^6.3.0",
|
||||
"concurrently": "^6.4.0",
|
||||
"cpx2": "^4.0.0",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"css-loader": "^6.3.0",
|
||||
"eslint": "^7.32.0",
|
||||
"css-loader": "^6.5.1",
|
||||
"eslint": "^8.3.0",
|
||||
"extra-watch-webpack-plugin": "^1.0.3",
|
||||
"file-loader": "^6.2.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
"graphql": "^15.6.0",
|
||||
"http-server": "^13.0.2",
|
||||
"jest": "^27.2.4",
|
||||
"mini-css-extract-plugin": "^2.3.0",
|
||||
"graphql": "^15.7.2",
|
||||
"http-server": "^14.0.0",
|
||||
"jest": "^27.3.1",
|
||||
"mini-css-extract-plugin": "^2.4.5",
|
||||
"path-browserify": "^1.0.1",
|
||||
"raw-loader": "^4.0.2",
|
||||
"sass": "^1.43.4",
|
||||
"sass": "^1.43.5",
|
||||
"sass-loader": "^12.3.0",
|
||||
"simple-git": "^2.46.0",
|
||||
"simple-git": "^2.47.0",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"style-loader": "^3.3.0",
|
||||
"ts-jest": "^27.0.5",
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-jest": "^27.0.7",
|
||||
"typescript": "^4.5.2",
|
||||
"webpack": "^5.64.1",
|
||||
"webpack": "^5.64.4",
|
||||
"webpack-cli": "^4.9.1"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -130,10 +130,10 @@
|
||||
"@types/benchmark": "^2.1.1",
|
||||
"@types/compression": "1.7.2",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/node": "^16.10.2",
|
||||
"@types/node": "^16.11.10",
|
||||
"@types/node-fetch": "^2.5.12",
|
||||
"@types/react": "^17.0.27",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"@types/react": "^17.0.37",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"@types/swagger-ui-dist": "3.30.1",
|
||||
"argparse": "^2.0.1",
|
||||
"body-parser": "^1.19.0",
|
||||
@@ -141,13 +141,13 @@
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"h264-mp4-encoder": "^1.0.12",
|
||||
"immer": "^9.0.6",
|
||||
"immer": "^9.0.7",
|
||||
"immutable": "^3.8.2",
|
||||
"node-fetch": "^2.6.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"rxjs": "^7.3.1",
|
||||
"swagger-ui-dist": "^3.52.3",
|
||||
"rxjs": "^7.4.0",
|
||||
"swagger-ui-dist": "^4.1.1",
|
||||
"tslib": "^2.3.1",
|
||||
"util.promisify": "^1.1.1",
|
||||
"xhr2": "^0.2.1"
|
||||
|
||||
@@ -84,6 +84,7 @@ export function getFieldType(type: string, description: string, values?: string[
|
||||
case 'DateTime':
|
||||
case 'Tag':
|
||||
case 'Implied':
|
||||
case 'Word':
|
||||
return wrapContainer('str', ',', description, container);
|
||||
case 'Real':
|
||||
return wrapContainer('float', ',', description, container);
|
||||
|
||||
@@ -77,7 +77,7 @@ export class GeometryControls extends PluginComponent {
|
||||
filename: filename + '.' + renderObjectExporter.fileExtension
|
||||
};
|
||||
} catch (e) {
|
||||
this.plugin.log.error('' + e);
|
||||
this.plugin.log.error('Error during geometry export');
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { BaseValues } from '../../mol-gl/renderable/schema';
|
||||
import { Style } from '../../mol-gl/renderer';
|
||||
import { asciiWrite } from '../../mol-io/common/ascii';
|
||||
import { IsNativeEndianLittle, flipByteOrder } from '../../mol-io/common/binary';
|
||||
@@ -15,7 +15,7 @@ import { RuntimeContext } from '../../mol-task';
|
||||
import { Color } from '../../mol-util/color/color';
|
||||
import { fillSerial } from '../../mol-util/array';
|
||||
import { NumberArray } from '../../mol-util/type-helpers';
|
||||
import { MeshExporter, AddMeshInput } from './mesh-exporter';
|
||||
import { MeshExporter, AddMeshInput, MeshGeoData } from './mesh-exporter';
|
||||
|
||||
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
|
||||
const v3fromArray = Vec3.fromArray;
|
||||
@@ -30,6 +30,12 @@ const FLOAT = 5126;
|
||||
const ARRAY_BUFFER = 34962;
|
||||
const ELEMENT_ARRAY_BUFFER = 34963;
|
||||
|
||||
const GLTF_MAGIC_BYTE = 0x46546C67;
|
||||
const JSON_CHUNK_TYPE = 0x4E4F534A;
|
||||
const BIN_CHUNK_TYPE = 0x004E4942;
|
||||
const JSON_PAD_CHAR = 0x20;
|
||||
const BIN_PAD_CHAR = 0x00;
|
||||
|
||||
export type GlbData = {
|
||||
glb: Uint8Array
|
||||
}
|
||||
@@ -126,23 +132,17 @@ export class GlbExporter extends MeshExporter<GlbData> {
|
||||
};
|
||||
}
|
||||
|
||||
private addColorBuffer(values: BaseValues, groups: Float32Array | Uint8Array, vertexCount: number, instanceIndex: number, isGeoTexture: boolean, interpolatedColors: Uint8Array | undefined) {
|
||||
const groupCount = values.uGroupCount.ref.value;
|
||||
private addColorBuffer(geoData: MeshGeoData, interpolatedColors: Uint8Array | undefined, interpolatedOverpaint: Uint8Array | undefined, interpolatedTransparency: Uint8Array | undefined) {
|
||||
const { values, vertexCount } = geoData;
|
||||
const uAlpha = values.uAlpha.ref.value;
|
||||
const dTransparency = values.dTransparency.ref.value;
|
||||
const tTransparency = values.tTransparency.ref.value;
|
||||
|
||||
const colorArray = new Uint8Array(vertexCount * 4);
|
||||
|
||||
for (let i = 0; i < vertexCount; ++i) {
|
||||
let color = GlbExporter.getColor(values, groups, vertexCount, instanceIndex, isGeoTexture, interpolatedColors, i);
|
||||
let color = GlbExporter.getColor(i, geoData, interpolatedColors, interpolatedOverpaint);
|
||||
|
||||
let alpha = uAlpha;
|
||||
if (dTransparency) {
|
||||
const group = isGeoTexture ? GlbExporter.getGroup(groups, i) : groups[i];
|
||||
const transparency = tTransparency.array[instanceIndex * groupCount + group] / 255;
|
||||
alpha *= 1 - transparency;
|
||||
}
|
||||
const transparency = GlbExporter.getTransparency(i, geoData, interpolatedTransparency);
|
||||
const alpha = uAlpha * (1 - transparency);
|
||||
|
||||
color = Color.sRGBToLinear(color);
|
||||
Color.toArray(color, colorArray, i * 4);
|
||||
@@ -163,6 +163,8 @@ export class GlbExporter extends MeshExporter<GlbData> {
|
||||
const t = Mat4();
|
||||
|
||||
const colorType = values.dColorType.ref.value;
|
||||
const overpaintType = values.dOverpaintType.ref.value;
|
||||
const transparencyType = values.dTransparencyType.ref.value;
|
||||
const dTransparency = values.dTransparency.ref.value;
|
||||
const aTransform = values.aTransform.ref.value;
|
||||
const instanceCount = values.uInstanceCount.ref.value;
|
||||
@@ -170,7 +172,19 @@ export class GlbExporter extends MeshExporter<GlbData> {
|
||||
let interpolatedColors: Uint8Array | undefined;
|
||||
if (colorType === 'volume' || colorType === 'volumeInstance') {
|
||||
const stride = isGeoTexture ? 4 : 3;
|
||||
interpolatedColors = GlbExporter.getInterpolatedColors(mesh!.vertices, mesh!.vertexCount, values, stride, colorType, webgl!);
|
||||
interpolatedColors = GlbExporter.getInterpolatedColors(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType });
|
||||
}
|
||||
|
||||
let interpolatedOverpaint: Uint8Array | undefined;
|
||||
if (overpaintType === 'volumeInstance') {
|
||||
const stride = isGeoTexture ? 4 : 3;
|
||||
interpolatedOverpaint = GlbExporter.getInterpolatedOverpaint(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType: overpaintType });
|
||||
}
|
||||
|
||||
let interpolatedTransparency: Uint8Array | undefined;
|
||||
if (transparencyType === 'volumeInstance') {
|
||||
const stride = isGeoTexture ? 4 : 3;
|
||||
interpolatedTransparency = GlbExporter.getInterpolatedTransparency(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType: transparencyType });
|
||||
}
|
||||
|
||||
// instancing
|
||||
@@ -201,7 +215,7 @@ export class GlbExporter extends MeshExporter<GlbData> {
|
||||
|
||||
// create a color buffer if needed
|
||||
if (instanceIndex === 0 || !sameColorBuffer) {
|
||||
colorAccessorIndex = this.addColorBuffer(values, groups, vertexCount, instanceIndex, isGeoTexture, interpolatedColors);
|
||||
colorAccessorIndex = this.addColorBuffer({ values, groups, vertexCount, instanceIndex, isGeoTexture }, interpolatedColors, interpolatedOverpaint, interpolatedTransparency);
|
||||
}
|
||||
|
||||
// glTF mesh
|
||||
@@ -279,13 +293,13 @@ export class GlbExporter extends MeshExporter<GlbData> {
|
||||
const jsonBuffer = new Uint8Array(jsonString.length);
|
||||
asciiWrite(jsonBuffer, jsonString);
|
||||
|
||||
const [jsonChunk, jsonChunkLength] = createChunk(0x4E4F534A, [jsonBuffer.buffer], jsonBuffer.length, 0x20);
|
||||
const [binaryChunk, binaryChunkLength] = createChunk(0x004E4942, this.binaryBuffer, binaryBufferLength, 0x00);
|
||||
const [jsonChunk, jsonChunkLength] = createChunk(JSON_CHUNK_TYPE, [jsonBuffer.buffer], jsonBuffer.length, JSON_PAD_CHAR);
|
||||
const [binaryChunk, binaryChunkLength] = createChunk(BIN_CHUNK_TYPE, this.binaryBuffer, binaryBufferLength, BIN_PAD_CHAR);
|
||||
|
||||
const glbBufferLength = 12 + jsonChunkLength + binaryChunkLength;
|
||||
const header = new ArrayBuffer(12);
|
||||
const headerDataView = new DataView(header);
|
||||
headerDataView.setUint32(0, 0x46546C67, true); // magic number "glTF"
|
||||
headerDataView.setUint32(0, GLTF_MAGIC_BYTE, true); // magic number "glTF"
|
||||
headerDataView.setUint32(4, 2, true); // version
|
||||
headerDataView.setUint32(8, glbBufferLength, true); // length
|
||||
const glbBuffer = [header, ...jsonChunk, ...binaryChunk];
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { sort, arraySwap } from '../../mol-data/util';
|
||||
@@ -26,6 +27,7 @@ import { RuntimeContext } from '../../mol-task';
|
||||
import { Color } from '../../mol-util/color/color';
|
||||
import { decodeFloatRGB } from '../../mol-util/float-packing';
|
||||
import { RenderObjectExporter, RenderObjectExportData } from './render-object-exporter';
|
||||
import { readAlphaTexture, readTexture } from '../../mol-gl/compute/util';
|
||||
|
||||
const GeoExportName = 'geo-export';
|
||||
|
||||
@@ -48,6 +50,14 @@ export interface AddMeshInput {
|
||||
ctx: RuntimeContext
|
||||
}
|
||||
|
||||
export type MeshGeoData = {
|
||||
values: BaseValues,
|
||||
groups: Float32Array | Uint8Array,
|
||||
vertexCount: number,
|
||||
instanceIndex: number,
|
||||
isGeoTexture: boolean
|
||||
}
|
||||
|
||||
export abstract class MeshExporter<D extends RenderObjectExportData> implements RenderObjectExporter<D> {
|
||||
abstract readonly fileExtension: string;
|
||||
|
||||
@@ -90,26 +100,43 @@ export abstract class MeshExporter<D extends RenderObjectExportData> implements
|
||||
return decodeFloatRGB(r, g, b);
|
||||
}
|
||||
|
||||
protected static getInterpolatedColors(vertices: Float32Array, vertexCount: number, values: BaseValues, stride: number, colorType: 'volume' | 'volumeInstance', webgl: WebGLContext) {
|
||||
protected static getInterpolatedColors(webgl: WebGLContext, input: { vertices: Float32Array, vertexCount: number, values: BaseValues, stride: 3 | 4, colorType: 'volume' | 'volumeInstance' }) {
|
||||
const { values, vertexCount, vertices, colorType, stride } = input;
|
||||
const colorGridTransform = values.uColorGridTransform.ref.value;
|
||||
const colorGridDim = values.uColorGridDim.ref.value;
|
||||
const colorTexDim = values.uColorTexDim.ref.value;
|
||||
const aTransform = values.aTransform.ref.value;
|
||||
const instanceCount = values.uInstanceCount.ref.value;
|
||||
|
||||
if (!webgl.namedFramebuffers[GeoExportName]) {
|
||||
webgl.namedFramebuffers[GeoExportName] = webgl.resources.framebuffer();
|
||||
}
|
||||
const framebuffer = webgl.namedFramebuffers[GeoExportName];
|
||||
const colorGrid = readTexture(webgl, values.tColorGrid.ref.value).array;
|
||||
const interpolated = getTrilinearlyInterpolated({ vertexCount, instanceCount, transformBuffer: aTransform, positionBuffer: vertices, colorType, grid: colorGrid, gridDim: colorGridDim, gridTexDim: colorTexDim, gridTransform: colorGridTransform, vertexStride: stride, colorStride: 4, outputStride: 3 });
|
||||
return interpolated.array;
|
||||
}
|
||||
|
||||
const [width, height] = colorTexDim;
|
||||
const colorGrid = new Uint8Array(width * height * 4);
|
||||
protected static getInterpolatedOverpaint(webgl: WebGLContext, input: { vertices: Float32Array, vertexCount: number, values: BaseValues, stride: 3 | 4, colorType: 'volumeInstance' }) {
|
||||
const { values, vertexCount, vertices, colorType, stride } = input;
|
||||
const overpaintGridTransform = values.uOverpaintGridTransform.ref.value;
|
||||
const overpaintGridDim = values.uOverpaintGridDim.ref.value;
|
||||
const overpaintTexDim = values.uOverpaintTexDim.ref.value;
|
||||
const aTransform = values.aTransform.ref.value;
|
||||
const instanceCount = values.uInstanceCount.ref.value;
|
||||
|
||||
framebuffer.bind();
|
||||
values.tColorGrid.ref.value.attachFramebuffer(framebuffer, 0);
|
||||
webgl.readPixels(0, 0, width, height, colorGrid);
|
||||
const overpaintGrid = readTexture(webgl, values.tOverpaintGrid.ref.value).array;
|
||||
const interpolated = getTrilinearlyInterpolated({ vertexCount, instanceCount, transformBuffer: aTransform, positionBuffer: vertices, colorType, grid: overpaintGrid, gridDim: overpaintGridDim, gridTexDim: overpaintTexDim, gridTransform: overpaintGridTransform, vertexStride: stride, colorStride: 4, outputStride: 4 });
|
||||
return interpolated.array;
|
||||
}
|
||||
|
||||
protected static getInterpolatedTransparency(webgl: WebGLContext, input: { vertices: Float32Array, vertexCount: number, values: BaseValues, stride: 3 | 4, colorType: 'volumeInstance' }) {
|
||||
const { values, vertexCount, vertices, colorType, stride } = input;
|
||||
const transparencyGridTransform = values.uTransparencyGridTransform.ref.value;
|
||||
const transparencyGridDim = values.uTransparencyGridDim.ref.value;
|
||||
const transparencyTexDim = values.uTransparencyTexDim.ref.value;
|
||||
const aTransform = values.aTransform.ref.value;
|
||||
const instanceCount = values.uInstanceCount.ref.value;
|
||||
|
||||
const transparencyGrid = readAlphaTexture(webgl, values.tTransparencyGrid.ref.value).array;
|
||||
const interpolated = getTrilinearlyInterpolated({ vertexCount, instanceCount, transformBuffer: aTransform, positionBuffer: vertices, colorType, grid: transparencyGrid, gridDim: transparencyGridDim, gridTexDim: transparencyTexDim, gridTransform: transparencyGridTransform, vertexStride: stride, colorStride: 4, outputStride: 1, itemOffset: 3 });
|
||||
|
||||
const interpolated = getTrilinearlyInterpolated({ vertexCount, instanceCount, transformBuffer: aTransform, positionBuffer: vertices, colorType, grid: colorGrid, gridDim: colorGridDim, gridTexDim: colorTexDim, gridTransform: colorGridTransform, vertexStride: stride, colorStride: 4 });
|
||||
return interpolated.array;
|
||||
}
|
||||
|
||||
@@ -194,11 +221,13 @@ export abstract class MeshExporter<D extends RenderObjectExportData> implements
|
||||
}
|
||||
}
|
||||
|
||||
protected static getColor(values: BaseValues, groups: Float32Array | Uint8Array, vertexCount: number, instanceIndex: number, isGeoTexture: boolean, interpolatedColors: Uint8Array | undefined, vertexIndex: number): Color {
|
||||
protected static getColor(vertexIndex: number, geoData: MeshGeoData, interpolatedColors?: Uint8Array, interpolatedOverpaint?: Uint8Array): Color {
|
||||
const { values, instanceIndex, isGeoTexture, groups, vertexCount } = geoData;
|
||||
const groupCount = values.uGroupCount.ref.value;
|
||||
const colorType = values.dColorType.ref.value;
|
||||
const uColor = values.uColor.ref.value;
|
||||
const tColor = values.tColor.ref.value.array;
|
||||
const overpaintType = values.dOverpaintType.ref.value;
|
||||
const dOverpaint = values.dOverpaint.ref.value;
|
||||
const tOverpaint = values.tOverpaint.ref.value.array;
|
||||
|
||||
@@ -236,15 +265,70 @@ export abstract class MeshExporter<D extends RenderObjectExportData> implements
|
||||
}
|
||||
|
||||
if (dOverpaint) {
|
||||
const group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex];
|
||||
const overpaintColor = Color.fromArray(tOverpaint, (instanceIndex * groupCount + group) * 4);
|
||||
const overpaintAlpha = tOverpaint[(instanceIndex * groupCount + group) * 4 + 3] / 255;
|
||||
let overpaintColor: Color;
|
||||
let overpaintAlpha: number;
|
||||
switch (overpaintType) {
|
||||
case 'groupInstance': {
|
||||
const group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex];
|
||||
const idx = (instanceIndex * groupCount + group) * 4;
|
||||
overpaintColor = Color.fromArray(tOverpaint, idx);
|
||||
overpaintAlpha = tOverpaint[idx + 3] / 255;
|
||||
break;
|
||||
}
|
||||
case 'vertexInstance': {
|
||||
const idx = (instanceIndex * vertexCount + vertexIndex) * 4;
|
||||
overpaintColor = Color.fromArray(tOverpaint, idx);
|
||||
overpaintAlpha = tOverpaint[idx + 3] / 255;
|
||||
break;
|
||||
}
|
||||
case 'volumeInstance': {
|
||||
const idx = (instanceIndex * vertexCount + vertexIndex) * 4;
|
||||
overpaintColor = Color.fromArray(interpolatedOverpaint!, idx);
|
||||
overpaintAlpha = interpolatedOverpaint![idx + 3] / 255;
|
||||
break;
|
||||
}
|
||||
default: throw new Error('Unsupported overpaint type.');
|
||||
}
|
||||
// interpolate twice to avoid darkening due to empty overpaint
|
||||
overpaintColor = Color.interpolate(color, overpaintColor, overpaintAlpha);
|
||||
color = Color.interpolate(color, overpaintColor, overpaintAlpha);
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
protected static getTransparency(vertexIndex: number, geoData: MeshGeoData, interpolatedTransparency?: Uint8Array): number {
|
||||
const { values, instanceIndex, isGeoTexture, groups, vertexCount } = geoData;
|
||||
const groupCount = values.uGroupCount.ref.value;
|
||||
const dTransparency = values.dTransparency.ref.value;
|
||||
const tTransparency = values.tTransparency.ref.value.array;
|
||||
const transparencyType = values.dTransparencyType.ref.value;
|
||||
|
||||
let transparency: number = 0;
|
||||
if (dTransparency) {
|
||||
switch (transparencyType) {
|
||||
case 'groupInstance': {
|
||||
const group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex];
|
||||
const idx = (instanceIndex * groupCount + group);
|
||||
transparency = tTransparency[idx] / 255;
|
||||
break;
|
||||
}
|
||||
case 'vertexInstance': {
|
||||
const idx = (instanceIndex * vertexCount + vertexIndex);
|
||||
transparency = tTransparency[idx] / 255;
|
||||
break;
|
||||
}
|
||||
case 'volumeInstance': {
|
||||
const idx = (instanceIndex * vertexCount + vertexIndex);
|
||||
transparency = interpolatedTransparency![idx] / 255;
|
||||
break;
|
||||
}
|
||||
default: throw new Error('Unsupported transparency type.');
|
||||
}
|
||||
}
|
||||
return transparency;
|
||||
}
|
||||
|
||||
protected abstract addMeshWithColors(input: AddMeshInput): void;
|
||||
|
||||
private async addMesh(values: MeshValues, webgl: WebGLContext, ctx: RuntimeContext) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { asciiWrite } from '../../mol-io/common/ascii';
|
||||
@@ -47,7 +48,7 @@ export class ObjExporter extends MeshExporter<ObjData> {
|
||||
StringBuilder.newline(this.obj);
|
||||
if (!this.materialSet.has(material)) {
|
||||
this.materialSet.add(material);
|
||||
const [r, g, b] = Color.toRgbNormalized(color);
|
||||
const [r, g, b] = Color.toRgbNormalized(color).map(v => Math.round(v * 1000) / 1000);
|
||||
const mtl = this.mtl;
|
||||
StringBuilder.writeSafe(mtl, `newmtl ${material}\n`);
|
||||
StringBuilder.writeSafe(mtl, 'illum 2\n'); // illumination model
|
||||
@@ -77,18 +78,27 @@ export class ObjExporter extends MeshExporter<ObjData> {
|
||||
const tmpV = Vec3();
|
||||
const stride = isGeoTexture ? 4 : 3;
|
||||
|
||||
const groupCount = values.uGroupCount.ref.value;
|
||||
const colorType = values.dColorType.ref.value;
|
||||
const overpaintType = values.dOverpaintType.ref.value;
|
||||
const transparencyType = values.dTransparencyType.ref.value;
|
||||
const uAlpha = values.uAlpha.ref.value;
|
||||
const dTransparency = values.dTransparency.ref.value;
|
||||
const tTransparency = values.tTransparency.ref.value;
|
||||
const aTransform = values.aTransform.ref.value;
|
||||
const instanceCount = values.uInstanceCount.ref.value;
|
||||
|
||||
let interpolatedColors: Uint8Array | undefined;
|
||||
if (colorType === 'volume' || colorType === 'volumeInstance') {
|
||||
interpolatedColors = ObjExporter.getInterpolatedColors(mesh!.vertices, mesh!.vertexCount, values, stride, colorType, webgl!);
|
||||
ObjExporter.quantizeColors(interpolatedColors, mesh!.vertexCount);
|
||||
interpolatedColors = ObjExporter.getInterpolatedColors(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType });
|
||||
}
|
||||
|
||||
let interpolatedOverpaint: Uint8Array | undefined;
|
||||
if (overpaintType === 'volumeInstance') {
|
||||
interpolatedOverpaint = ObjExporter.getInterpolatedOverpaint(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType: overpaintType });
|
||||
}
|
||||
|
||||
let interpolatedTransparency: Uint8Array | undefined;
|
||||
if (transparencyType === 'volumeInstance') {
|
||||
const stride = isGeoTexture ? 4 : 3;
|
||||
interpolatedTransparency = ObjExporter.getInterpolatedTransparency(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType: transparencyType });
|
||||
}
|
||||
|
||||
await ctx.update({ isIndeterminate: false, current: 0, max: instanceCount });
|
||||
@@ -126,17 +136,23 @@ export class ObjExporter extends MeshExporter<ObjData> {
|
||||
StringBuilder.newline(obj);
|
||||
}
|
||||
|
||||
// face
|
||||
const geoData = { values, groups, vertexCount, instanceIndex, isGeoTexture };
|
||||
|
||||
// color
|
||||
const quantizedColors = new Uint8Array(drawCount * 3);
|
||||
for (let i = 0; i < drawCount; i += 3) {
|
||||
const v = isGeoTexture ? i : indices![i];
|
||||
const color = ObjExporter.getColor(values, groups, vertexCount, instanceIndex, isGeoTexture, interpolatedColors, v);
|
||||
const color = ObjExporter.getColor(v, geoData, interpolatedColors, interpolatedOverpaint);
|
||||
Color.toArray(color, quantizedColors, i);
|
||||
}
|
||||
ObjExporter.quantizeColors(quantizedColors, mesh!.vertexCount);
|
||||
|
||||
let alpha = uAlpha;
|
||||
if (dTransparency) {
|
||||
const group = isGeoTexture ? ObjExporter.getGroup(groups, i) : groups[indices![i]];
|
||||
const transparency = tTransparency.array[instanceIndex * groupCount + group] / 255;
|
||||
alpha *= 1 - transparency;
|
||||
}
|
||||
// face
|
||||
for (let i = 0; i < drawCount; i += 3) {
|
||||
const color = Color.fromArray(quantizedColors, i);
|
||||
|
||||
const transparency = ObjExporter.getTransparency(i, geoData, interpolatedTransparency);
|
||||
const alpha = Math.round(uAlpha * (1 - transparency) * 10) / 10; // quantized
|
||||
|
||||
this.updateMaterial(color, alpha);
|
||||
|
||||
|
||||
@@ -79,10 +79,10 @@ export class GeometryExporterUI extends CollapsableControls<{}, State> {
|
||||
try {
|
||||
this.setState({ busy: true });
|
||||
const data = await this.controls.exportGeometry();
|
||||
this.setState({ busy: false });
|
||||
|
||||
download(data.blob, data.filename);
|
||||
} catch {
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
this.setState({ busy: false });
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,6 @@ export class GeometryExporterUI extends CollapsableControls<{}, State> {
|
||||
try {
|
||||
this.setState({ busy: true });
|
||||
const data = await this.controls.exportGeometry();
|
||||
this.setState({ busy: false });
|
||||
const a = document.createElement('a');
|
||||
a.rel = 'ar';
|
||||
a.href = URL.createObjectURL(data.blob);
|
||||
@@ -100,7 +99,9 @@ export class GeometryExporterUI extends CollapsableControls<{}, State> {
|
||||
a.appendChild(document.createElement('img'));
|
||||
setTimeout(() => URL.revokeObjectURL(a.href), 4E4); // 40s
|
||||
setTimeout(() => a.dispatchEvent(new MouseEvent('click')));
|
||||
} catch {
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
this.setState({ busy: false });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Style } from '../../mol-gl/renderer';
|
||||
@@ -42,7 +43,7 @@ export class UsdzExporter extends MeshExporter<UsdzData> {
|
||||
const materialKey = UsdzExporter.getMaterialKey(color, alpha);
|
||||
if (this.materialSet.has(materialKey)) return;
|
||||
this.materialSet.add(materialKey);
|
||||
const [r, g, b] = Color.toRgbNormalized(color);
|
||||
const [r, g, b] = Color.toRgbNormalized(color).map(v => Math.round(v * 1000) / 1000);
|
||||
this.materials.push(`
|
||||
def Material "material${materialKey}"
|
||||
{
|
||||
@@ -68,18 +69,28 @@ def Material "material${materialKey}"
|
||||
const tmpV = Vec3();
|
||||
const stride = isGeoTexture ? 4 : 3;
|
||||
|
||||
const groupCount = values.uGroupCount.ref.value;
|
||||
const colorType = values.dColorType.ref.value;
|
||||
const overpaintType = values.dOverpaintType.ref.value;
|
||||
const transparencyType = values.dTransparencyType.ref.value;
|
||||
const uAlpha = values.uAlpha.ref.value;
|
||||
const dTransparency = values.dTransparency.ref.value;
|
||||
const tTransparency = values.tTransparency.ref.value;
|
||||
const aTransform = values.aTransform.ref.value;
|
||||
const instanceCount = values.uInstanceCount.ref.value;
|
||||
|
||||
let interpolatedColors: Uint8Array | undefined;
|
||||
if (colorType === 'volume' || colorType === 'volumeInstance') {
|
||||
interpolatedColors = UsdzExporter.getInterpolatedColors(mesh!.vertices, mesh!.vertexCount, values, stride, colorType, webgl!);
|
||||
UsdzExporter.quantizeColors(interpolatedColors, mesh!.vertexCount);
|
||||
interpolatedColors = UsdzExporter.getInterpolatedColors(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType });
|
||||
}
|
||||
|
||||
let interpolatedOverpaint: Uint8Array | undefined;
|
||||
if (overpaintType === 'volumeInstance') {
|
||||
const stride = isGeoTexture ? 4 : 3;
|
||||
interpolatedOverpaint = UsdzExporter.getInterpolatedOverpaint(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType: overpaintType });
|
||||
}
|
||||
|
||||
let interpolatedTransparency: Uint8Array | undefined;
|
||||
if (transparencyType === 'volumeInstance') {
|
||||
const stride = isGeoTexture ? 4 : 3;
|
||||
interpolatedTransparency = UsdzExporter.getInterpolatedTransparency(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType: transparencyType });
|
||||
}
|
||||
|
||||
await ctx.update({ isIndeterminate: false, current: 0, max: instanceCount });
|
||||
@@ -121,6 +132,8 @@ def Material "material${materialKey}"
|
||||
StringBuilder.writeSafe(normalBuilder, ')');
|
||||
}
|
||||
|
||||
const geoData = { values, groups, vertexCount, instanceIndex, isGeoTexture };
|
||||
|
||||
// face
|
||||
for (let i = 0; i < drawCount; ++i) {
|
||||
const v = isGeoTexture ? i : indices![i];
|
||||
@@ -129,17 +142,21 @@ def Material "material${materialKey}"
|
||||
}
|
||||
|
||||
// color
|
||||
const faceIndicesByMaterial = new Map<number, number[]>();
|
||||
const quantizedColors = new Uint8Array(drawCount * 3);
|
||||
for (let i = 0; i < drawCount; i += 3) {
|
||||
const v = isGeoTexture ? i : indices![i];
|
||||
const color = UsdzExporter.getColor(values, groups, vertexCount, instanceIndex, isGeoTexture, interpolatedColors, v);
|
||||
const color = UsdzExporter.getColor(v, geoData, interpolatedColors, interpolatedOverpaint);
|
||||
Color.toArray(color, quantizedColors, i);
|
||||
}
|
||||
UsdzExporter.quantizeColors(quantizedColors, mesh!.vertexCount);
|
||||
|
||||
let alpha = uAlpha;
|
||||
if (dTransparency) {
|
||||
const group = isGeoTexture ? UsdzExporter.getGroup(groups, i) : groups[indices![i]];
|
||||
const transparency = tTransparency.array[instanceIndex * groupCount + group] / 255;
|
||||
alpha *= 1 - transparency;
|
||||
}
|
||||
// material
|
||||
const faceIndicesByMaterial = new Map<number, number[]>();
|
||||
for (let i = 0; i < drawCount; i += 3) {
|
||||
const color = Color.fromArray(quantizedColors, i);
|
||||
|
||||
const transparency = UsdzExporter.getTransparency(i, geoData, interpolatedTransparency);
|
||||
const alpha = Math.round(uAlpha * (1 - transparency) * 10) / 10; // quantized
|
||||
|
||||
this.addMaterial(color, alpha);
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ export class Mp4Controls extends PluginComponent {
|
||||
const filename = anim.anim.display.name.toLowerCase().replace(/\s/g, '-').replace(/[^a-z0-9_\-]/g, '');
|
||||
return { movie, filename: `${this.plugin.helpers.viewportScreenshot?.getFilename('')}_${filename}.mp4` };
|
||||
} catch (e) {
|
||||
this.plugin.log.error('' + e);
|
||||
this.plugin.log.error('Error during animation export');
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -115,7 +115,8 @@ export class Mp4EncoderUI extends CollapsableControls<{}, State> {
|
||||
this.setState({ busy: true });
|
||||
const data = await this.controls.render();
|
||||
this.setState({ busy: false, data });
|
||||
} catch {
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.setState({ busy: false });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/* eslint-disable */
|
||||
export type Maybe<T> = T | null;
|
||||
export type InputMaybe<T> = Maybe<T>;
|
||||
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
|
||||
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
|
||||
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
|
||||
// Generated on 2021-09-12T13:31:40-07:00
|
||||
// Generated on 2021-11-25T14:34:23-08:00
|
||||
|
||||
/** All built-in and custom scalars, mapped to their actual values */
|
||||
export type Scalars = {
|
||||
@@ -6769,7 +6770,7 @@ export type Query = {
|
||||
|
||||
/** Query root */
|
||||
export type QueryAssembliesArgs = {
|
||||
assembly_ids: ReadonlyArray<Maybe<Scalars['String']>>;
|
||||
assembly_ids: ReadonlyArray<InputMaybe<Scalars['String']>>;
|
||||
};
|
||||
|
||||
|
||||
@@ -6802,7 +6803,7 @@ export type QueryBranched_Entity_InstanceArgs = {
|
||||
|
||||
/** Query root */
|
||||
export type QueryBranched_Entity_InstancesArgs = {
|
||||
instance_ids: ReadonlyArray<Maybe<Scalars['String']>>;
|
||||
instance_ids: ReadonlyArray<InputMaybe<Scalars['String']>>;
|
||||
};
|
||||
|
||||
|
||||
@@ -6814,7 +6815,7 @@ export type QueryChem_CompArgs = {
|
||||
|
||||
/** Query root */
|
||||
export type QueryChem_CompsArgs = {
|
||||
comp_ids: ReadonlyArray<Maybe<Scalars['String']>>;
|
||||
comp_ids: ReadonlyArray<InputMaybe<Scalars['String']>>;
|
||||
};
|
||||
|
||||
|
||||
@@ -6852,7 +6853,7 @@ export type QueryNonpolymer_Entity_InstanceArgs = {
|
||||
|
||||
/** Query root */
|
||||
export type QueryNonpolymer_Entity_InstancesArgs = {
|
||||
instance_ids: ReadonlyArray<Maybe<Scalars['String']>>;
|
||||
instance_ids: ReadonlyArray<InputMaybe<Scalars['String']>>;
|
||||
};
|
||||
|
||||
|
||||
@@ -6878,7 +6879,7 @@ export type QueryPolymer_Entity_InstanceArgs = {
|
||||
|
||||
/** Query root */
|
||||
export type QueryPolymer_Entity_InstancesArgs = {
|
||||
instance_ids: ReadonlyArray<Maybe<Scalars['String']>>;
|
||||
instance_ids: ReadonlyArray<InputMaybe<Scalars['String']>>;
|
||||
};
|
||||
|
||||
|
||||
@@ -13144,4 +13145,4 @@ export type AssemblySymmetryQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type AssemblySymmetryQuery = { readonly assembly?: Maybe<{ readonly rcsb_struct_symmetry?: Maybe<ReadonlyArray<Maybe<{ readonly kind: string, readonly oligomeric_state: string, readonly stoichiometry: ReadonlyArray<Maybe<string>>, readonly symbol: string, readonly type: string, readonly clusters: ReadonlyArray<Maybe<{ readonly avg_rmsd?: Maybe<number>, readonly members: ReadonlyArray<Maybe<{ readonly asym_id: string, readonly pdbx_struct_oper_list_ids?: Maybe<ReadonlyArray<Maybe<string>>> }>> }>>, readonly rotation_axes?: Maybe<ReadonlyArray<Maybe<{ readonly order?: Maybe<number>, readonly start: ReadonlyArray<Maybe<number>>, readonly end: ReadonlyArray<Maybe<number>> }>>> }>>> }> };
|
||||
export type AssemblySymmetryQuery = { readonly assembly?: { readonly rcsb_struct_symmetry?: ReadonlyArray<{ readonly kind: string, readonly oligomeric_state: string, readonly stoichiometry: ReadonlyArray<string | null | undefined>, readonly symbol: string, readonly type: string, readonly clusters: ReadonlyArray<{ readonly avg_rmsd?: number | null | undefined, readonly members: ReadonlyArray<{ readonly asym_id: string, readonly pdbx_struct_oper_list_ids?: ReadonlyArray<string | null | undefined> | null | undefined } | null | undefined> } | null | undefined>, readonly rotation_axes?: ReadonlyArray<{ readonly order?: number | null | undefined, readonly start: ReadonlyArray<number | null | undefined>, readonly end: ReadonlyArray<number | null | undefined> } | null | undefined> | null | undefined } | null | undefined> | null | undefined } | null | undefined };
|
||||
|
||||
@@ -22,11 +22,11 @@ import { Helper } from '../helper/helper';
|
||||
|
||||
import { quad_vert } from '../../mol-gl/shader/quad.vert';
|
||||
import { depthMerge_frag } from '../../mol-gl/shader/depth-merge.frag';
|
||||
import { copy_frag } from '../../mol-gl/shader/copy.frag';
|
||||
import { StereoCamera } from '../camera/stereo';
|
||||
import { WboitPass } from './wboit';
|
||||
import { AntialiasingPass, PostprocessingPass, PostprocessingProps } from './postprocessing';
|
||||
import { MarkingPass, MarkingProps } from './marking';
|
||||
import { CopyRenderable, createCopyRenderable } from '../../mol-gl/compute/util';
|
||||
|
||||
const DepthMergeSchema = {
|
||||
...QuadSchema,
|
||||
@@ -53,27 +53,6 @@ function getDepthMergeRenderable(ctx: WebGLContext, depthTexturePrimitives: Text
|
||||
return createComputeRenderable(renderItem, values);
|
||||
}
|
||||
|
||||
const CopySchema = {
|
||||
...QuadSchema,
|
||||
tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
uTexSize: UniformSpec('v2'),
|
||||
};
|
||||
const CopyShaderCode = ShaderCode('copy', quad_vert, copy_frag);
|
||||
type CopyRenderable = ComputeRenderable<Values<typeof CopySchema>>
|
||||
|
||||
function getCopyRenderable(ctx: WebGLContext, colorTexture: Texture): CopyRenderable {
|
||||
const values: Values<typeof CopySchema> = {
|
||||
...QuadValues,
|
||||
tColor: ValueCell.create(colorTexture),
|
||||
uTexSize: ValueCell.create(Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())),
|
||||
};
|
||||
|
||||
const schema = { ...CopySchema };
|
||||
const renderItem = createComputeRenderItem(ctx, 'triangles', CopyShaderCode, schema, values);
|
||||
|
||||
return createComputeRenderable(renderItem, values);
|
||||
}
|
||||
|
||||
export class DrawPass {
|
||||
private readonly drawTarget: RenderTarget
|
||||
|
||||
@@ -128,8 +107,8 @@ export class DrawPass {
|
||||
this.postprocessing = new PostprocessingPass(webgl, this);
|
||||
this.antialiasing = new AntialiasingPass(webgl, this);
|
||||
|
||||
this.copyFboTarget = getCopyRenderable(webgl, this.colorTarget.texture);
|
||||
this.copyFboPostprocessing = getCopyRenderable(webgl, this.postprocessing.target.texture);
|
||||
this.copyFboTarget = createCopyRenderable(webgl, this.colorTarget.texture);
|
||||
this.copyFboPostprocessing = createCopyRenderable(webgl, this.postprocessing.target.texture);
|
||||
}
|
||||
|
||||
reset() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -15,6 +15,7 @@ import { ColorNames } from '../../mol-util/color/names';
|
||||
import { NullLocation } from '../../mol-model/location';
|
||||
import { UniformColorTheme } from '../../mol-theme/color/uniform';
|
||||
import { UniformSizeTheme } from '../../mol-theme/size/uniform';
|
||||
import { smoothstep } from '../../mol-math/interpolate';
|
||||
|
||||
export const VisualQualityInfo = {
|
||||
'custom': {},
|
||||
@@ -33,6 +34,40 @@ export const VisualQualityOptions = PD.arrayToOptions(VisualQualityNames);
|
||||
|
||||
//
|
||||
|
||||
export const ColorSmoothingParams = {
|
||||
smoothColors: PD.MappedStatic('auto', {
|
||||
auto: PD.Group({}),
|
||||
on: PD.Group({
|
||||
resolutionFactor: PD.Numeric(2, { min: 0.5, max: 6, step: 0.1 }),
|
||||
sampleStride: PD.Numeric(3, { min: 1, max: 12, step: 1 }),
|
||||
}),
|
||||
off: PD.Group({})
|
||||
}),
|
||||
};
|
||||
export type ColorSmoothingParams = typeof ColorSmoothingParams
|
||||
|
||||
export function hasColorSmoothingProp(props: PD.Values<any>): props is PD.Values<ColorSmoothingParams> {
|
||||
return !!props.smoothColors;
|
||||
}
|
||||
|
||||
export function getColorSmoothingProps(smoothColors: PD.Values<ColorSmoothingParams>['smoothColors'], preferSmoothing?: boolean, resolution?: number) {
|
||||
if ((smoothColors.name === 'on' || (smoothColors.name === 'auto' && preferSmoothing)) && resolution && resolution < 3) {
|
||||
let stride = 3;
|
||||
if (smoothColors.name === 'on') {
|
||||
resolution *= smoothColors.params.resolutionFactor;
|
||||
stride = smoothColors.params.sampleStride;
|
||||
} else {
|
||||
// https://graphtoy.com/?f1(x,t)=(2-smoothstep(0,1.1,x))*x&coords=0.7,0.6,1.8
|
||||
resolution *= 2 - smoothstep(0, 1.1, resolution);
|
||||
resolution = Math.max(0.5, resolution);
|
||||
if (resolution > 1.2) stride = 2;
|
||||
}
|
||||
return { resolution, stride };
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
export namespace BaseGeometry {
|
||||
export const Params = {
|
||||
alpha: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }, { label: 'Opacity', isEssential: true, description: 'How opaque/transparent the representation is rendered.' }),
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { MeshValues } from '../../../mol-gl/renderable/mesh';
|
||||
import { createTextureImage, TextureImage } from '../../../mol-gl/renderable/util';
|
||||
import { WebGLContext } from '../../../mol-gl/webgl/context';
|
||||
import { Texture } from '../../../mol-gl/webgl/texture';
|
||||
import { Box3D, Sphere3D } from '../../../mol-math/geometry';
|
||||
import { lerp } from '../../../mol-math/interpolate';
|
||||
import { Vec2, Vec3, Vec4 } from '../../../mol-math/linear-algebra';
|
||||
import { getVolumeTexture2dLayout } from '../../../mol-repr/volume/util';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
import { ValueCell } from '../../../mol-util';
|
||||
|
||||
interface ColorSmoothingInput {
|
||||
vertexCount: number
|
||||
@@ -24,10 +26,11 @@ interface ColorSmoothingInput {
|
||||
colorType: 'group' | 'groupInstance'
|
||||
boundingSphere: Sphere3D
|
||||
invariantBoundingSphere: Sphere3D
|
||||
itemSize: 4 | 3 | 1
|
||||
}
|
||||
|
||||
export function calcMeshColorSmoothing(input: ColorSmoothingInput, resolution: number, stride: number, webgl?: WebGLContext, texture?: Texture) {
|
||||
const { colorType, vertexCount, groupCount, positionBuffer, transformBuffer, groupBuffer } = input;
|
||||
const { colorType, vertexCount, groupCount, positionBuffer, transformBuffer, groupBuffer, itemSize } = input;
|
||||
|
||||
const isInstanceType = colorType.endsWith('Instance');
|
||||
const box = Box3D.fromSphere3D(Box3D(), isInstanceType ? input.boundingSphere : input.invariantBoundingSphere);
|
||||
@@ -43,7 +46,6 @@ export function calcMeshColorSmoothing(input: ColorSmoothingInput, resolution: n
|
||||
const { width, height } = getVolumeTexture2dLayout(gridDim);
|
||||
// console.log({ width, height, dim });
|
||||
|
||||
const itemSize = 3;
|
||||
const data = new Float32Array(width * height * itemSize);
|
||||
const count = new Float32Array(width * height);
|
||||
|
||||
@@ -78,10 +80,7 @@ export function calcMeshColorSmoothing(input: ColorSmoothingInput, resolution: n
|
||||
const z = Math.floor(vz);
|
||||
|
||||
// group colors
|
||||
const ci = i * groupCount + groupBuffer[j];
|
||||
const r = colors[ci * 3];
|
||||
const g = colors[ci * 3 + 1];
|
||||
const b = colors[ci * 3 + 2];
|
||||
const ci = (i * groupCount + groupBuffer[j]) * itemSize;
|
||||
|
||||
// Extents of grid to consider for this atom
|
||||
const begX = Math.max(0, x - p);
|
||||
@@ -106,10 +105,10 @@ export function calcMeshColorSmoothing(input: ColorSmoothingInput, resolution: n
|
||||
|
||||
const s = p - d;
|
||||
const index = getIndex(xi, yi, zi);
|
||||
data[index] += r * s;
|
||||
data[index + 1] += g * s;
|
||||
data[index + 2] += b * s;
|
||||
count[index / 3] += s;
|
||||
for (let k = 0; k < itemSize; ++k) {
|
||||
data[index + k] += colors[ci + k] * s;
|
||||
}
|
||||
count[index / itemSize] += s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,11 +116,11 @@ export function calcMeshColorSmoothing(input: ColorSmoothingInput, resolution: n
|
||||
}
|
||||
|
||||
for (let i = 0, il = count.length; i < il; ++i) {
|
||||
const i3 = i * 3;
|
||||
const is = i * itemSize;
|
||||
const c = count[i];
|
||||
grid[i3] = Math.round(data[i3] / c);
|
||||
grid[i3 + 1] = Math.round(data[i3 + 1] / c);
|
||||
grid[i3 + 2] = Math.round(data[i3 + 2] / c);
|
||||
for (let k = 0; k < itemSize; ++k) {
|
||||
grid[is + k] = Math.round(data[is + k] / c);
|
||||
}
|
||||
}
|
||||
|
||||
const gridTexDim = Vec2.create(width, height);
|
||||
@@ -129,12 +128,16 @@ export function calcMeshColorSmoothing(input: ColorSmoothingInput, resolution: n
|
||||
const type = isInstanceType ? 'volumeInstance' as const : 'volume' as const;
|
||||
|
||||
if (webgl) {
|
||||
if (!texture) texture = webgl.resources.texture('image-uint8', 'rgb', 'ubyte', 'linear');
|
||||
if (!texture) {
|
||||
const format = itemSize === 4 ? 'rgba' :
|
||||
itemSize === 3 ? 'rgb' : 'alpha';
|
||||
texture = webgl.resources.texture('image-uint8', format, 'ubyte', 'linear');
|
||||
}
|
||||
texture.load(textureImage);
|
||||
|
||||
return { kind: 'volume' as const, texture, gridTexDim, gridDim, gridTransform, type };
|
||||
} else {
|
||||
const interpolated = getTrilinearlyInterpolated({ vertexCount, instanceCount, transformBuffer, positionBuffer, colorType: type, grid, gridDim, gridTexDim, gridTransform, vertexStride: 3, colorStride: 3 });
|
||||
const interpolated = getTrilinearlyInterpolated({ vertexCount, instanceCount, transformBuffer, positionBuffer, colorType: type, grid, gridDim, gridTexDim, gridTransform, vertexStride: 3, colorStride: itemSize, outputStride: itemSize });
|
||||
|
||||
return {
|
||||
kind: 'vertex' as const,
|
||||
@@ -157,16 +160,24 @@ interface ColorInterpolationInput {
|
||||
gridTexDim: Vec2
|
||||
gridDim: Vec3
|
||||
gridTransform: Vec4
|
||||
vertexStride: number
|
||||
colorStride: number
|
||||
vertexStride: 3 | 4
|
||||
colorStride: 1 | 3 | 4
|
||||
outputStride: 1 | 3 | 4
|
||||
itemOffset?: 0 | 1 | 2 | 3
|
||||
}
|
||||
|
||||
export function getTrilinearlyInterpolated(input: ColorInterpolationInput): TextureImage<Uint8Array> {
|
||||
const { vertexCount, positionBuffer, transformBuffer, grid, gridDim, gridTexDim, gridTransform, vertexStride, colorStride } = input;
|
||||
|
||||
const itemOffset = input.itemOffset || 0;
|
||||
const outputStride = input.outputStride;
|
||||
if (outputStride + itemOffset > colorStride) {
|
||||
throw new Error('outputStride + itemOffset must NOT be larger than colorStride');
|
||||
}
|
||||
|
||||
const isInstanceType = input.colorType.endsWith('Instance');
|
||||
const instanceCount = isInstanceType ? input.instanceCount : 1;
|
||||
const image = createTextureImage(Math.max(1, instanceCount * vertexCount), 3, Uint8Array);
|
||||
const image = createTextureImage(Math.max(1, instanceCount * vertexCount), outputStride, Uint8Array);
|
||||
const { array } = image;
|
||||
|
||||
const [xn, yn] = gridDim;
|
||||
@@ -204,26 +215,144 @@ export function getTrilinearlyInterpolated(input: ColorInterpolationInput): Text
|
||||
const [x1, y1, z1] = v1;
|
||||
const [xd, yd, zd] = vd;
|
||||
|
||||
const s000 = Color.fromArray(grid, getIndex(x0, y0, z0));
|
||||
const s100 = Color.fromArray(grid, getIndex(x1, y0, z0));
|
||||
const s001 = Color.fromArray(grid, getIndex(x0, y0, z1));
|
||||
const s101 = Color.fromArray(grid, getIndex(x1, y0, z1));
|
||||
const s010 = Color.fromArray(grid, getIndex(x0, y1, z0));
|
||||
const s110 = Color.fromArray(grid, getIndex(x1, y1, z0));
|
||||
const s011 = Color.fromArray(grid, getIndex(x0, y1, z1));
|
||||
const s111 = Color.fromArray(grid, getIndex(x1, y1, z1));
|
||||
const i000 = getIndex(x0, y0, z0) + itemOffset;
|
||||
const i100 = getIndex(x1, y0, z0) + itemOffset;
|
||||
const i001 = getIndex(x0, y0, z1) + itemOffset;
|
||||
const i101 = getIndex(x1, y0, z1) + itemOffset;
|
||||
const i010 = getIndex(x0, y1, z0) + itemOffset;
|
||||
const i110 = getIndex(x1, y1, z0) + itemOffset;
|
||||
const i011 = getIndex(x0, y1, z1) + itemOffset;
|
||||
const i111 = getIndex(x1, y1, z1) + itemOffset;
|
||||
|
||||
const s00 = Color.interpolate(s000, s100, xd);
|
||||
const s01 = Color.interpolate(s001, s101, xd);
|
||||
const s10 = Color.interpolate(s010, s110, xd);
|
||||
const s11 = Color.interpolate(s011, s111, xd);
|
||||
const o = (i * vertexCount + j) * outputStride;
|
||||
|
||||
const s0 = Color.interpolate(s00, s10, yd);
|
||||
const s1 = Color.interpolate(s01, s11, yd);
|
||||
for (let k = 0; k < outputStride; ++k) {
|
||||
const s000 = grid[i000 + k];
|
||||
const s100 = grid[i100 + k];
|
||||
const s001 = grid[i001 + k];
|
||||
const s101 = grid[i101 + k];
|
||||
const s010 = grid[i010 + k];
|
||||
const s110 = grid[i110 + k];
|
||||
const s011 = grid[i011 + k];
|
||||
const s111 = grid[i111 + k];
|
||||
|
||||
Color.toArray(Color.interpolate(s0, s1, zd), array, (i * vertexCount + j) * 3);
|
||||
const s00 = lerp(s000, s100, xd);
|
||||
const s01 = lerp(s001, s101, xd);
|
||||
const s10 = lerp(s010, s110, xd);
|
||||
const s11 = lerp(s011, s111, xd);
|
||||
|
||||
const s0 = lerp(s00, s10, yd);
|
||||
const s1 = lerp(s01, s11, yd);
|
||||
|
||||
array[o + k] = lerp(s0, s1, zd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
function isSupportedColorType(x: string): x is 'group' | 'groupInstance' {
|
||||
return x === 'group' || x === 'groupInstance';
|
||||
}
|
||||
|
||||
export function applyMeshColorSmoothing(values: MeshValues, resolution: number, stride: number, webgl?: WebGLContext, colorTexture?: Texture) {
|
||||
if (!isSupportedColorType(values.dColorType.ref.value)) return;
|
||||
|
||||
const smoothingData = calcMeshColorSmoothing({
|
||||
vertexCount: values.uVertexCount.ref.value,
|
||||
instanceCount: values.uInstanceCount.ref.value,
|
||||
groupCount: values.uGroupCount.ref.value,
|
||||
transformBuffer: values.aTransform.ref.value,
|
||||
instanceBuffer: values.aInstance.ref.value,
|
||||
positionBuffer: values.aPosition.ref.value,
|
||||
groupBuffer: values.aGroup.ref.value,
|
||||
colorData: values.tColor.ref.value,
|
||||
colorType: values.dColorType.ref.value,
|
||||
boundingSphere: values.boundingSphere.ref.value,
|
||||
invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
|
||||
itemSize: 3
|
||||
}, resolution, stride, webgl, colorTexture);
|
||||
|
||||
if (smoothingData.kind === 'volume') {
|
||||
ValueCell.updateIfChanged(values.dColorType, smoothingData.type);
|
||||
ValueCell.update(values.tColorGrid, smoothingData.texture);
|
||||
ValueCell.update(values.uColorTexDim, smoothingData.gridTexDim);
|
||||
ValueCell.update(values.uColorGridDim, smoothingData.gridDim);
|
||||
ValueCell.update(values.uColorGridTransform, smoothingData.gridTransform);
|
||||
} else if (smoothingData.kind === 'vertex') {
|
||||
ValueCell.updateIfChanged(values.dColorType, smoothingData.type);
|
||||
ValueCell.update(values.tColor, smoothingData.texture);
|
||||
ValueCell.update(values.uColorTexDim, smoothingData.texDim);
|
||||
}
|
||||
}
|
||||
|
||||
function isSupportedOverpaintType(x: string): x is 'groupInstance' {
|
||||
return x === 'groupInstance';
|
||||
}
|
||||
|
||||
export function applyMeshOverpaintSmoothing(values: MeshValues, resolution: number, stride: number, webgl?: WebGLContext, colorTexture?: Texture) {
|
||||
if (!isSupportedOverpaintType(values.dOverpaintType.ref.value)) return;
|
||||
|
||||
const smoothingData = calcMeshColorSmoothing({
|
||||
vertexCount: values.uVertexCount.ref.value,
|
||||
instanceCount: values.uInstanceCount.ref.value,
|
||||
groupCount: values.uGroupCount.ref.value,
|
||||
transformBuffer: values.aTransform.ref.value,
|
||||
instanceBuffer: values.aInstance.ref.value,
|
||||
positionBuffer: values.aPosition.ref.value,
|
||||
groupBuffer: values.aGroup.ref.value,
|
||||
colorData: values.tOverpaint.ref.value,
|
||||
colorType: values.dOverpaintType.ref.value,
|
||||
boundingSphere: values.boundingSphere.ref.value,
|
||||
invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
|
||||
itemSize: 4
|
||||
}, resolution, stride, webgl, colorTexture);
|
||||
if (smoothingData.kind === 'volume') {
|
||||
ValueCell.updateIfChanged(values.dOverpaintType, smoothingData.type);
|
||||
ValueCell.update(values.tOverpaintGrid, smoothingData.texture);
|
||||
ValueCell.update(values.uOverpaintTexDim, smoothingData.gridTexDim);
|
||||
ValueCell.update(values.uOverpaintGridDim, smoothingData.gridDim);
|
||||
ValueCell.update(values.uOverpaintGridTransform, smoothingData.gridTransform);
|
||||
} else if (smoothingData.kind === 'vertex') {
|
||||
ValueCell.updateIfChanged(values.dOverpaintType, smoothingData.type);
|
||||
ValueCell.update(values.tOverpaint, smoothingData.texture);
|
||||
ValueCell.update(values.uOverpaintTexDim, smoothingData.texDim);
|
||||
}
|
||||
}
|
||||
|
||||
function isSupportedTransparencyType(x: string): x is 'groupInstance' {
|
||||
return x === 'groupInstance';
|
||||
}
|
||||
|
||||
export function applyMeshTransparencySmoothing(values: MeshValues, resolution: number, stride: number, webgl?: WebGLContext, colorTexture?: Texture) {
|
||||
if (!isSupportedTransparencyType(values.dTransparencyType.ref.value)) return;
|
||||
|
||||
const smoothingData = calcMeshColorSmoothing({
|
||||
vertexCount: values.uVertexCount.ref.value,
|
||||
instanceCount: values.uInstanceCount.ref.value,
|
||||
groupCount: values.uGroupCount.ref.value,
|
||||
transformBuffer: values.aTransform.ref.value,
|
||||
instanceBuffer: values.aInstance.ref.value,
|
||||
positionBuffer: values.aPosition.ref.value,
|
||||
groupBuffer: values.aGroup.ref.value,
|
||||
colorData: values.tTransparency.ref.value,
|
||||
colorType: values.dTransparencyType.ref.value,
|
||||
boundingSphere: values.boundingSphere.ref.value,
|
||||
invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
|
||||
itemSize: 1
|
||||
}, resolution, stride, webgl, colorTexture);
|
||||
if (smoothingData.kind === 'volume') {
|
||||
ValueCell.updateIfChanged(values.dTransparencyType, smoothingData.type);
|
||||
ValueCell.update(values.tTransparencyGrid, smoothingData.texture);
|
||||
ValueCell.update(values.uTransparencyTexDim, smoothingData.gridTexDim);
|
||||
ValueCell.update(values.uTransparencyGridDim, smoothingData.gridDim);
|
||||
ValueCell.update(values.uTransparencyGridTransform, smoothingData.gridTransform);
|
||||
} else if (smoothingData.kind === 'vertex') {
|
||||
ValueCell.updateIfChanged(values.dTransparencyType, smoothingData.type);
|
||||
ValueCell.update(values.tTransparency, smoothingData.texture);
|
||||
ValueCell.update(values.uTransparencyTexDim, smoothingData.texDim);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../mol-util/value-cell';
|
||||
import { Vec2 } from '../../mol-math/linear-algebra';
|
||||
import { Vec2, Vec3, Vec4 } from '../../mol-math/linear-algebra';
|
||||
import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
|
||||
|
||||
export type OverpaintData = {
|
||||
tOverpaint: ValueCell<TextureImage<Uint8Array>>
|
||||
uOverpaintTexDim: ValueCell<Vec2>
|
||||
dOverpaint: ValueCell<boolean>,
|
||||
|
||||
tOverpaintGrid: ValueCell<Texture>,
|
||||
uOverpaintGridDim: ValueCell<Vec3>,
|
||||
uOverpaintGridTransform: ValueCell<Vec4>,
|
||||
dOverpaintType: ValueCell<string>,
|
||||
}
|
||||
|
||||
export function applyOverpaintColor(array: Uint8Array, start: number, end: number, color: Color) {
|
||||
@@ -40,6 +46,11 @@ export function createOverpaint(count: number, overpaintData?: OverpaintData): O
|
||||
tOverpaint: ValueCell.create(overpaint),
|
||||
uOverpaintTexDim: ValueCell.create(Vec2.create(overpaint.width, overpaint.height)),
|
||||
dOverpaint: ValueCell.create(count > 0),
|
||||
|
||||
tOverpaintGrid: ValueCell.create(createNullTexture()),
|
||||
uOverpaintGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
|
||||
uOverpaintGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
|
||||
dOverpaintType: ValueCell.create('groupInstance'),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -55,6 +66,11 @@ export function createEmptyOverpaint(overpaintData?: OverpaintData): OverpaintDa
|
||||
tOverpaint: ValueCell.create(emptyOverpaintTexture),
|
||||
uOverpaintTexDim: ValueCell.create(Vec2.create(1, 1)),
|
||||
dOverpaint: ValueCell.create(false),
|
||||
|
||||
tOverpaintGrid: ValueCell.create(createNullTexture()),
|
||||
uOverpaintGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
|
||||
uOverpaintGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
|
||||
dOverpaintType: ValueCell.create('groupInstance'),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,8 @@ import { Vec2, Vec3, Vec4 } from '../../../mol-math/linear-algebra';
|
||||
import { Box3D, Sphere3D } from '../../../mol-math/geometry';
|
||||
import { accumulate_frag } from '../../../mol-gl/shader/compute/color-smoothing/accumulate.frag';
|
||||
import { accumulate_vert } from '../../../mol-gl/shader/compute/color-smoothing/accumulate.vert';
|
||||
import { TextureImage } from '../../../mol-gl/renderable/util';
|
||||
import { isWebGL2 } from '../../../mol-gl/webgl/compat';
|
||||
import { TextureMeshValues } from '../../../mol-gl/renderable/texture-mesh';
|
||||
|
||||
export const ColorAccumulateSchema = {
|
||||
drawCount: ValueSpec('number'),
|
||||
@@ -38,7 +39,7 @@ export const ColorAccumulateSchema = {
|
||||
tGroup: TextureSpec('texture', 'rgba', 'float', 'nearest'),
|
||||
|
||||
uColorTexDim: UniformSpec('v2'),
|
||||
tColor: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'),
|
||||
tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
dColorType: DefineSpec('string', ['group', 'groupInstance', 'vertex', 'vertexInstance']),
|
||||
|
||||
uCurrentSlice: UniformSpec('f'),
|
||||
@@ -50,6 +51,7 @@ export const ColorAccumulateSchema = {
|
||||
};
|
||||
type ColorAccumulateValues = Values<typeof ColorAccumulateSchema>
|
||||
const ColorAccumulateName = 'color-accumulate';
|
||||
const ColorCountName = 'color-count';
|
||||
|
||||
interface AccumulateInput {
|
||||
vertexCount: number
|
||||
@@ -59,7 +61,7 @@ interface AccumulateInput {
|
||||
instanceBuffer: Float32Array
|
||||
positionTexture: Texture
|
||||
groupTexture: Texture
|
||||
colorData: TextureImage<Uint8Array>
|
||||
colorData: Texture
|
||||
colorType: 'group' | 'groupInstance'
|
||||
}
|
||||
|
||||
@@ -96,7 +98,7 @@ function getAccumulateRenderable(ctx: WebGLContext, input: AccumulateInput, box:
|
||||
ValueCell.update(v.tPosition, input.positionTexture);
|
||||
ValueCell.update(v.tGroup, input.groupTexture);
|
||||
|
||||
ValueCell.update(v.uColorTexDim, Vec2.set(v.uColorTexDim.ref.value, input.colorData.width, input.colorData.height));
|
||||
ValueCell.update(v.uColorTexDim, Vec2.set(v.uColorTexDim.ref.value, input.colorData.getWidth(), input.colorData.getHeight()));
|
||||
ValueCell.update(v.tColor, input.colorData);
|
||||
ValueCell.updateIfChanged(v.dColorType, input.colorType);
|
||||
|
||||
@@ -135,7 +137,7 @@ function createAccumulateRenderable(ctx: WebGLContext, input: AccumulateInput, b
|
||||
tPosition: ValueCell.create(input.positionTexture),
|
||||
tGroup: ValueCell.create(input.groupTexture),
|
||||
|
||||
uColorTexDim: ValueCell.create(Vec2.create(input.colorData.width, input.colorData.height)),
|
||||
uColorTexDim: ValueCell.create(Vec2.create(input.colorData.getWidth(), input.colorData.getHeight())),
|
||||
tColor: ValueCell.create(input.colorData),
|
||||
dColorType: ValueCell.create(input.colorType),
|
||||
|
||||
@@ -148,7 +150,7 @@ function createAccumulateRenderable(ctx: WebGLContext, input: AccumulateInput, b
|
||||
};
|
||||
|
||||
const schema = { ...ColorAccumulateSchema };
|
||||
const shaderCode = ShaderCode('accumulate', accumulate_vert, accumulate_frag);
|
||||
const shaderCode = ShaderCode('accumulate', accumulate_vert, accumulate_frag, { drawBuffers: 'required' });
|
||||
const renderItem = createComputeRenderItem(ctx, 'points', shaderCode, schema, values);
|
||||
|
||||
return createComputeRenderable(renderItem, values);
|
||||
@@ -172,30 +174,33 @@ export const ColorNormalizeSchema = {
|
||||
...QuadSchema,
|
||||
|
||||
tColor: TextureSpec('texture', 'rgba', 'float', 'nearest'),
|
||||
tCount: TextureSpec('texture', 'alpha', 'float', 'nearest'),
|
||||
uTexSize: UniformSpec('v2'),
|
||||
|
||||
};
|
||||
type ColorNormalizeValues = Values<typeof ColorNormalizeSchema>
|
||||
const ColorNormalizeName = 'color-normalize';
|
||||
|
||||
function getNormalizeRenderable(ctx: WebGLContext, color: Texture): ComputeRenderable<ColorNormalizeValues> {
|
||||
function getNormalizeRenderable(ctx: WebGLContext, color: Texture, count: Texture): ComputeRenderable<ColorNormalizeValues> {
|
||||
if (ctx.namedComputeRenderables[ColorNormalizeName]) {
|
||||
const v = ctx.namedComputeRenderables[ColorNormalizeName].values as ColorNormalizeValues;
|
||||
|
||||
ValueCell.update(v.tColor, color);
|
||||
ValueCell.update(v.tCount, count);
|
||||
ValueCell.update(v.uTexSize, Vec2.set(v.uTexSize.ref.value, color.getWidth(), color.getHeight()));
|
||||
|
||||
ctx.namedComputeRenderables[ColorNormalizeName].update();
|
||||
} else {
|
||||
ctx.namedComputeRenderables[ColorNormalizeName] = createColorNormalizeRenderable(ctx, color);
|
||||
ctx.namedComputeRenderables[ColorNormalizeName] = createColorNormalizeRenderable(ctx, color, count);
|
||||
}
|
||||
return ctx.namedComputeRenderables[ColorNormalizeName];
|
||||
}
|
||||
|
||||
function createColorNormalizeRenderable(ctx: WebGLContext, color: Texture) {
|
||||
function createColorNormalizeRenderable(ctx: WebGLContext, color: Texture, count: Texture) {
|
||||
const values: ColorNormalizeValues = {
|
||||
...QuadValues,
|
||||
tColor: ValueCell.create(color),
|
||||
tCount: ValueCell.create(count),
|
||||
uTexSize: ValueCell.create(Vec2.create(color.getWidth(), color.getHeight())),
|
||||
};
|
||||
|
||||
@@ -247,6 +252,9 @@ interface ColorSmoothingInput extends AccumulateInput {
|
||||
}
|
||||
|
||||
export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolution: number, stride: number, webgl: WebGLContext, texture?: Texture) {
|
||||
const { drawBuffers } = webgl.extensions;
|
||||
if (!drawBuffers) throw new Error('need WebGL draw buffers');
|
||||
|
||||
const { gl, resources, state, extensions: { colorBufferHalfFloat, textureHalfFloat } } = webgl;
|
||||
|
||||
const isInstanceType = input.colorType.endsWith('Instance');
|
||||
@@ -263,29 +271,55 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
|
||||
const { texDimX: width, texDimY: height, texCols } = getTexture2dSize(gridDim);
|
||||
// console.log({ width, height, texCols, dim, resolution });
|
||||
|
||||
if (!webgl.namedTextures[ColorAccumulateName]) {
|
||||
webgl.namedTextures[ColorAccumulateName] = colorBufferHalfFloat && textureHalfFloat
|
||||
? resources.texture('image-float16', 'rgba', 'fp16', 'nearest')
|
||||
: resources.texture('image-float32', 'rgba', 'float', 'nearest');
|
||||
}
|
||||
const accumulateTexture = webgl.namedTextures[ColorAccumulateName];
|
||||
accumulateTexture.define(width, height);
|
||||
|
||||
const accumulateRenderable = getAccumulateRenderable(webgl, input, box, resolution, stride);
|
||||
|
||||
//
|
||||
|
||||
const { uCurrentSlice, uCurrentX, uCurrentY } = accumulateRenderable.values;
|
||||
|
||||
if (!webgl.namedFramebuffers[ColorAccumulateName]) {
|
||||
webgl.namedFramebuffers[ColorAccumulateName] = webgl.resources.framebuffer();
|
||||
}
|
||||
const framebuffer = webgl.namedFramebuffers[ColorAccumulateName];
|
||||
|
||||
if (isWebGL2(gl)) {
|
||||
if (!webgl.namedTextures[ColorAccumulateName]) {
|
||||
webgl.namedTextures[ColorAccumulateName] = colorBufferHalfFloat && textureHalfFloat
|
||||
? resources.texture('image-float16', 'rgba', 'fp16', 'nearest')
|
||||
: resources.texture('image-float32', 'rgba', 'float', 'nearest');
|
||||
}
|
||||
|
||||
if (!webgl.namedTextures[ColorCountName]) {
|
||||
webgl.namedTextures[ColorCountName] = resources.texture('image-float32', 'alpha', 'float', 'nearest');
|
||||
}
|
||||
} else {
|
||||
// in webgl1 drawbuffers must be in the same format for some reason
|
||||
// this is quite wasteful but good enough for medium size meshes
|
||||
|
||||
if (!webgl.namedTextures[ColorAccumulateName]) {
|
||||
webgl.namedTextures[ColorAccumulateName] = resources.texture('image-float32', 'rgba', 'float', 'nearest');
|
||||
}
|
||||
|
||||
if (!webgl.namedTextures[ColorCountName]) {
|
||||
webgl.namedTextures[ColorCountName] = resources.texture('image-float32', 'rgba', 'float', 'nearest');
|
||||
}
|
||||
}
|
||||
|
||||
const accumulateTexture = webgl.namedTextures[ColorAccumulateName];
|
||||
const countTexture = webgl.namedTextures[ColorCountName];
|
||||
|
||||
accumulateTexture.define(width, height);
|
||||
countTexture.define(width, height);
|
||||
|
||||
accumulateTexture.attachFramebuffer(framebuffer, 0);
|
||||
countTexture.attachFramebuffer(framebuffer, 1);
|
||||
|
||||
const accumulateRenderable = getAccumulateRenderable(webgl, input, box, resolution, stride);
|
||||
state.currentRenderItemId = -1;
|
||||
|
||||
framebuffer.bind();
|
||||
drawBuffers.drawBuffers([
|
||||
drawBuffers.COLOR_ATTACHMENT0,
|
||||
drawBuffers.COLOR_ATTACHMENT1,
|
||||
]);
|
||||
|
||||
const { uCurrentSlice, uCurrentX, uCurrentY } = accumulateRenderable.values;
|
||||
|
||||
setAccumulateDefaults(webgl);
|
||||
state.currentRenderItemId = -1;
|
||||
accumulateTexture.attachFramebuffer(framebuffer, 0);
|
||||
gl.viewport(0, 0, width, height);
|
||||
gl.scissor(0, 0, width, height);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
@@ -310,21 +344,31 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
|
||||
currX += dx;
|
||||
}
|
||||
|
||||
accumulateTexture.detachFramebuffer(framebuffer, 0);
|
||||
countTexture.detachFramebuffer(framebuffer, 1);
|
||||
drawBuffers.drawBuffers([gl.COLOR_ATTACHMENT0, gl.NONE]);
|
||||
|
||||
// const accImage = new Float32Array(width * height * 4);
|
||||
// accumulateTexture.attachFramebuffer(framebuffer, 0);
|
||||
// webgl.readPixels(0, 0, width, height, accImage);
|
||||
// console.log(accImage);
|
||||
// printTextureImage({ array: accImage, width, height }, 1 / 4);
|
||||
// printTextureImage({ array: accImage, width, height }, { scale: 1 });
|
||||
|
||||
// const cntImage = new Float32Array(width * height * 4);
|
||||
// countTexture.attachFramebuffer(framebuffer, 0);
|
||||
// webgl.readPixels(0, 0, width, height, cntImage);
|
||||
// console.log(cntImage);
|
||||
// printTextureImage({ array: cntImage, width, height }, { scale: 1 });
|
||||
|
||||
// normalize
|
||||
|
||||
if (!texture) texture = resources.texture('image-uint8', 'rgb', 'ubyte', 'linear');
|
||||
if (!texture) texture = resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
|
||||
texture.define(width, height);
|
||||
|
||||
const normalizeRenderable = getNormalizeRenderable(webgl, accumulateTexture);
|
||||
const normalizeRenderable = getNormalizeRenderable(webgl, accumulateTexture, countTexture);
|
||||
state.currentRenderItemId = -1;
|
||||
|
||||
setNormalizeDefaults(webgl);
|
||||
state.currentRenderItemId = -1;
|
||||
texture.attachFramebuffer(framebuffer, 0);
|
||||
gl.viewport(0, 0, width, height);
|
||||
gl.scissor(0, 0, width, height);
|
||||
@@ -335,10 +379,124 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
|
||||
// texture.attachFramebuffer(framebuffer, 0);
|
||||
// webgl.readPixels(0, 0, width, height, normImage);
|
||||
// console.log(normImage);
|
||||
// printTextureImage({ array: normImage, width, height }, 1 / 4);
|
||||
// printTextureImage({ array: normImage, width, height }, { scale: 1 });
|
||||
|
||||
const gridTransform = Vec4.create(min[0], min[1], min[2], scaleFactor);
|
||||
const type = isInstanceType ? 'volumeInstance' : 'volume';
|
||||
|
||||
return { texture, gridDim, gridTexDim: Vec2.create(width, height), gridTransform, type };
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const ColorSmoothingRgbName = 'color-smoothing-rgb';
|
||||
const ColorSmoothingRgbaName = 'color-smoothing-rgba';
|
||||
const ColorSmoothingAlphaName = 'color-smoothing-alpha';
|
||||
|
||||
function isSupportedColorType(x: string): x is 'group' | 'groupInstance' {
|
||||
return x === 'group' || x === 'groupInstance';
|
||||
}
|
||||
|
||||
export function applyTextureMeshColorSmoothing(values: TextureMeshValues, resolution: number, stride: number, webgl: WebGLContext, colorTexture?: Texture) {
|
||||
if (!isSupportedColorType(values.dColorType.ref.value)) return;
|
||||
|
||||
stride *= 3; // triple because TextureMesh is never indexed (no elements buffer)
|
||||
|
||||
if (!webgl.namedTextures[ColorSmoothingRgbName]) {
|
||||
webgl.namedTextures[ColorSmoothingRgbName] = webgl.resources.texture('image-uint8', 'rgb', 'ubyte', 'nearest');
|
||||
}
|
||||
const colorData = webgl.namedTextures[ColorSmoothingRgbName];
|
||||
colorData.load(values.tColor.ref.value);
|
||||
|
||||
const smoothingData = calcTextureMeshColorSmoothing({
|
||||
vertexCount: values.uVertexCount.ref.value,
|
||||
instanceCount: values.uInstanceCount.ref.value,
|
||||
groupCount: values.uGroupCount.ref.value,
|
||||
transformBuffer: values.aTransform.ref.value,
|
||||
instanceBuffer: values.aInstance.ref.value,
|
||||
positionTexture: values.tPosition.ref.value,
|
||||
groupTexture: values.tGroup.ref.value,
|
||||
colorData,
|
||||
colorType: values.dColorType.ref.value,
|
||||
boundingSphere: values.boundingSphere.ref.value,
|
||||
invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
|
||||
}, resolution, stride, webgl, colorTexture);
|
||||
|
||||
ValueCell.updateIfChanged(values.dColorType, smoothingData.type);
|
||||
ValueCell.update(values.tColorGrid, smoothingData.texture);
|
||||
ValueCell.update(values.uColorTexDim, smoothingData.gridTexDim);
|
||||
ValueCell.update(values.uColorGridDim, smoothingData.gridDim);
|
||||
ValueCell.update(values.uColorGridTransform, smoothingData.gridTransform);
|
||||
}
|
||||
|
||||
function isSupportedOverpaintType(x: string): x is 'groupInstance' {
|
||||
return x === 'groupInstance';
|
||||
}
|
||||
|
||||
export function applyTextureMeshOverpaintSmoothing(values: TextureMeshValues, resolution: number, stride: number, webgl: WebGLContext, colorTexture?: Texture) {
|
||||
if (!isSupportedOverpaintType(values.dOverpaintType.ref.value)) return;
|
||||
|
||||
stride *= 3; // triple because TextureMesh is never indexed (no elements buffer)
|
||||
|
||||
if (!webgl.namedTextures[ColorSmoothingRgbaName]) {
|
||||
webgl.namedTextures[ColorSmoothingRgbaName] = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
|
||||
}
|
||||
const colorData = webgl.namedTextures[ColorSmoothingRgbaName];
|
||||
colorData.load(values.tOverpaint.ref.value);
|
||||
|
||||
const smoothingData = calcTextureMeshColorSmoothing({
|
||||
vertexCount: values.uVertexCount.ref.value,
|
||||
instanceCount: values.uInstanceCount.ref.value,
|
||||
groupCount: values.uGroupCount.ref.value,
|
||||
transformBuffer: values.aTransform.ref.value,
|
||||
instanceBuffer: values.aInstance.ref.value,
|
||||
positionTexture: values.tPosition.ref.value,
|
||||
groupTexture: values.tGroup.ref.value,
|
||||
colorData,
|
||||
colorType: values.dOverpaintType.ref.value,
|
||||
boundingSphere: values.boundingSphere.ref.value,
|
||||
invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
|
||||
}, resolution, stride, webgl, colorTexture);
|
||||
|
||||
ValueCell.updateIfChanged(values.dOverpaintType, smoothingData.type);
|
||||
ValueCell.update(values.tOverpaintGrid, smoothingData.texture);
|
||||
ValueCell.update(values.uOverpaintTexDim, smoothingData.gridTexDim);
|
||||
ValueCell.update(values.uOverpaintGridDim, smoothingData.gridDim);
|
||||
ValueCell.update(values.uOverpaintGridTransform, smoothingData.gridTransform);
|
||||
}
|
||||
|
||||
function isSupportedTransparencyType(x: string): x is 'groupInstance' {
|
||||
return x === 'groupInstance';
|
||||
}
|
||||
|
||||
export function applyTextureMeshTransparencySmoothing(values: TextureMeshValues, resolution: number, stride: number, webgl: WebGLContext, colorTexture?: Texture) {
|
||||
if (!isSupportedTransparencyType(values.dTransparencyType.ref.value)) return;
|
||||
|
||||
stride *= 3; // triple because TextureMesh is never indexed (no elements buffer)
|
||||
|
||||
if (!webgl.namedTextures[ColorSmoothingAlphaName]) {
|
||||
webgl.namedTextures[ColorSmoothingAlphaName] = webgl.resources.texture('image-uint8', 'alpha', 'ubyte', 'nearest');
|
||||
}
|
||||
const colorData = webgl.namedTextures[ColorSmoothingAlphaName];
|
||||
colorData.load(values.tTransparency.ref.value);
|
||||
|
||||
const smoothingData = calcTextureMeshColorSmoothing({
|
||||
vertexCount: values.uVertexCount.ref.value,
|
||||
instanceCount: values.uInstanceCount.ref.value,
|
||||
groupCount: values.uGroupCount.ref.value,
|
||||
transformBuffer: values.aTransform.ref.value,
|
||||
instanceBuffer: values.aInstance.ref.value,
|
||||
positionTexture: values.tPosition.ref.value,
|
||||
groupTexture: values.tGroup.ref.value,
|
||||
colorData,
|
||||
colorType: values.dTransparencyType.ref.value,
|
||||
boundingSphere: values.boundingSphere.ref.value,
|
||||
invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
|
||||
}, resolution, stride, webgl, colorTexture);
|
||||
|
||||
ValueCell.updateIfChanged(values.dTransparencyType, smoothingData.type);
|
||||
ValueCell.update(values.tTransparencyGrid, smoothingData.texture);
|
||||
ValueCell.update(values.uTransparencyTexDim, smoothingData.gridTexDim);
|
||||
ValueCell.update(values.uTransparencyGridDim, smoothingData.gridDim);
|
||||
ValueCell.update(values.uTransparencyGridTransform, smoothingData.gridTransform);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ export interface TextureMesh {
|
||||
|
||||
readonly boundingSphere: Sphere3D
|
||||
|
||||
meta?: unknown
|
||||
readonly meta: { [k: string]: unknown }
|
||||
}
|
||||
|
||||
export namespace TextureMesh {
|
||||
@@ -92,6 +92,7 @@ export namespace TextureMesh {
|
||||
normalTexture: ValueCell.create(normalTexture),
|
||||
doubleBuffer: new DoubleBuffer(),
|
||||
boundingSphere: Sphere3D.clone(boundingSphere),
|
||||
meta: {}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -165,6 +166,8 @@ export namespace TextureMesh {
|
||||
dIgnoreLight: ValueCell.create(props.ignoreLight),
|
||||
dXrayShaded: ValueCell.create(props.xrayShaded),
|
||||
dGeoTexture: ValueCell.create(true),
|
||||
|
||||
meta: ValueCell.create(textureMesh.meta),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../mol-util/value-cell';
|
||||
import { Vec2 } from '../../mol-math/linear-algebra';
|
||||
import { Vec2, Vec3, Vec4 } from '../../mol-math/linear-algebra';
|
||||
import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
|
||||
import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
|
||||
|
||||
export type TransparencyData = {
|
||||
tTransparency: ValueCell<TextureImage<Uint8Array>>
|
||||
uTransparencyTexDim: ValueCell<Vec2>
|
||||
dTransparency: ValueCell<boolean>,
|
||||
transparencyAverage: ValueCell<number>,
|
||||
|
||||
tTransparencyGrid: ValueCell<Texture>,
|
||||
uTransparencyGridDim: ValueCell<Vec3>,
|
||||
uTransparencyGridTransform: ValueCell<Vec4>,
|
||||
dTransparencyType: ValueCell<string>,
|
||||
}
|
||||
|
||||
export function applyTransparencyValue(array: Uint8Array, start: number, end: number, value: number) {
|
||||
@@ -48,6 +54,11 @@ export function createTransparency(count: number, transparencyData?: Transparenc
|
||||
uTransparencyTexDim: ValueCell.create(Vec2.create(transparency.width, transparency.height)),
|
||||
dTransparency: ValueCell.create(count > 0),
|
||||
transparencyAverage: ValueCell.create(0),
|
||||
|
||||
tTransparencyGrid: ValueCell.create(createNullTexture()),
|
||||
uTransparencyGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
|
||||
uTransparencyGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
|
||||
dTransparencyType: ValueCell.create('groupInstance'),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -64,6 +75,11 @@ export function createEmptyTransparency(transparencyData?: TransparencyData): Tr
|
||||
uTransparencyTexDim: ValueCell.create(Vec2.create(1, 1)),
|
||||
dTransparency: ValueCell.create(false),
|
||||
transparencyAverage: ValueCell.create(0),
|
||||
|
||||
tTransparencyGrid: ValueCell.create(createNullTexture()),
|
||||
uTransparencyGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
|
||||
uTransparencyGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
|
||||
dTransparencyType: ValueCell.create('groupInstance'),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ describe('renderer', () => {
|
||||
scene.commit();
|
||||
expect(ctx.stats.resourceCounts.attribute).toBe(ctx.isWebGL2 ? 4 : 5);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(7);
|
||||
expect(ctx.stats.resourceCounts.vertexArray).toBe(8);
|
||||
expect(ctx.stats.resourceCounts.vertexArray).toBe(ctx.extensions.vertexArrayObject ? 8 : 0);
|
||||
expect(ctx.stats.resourceCounts.program).toBe(8);
|
||||
expect(ctx.stats.resourceCounts.shader).toBe(16);
|
||||
|
||||
|
||||
@@ -116,6 +116,9 @@ function setRenderingDefaults(ctx: WebGLContext) {
|
||||
}
|
||||
|
||||
export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
|
||||
const { drawBuffers } = ctx.extensions;
|
||||
if (!drawBuffers) throw new Error('need WebGL draw buffers');
|
||||
|
||||
const { gl, resources, extensions } = ctx;
|
||||
const { pyramidTex, height, levels, scale, count } = histogramPyramid;
|
||||
const width = pyramidTex.getWidth();
|
||||
@@ -173,9 +176,6 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
|
||||
const renderable = getIsosurfaceRenderable(ctx, pyramidTex, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup);
|
||||
ctx.state.currentRenderItemId = -1;
|
||||
|
||||
const { drawBuffers } = ctx.extensions;
|
||||
if (!drawBuffers) throw new Error('need WebGL draw buffers');
|
||||
|
||||
framebuffer.bind();
|
||||
drawBuffers.drawBuffers([
|
||||
drawBuffers.COLOR_ATTACHMENT0,
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { Texture } from '../../mol-gl/webgl/texture';
|
||||
import { PrintImageOptions, printTextureImage } from '../../mol-gl/renderable/util';
|
||||
import { defaults, ValueCell } from '../../mol-util';
|
||||
import { ValueSpec, AttributeSpec, UniformSpec, Values } from '../../mol-gl/renderable/schema';
|
||||
import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
|
||||
import { ValueCell } from '../../mol-util';
|
||||
import { ValueSpec, AttributeSpec, UniformSpec, Values, TextureSpec } from '../../mol-gl/renderable/schema';
|
||||
import { Vec2 } from '../../mol-math/linear-algebra';
|
||||
import { GLRenderingContext } from '../../mol-gl/webgl/compat';
|
||||
import { PixelData } from '../../mol-util/image';
|
||||
import { ShaderCode } from '../shader-code';
|
||||
import { copy_frag } from '../shader/copy.frag';
|
||||
import { quad_vert } from '../shader/quad.vert';
|
||||
import { createComputeRenderItem } from '../webgl/render-item';
|
||||
import { ComputeRenderable, createComputeRenderable } from '../renderable';
|
||||
|
||||
export const QuadPositions = new Float32Array([
|
||||
1.0, 1.0, -1.0, 1.0, -1.0, -1.0, // First triangle
|
||||
@@ -34,21 +36,57 @@ export const QuadValues: Values<typeof QuadSchema> = {
|
||||
|
||||
//
|
||||
|
||||
function getArrayForTexture(gl: GLRenderingContext, texture: Texture, size: number) {
|
||||
switch (texture.type) {
|
||||
case gl.UNSIGNED_BYTE: return new Uint8Array(size);
|
||||
case gl.FLOAT: return new Float32Array(size);
|
||||
}
|
||||
throw new Error('unknown/unsupported texture type');
|
||||
const CopySchema = {
|
||||
...QuadSchema,
|
||||
tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
uTexSize: UniformSpec('v2'),
|
||||
};
|
||||
const CopyShaderCode = ShaderCode('copy', quad_vert, copy_frag);
|
||||
export type CopyRenderable = ComputeRenderable<Values<typeof CopySchema>>
|
||||
|
||||
export function createCopyRenderable(ctx: WebGLContext, texture: Texture): CopyRenderable {
|
||||
const values: Values<typeof CopySchema> = {
|
||||
...QuadValues,
|
||||
tColor: ValueCell.create(texture),
|
||||
uTexSize: ValueCell.create(Vec2.create(texture.getWidth(), texture.getHeight())),
|
||||
};
|
||||
|
||||
const schema = { ...CopySchema };
|
||||
const renderItem = createComputeRenderItem(ctx, 'triangles', CopyShaderCode, schema, values);
|
||||
|
||||
return createComputeRenderable(renderItem, values);
|
||||
}
|
||||
|
||||
export function readTexture(ctx: WebGLContext, texture: Texture, width?: number, height?: number): PixelData {
|
||||
const SharedCopyName = 'shared-copy';
|
||||
|
||||
export function getSharedCopyRenderable(ctx: WebGLContext, texture: Texture) {
|
||||
if (!ctx.namedComputeRenderables[SharedCopyName]) {
|
||||
ctx.namedComputeRenderables[SharedCopyName] = createCopyRenderable(ctx, createNullTexture());
|
||||
}
|
||||
const copy = ctx.namedComputeRenderables[SharedCopyName] as CopyRenderable;
|
||||
ValueCell.update(copy.values.tColor, texture);
|
||||
ValueCell.update(copy.values.uTexSize, Vec2.set(copy.values.uTexSize.ref.value, texture.getWidth(), texture.getHeight()));
|
||||
copy.update();
|
||||
return copy;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const ReadTextureName = 'read-texture';
|
||||
const ReadAlphaTextureName = 'read-alpha-texture';
|
||||
|
||||
export function readTexture(ctx: WebGLContext, texture: Texture) {
|
||||
const { gl, resources } = ctx;
|
||||
width = defaults(width, texture.getWidth());
|
||||
height = defaults(height, texture.getHeight());
|
||||
const size = width * height * 4;
|
||||
const framebuffer = resources.framebuffer();
|
||||
const array = getArrayForTexture(gl, texture, size);
|
||||
if (texture.type !== gl.UNSIGNED_BYTE) throw new Error('unsupported texture type');
|
||||
|
||||
if (!ctx.namedFramebuffers[ReadTextureName]) {
|
||||
ctx.namedFramebuffers[ReadTextureName] = resources.framebuffer();
|
||||
}
|
||||
const framebuffer = ctx.namedFramebuffers[ReadTextureName];
|
||||
|
||||
const width = texture.getWidth();
|
||||
const height = texture.getHeight();
|
||||
const array = new Uint8Array(width * height * 4);
|
||||
framebuffer.bind();
|
||||
texture.attachFramebuffer(framebuffer, 0);
|
||||
ctx.readPixels(0, 0, width, height, array);
|
||||
@@ -56,8 +94,44 @@ export function readTexture(ctx: WebGLContext, texture: Texture, width?: number,
|
||||
return { array, width, height };
|
||||
}
|
||||
|
||||
export function printTexture(ctx: WebGLContext, texture: Texture, options: Partial<PrintImageOptions> = {}) {
|
||||
const pixelData = readTexture(ctx, texture);
|
||||
PixelData.flipY(pixelData);
|
||||
printTextureImage(pixelData, options);
|
||||
export function readAlphaTexture(ctx: WebGLContext, texture: Texture) {
|
||||
const { gl, state, resources } = ctx;
|
||||
if (texture.type !== gl.UNSIGNED_BYTE) throw new Error('unsupported texture type');
|
||||
|
||||
const width = texture.getWidth();
|
||||
const height = texture.getHeight();
|
||||
|
||||
const copy = getSharedCopyRenderable(ctx, texture);
|
||||
state.currentRenderItemId = -1;
|
||||
|
||||
if (!ctx.namedFramebuffers[ReadAlphaTextureName]) {
|
||||
ctx.namedFramebuffers[ReadAlphaTextureName] = resources.framebuffer();
|
||||
}
|
||||
const framebuffer = ctx.namedFramebuffers[ReadAlphaTextureName];
|
||||
framebuffer.bind();
|
||||
|
||||
if (!ctx.namedTextures[ReadAlphaTextureName]) {
|
||||
ctx.namedTextures[ReadAlphaTextureName] = resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
|
||||
}
|
||||
const copyTex = ctx.namedTextures[ReadAlphaTextureName];
|
||||
copyTex.define(width, height);
|
||||
copyTex.attachFramebuffer(framebuffer, 0);
|
||||
|
||||
state.disable(gl.CULL_FACE);
|
||||
state.enable(gl.BLEND);
|
||||
state.disable(gl.DEPTH_TEST);
|
||||
state.enable(gl.SCISSOR_TEST);
|
||||
state.depthMask(false);
|
||||
state.clearColor(0, 0, 0, 0);
|
||||
state.blendFunc(gl.ONE, gl.ONE);
|
||||
state.blendEquation(gl.FUNC_ADD);
|
||||
gl.viewport(0, 0, width, height);
|
||||
gl.scissor(0, 0, width, height);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
copy.render();
|
||||
|
||||
const array = new Uint8Array(width * height * 4);
|
||||
ctx.readPixels(0, 0, width, height, array);
|
||||
|
||||
return { array, width, height };
|
||||
}
|
||||
@@ -224,6 +224,11 @@ export const OverpaintSchema = {
|
||||
uOverpaintTexDim: UniformSpec('v2'),
|
||||
tOverpaint: TextureSpec('image-uint8', 'rgba', 'ubyte', 'nearest'),
|
||||
dOverpaint: DefineSpec('boolean'),
|
||||
|
||||
uOverpaintGridDim: UniformSpec('v3'),
|
||||
uOverpaintGridTransform: UniformSpec('v4'),
|
||||
tOverpaintGrid: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
|
||||
dOverpaintType: DefineSpec('string', ['groupInstance', 'volumeInstance']),
|
||||
} as const;
|
||||
export type OverpaintSchema = typeof OverpaintSchema
|
||||
export type OverpaintValues = Values<OverpaintSchema>
|
||||
@@ -233,6 +238,11 @@ export const TransparencySchema = {
|
||||
tTransparency: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
|
||||
dTransparency: DefineSpec('boolean'),
|
||||
transparencyAverage: ValueSpec('number'),
|
||||
|
||||
uTransparencyGridDim: UniformSpec('v3'),
|
||||
uTransparencyGridTransform: UniformSpec('v4'),
|
||||
tTransparencyGrid: TextureSpec('texture', 'alpha', 'ubyte', 'linear'),
|
||||
dTransparencyType: DefineSpec('string', ['groupInstance', 'volumeInstance']),
|
||||
} as const;
|
||||
export type TransparencySchema = typeof TransparencySchema
|
||||
export type TransparencyValues = Values<TransparencySchema>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { Renderable, RenderableState, createRenderable } from '../renderable';
|
||||
import { WebGLContext } from '../webgl/context';
|
||||
import { createGraphicsRenderItem } from '../webgl/render-item';
|
||||
import { GlobalUniformSchema, BaseSchema, DefineSpec, Values, InternalSchema, InternalValues, UniformSpec, TextureSpec, GlobalTextureSchema } from './schema';
|
||||
import { GlobalUniformSchema, BaseSchema, DefineSpec, Values, InternalSchema, InternalValues, UniformSpec, TextureSpec, GlobalTextureSchema, ValueSpec } from './schema';
|
||||
import { MeshShaderCode } from '../shader-code';
|
||||
import { ValueCell } from '../../mol-util';
|
||||
|
||||
@@ -24,6 +24,7 @@ export const TextureMeshSchema = {
|
||||
dIgnoreLight: DefineSpec('boolean'),
|
||||
dXrayShaded: DefineSpec('boolean'),
|
||||
dGeoTexture: DefineSpec('boolean'),
|
||||
meta: ValueSpec('unknown')
|
||||
};
|
||||
export type TextureMeshSchema = typeof TextureMeshSchema
|
||||
export type TextureMeshValues = Values<TextureMeshSchema>
|
||||
|
||||
@@ -320,6 +320,10 @@ namespace Renderer {
|
||||
}
|
||||
|
||||
if (r.values.dRenderMode) { // indicates direct-volume
|
||||
if ((variant[0] === 'p' || variant === 'depth') && r.values.dRenderMode.ref.value === 'volume') {
|
||||
return; // no picking/depth in volume mode
|
||||
}
|
||||
|
||||
// culling done in fragment shader
|
||||
state.disable(gl.CULL_FACE);
|
||||
state.frontFace(gl.CCW);
|
||||
|
||||
@@ -13,11 +13,11 @@ export const assign_color_varying = `
|
||||
#elif defined(dColorType_vertexInstance)
|
||||
vColor.rgb = readFromTexture(tColor, int(aInstance) * uVertexCount + VertexID, uColorTexDim).rgb;
|
||||
#elif defined(dColorType_volume)
|
||||
vec3 gridPos = (uColorGridTransform.w * (position - uColorGridTransform.xyz)) / uColorGridDim;
|
||||
vColor.rgb = texture3dFrom2dLinear(tColorGrid, gridPos, uColorGridDim, uColorTexDim).rgb;
|
||||
vec3 cgridPos = (uColorGridTransform.w * (position - uColorGridTransform.xyz)) / uColorGridDim;
|
||||
vColor.rgb = texture3dFrom2dLinear(tColorGrid, cgridPos, uColorGridDim, uColorTexDim).rgb;
|
||||
#elif defined(dColorType_volumeInstance)
|
||||
vec3 gridPos = (uColorGridTransform.w * (vModelPosition - uColorGridTransform.xyz)) / uColorGridDim;
|
||||
vColor.rgb = texture3dFrom2dLinear(tColorGrid, gridPos, uColorGridDim, uColorTexDim).rgb;
|
||||
vec3 cgridPos = (uColorGridTransform.w * (vModelPosition - uColorGridTransform.xyz)) / uColorGridDim;
|
||||
vColor.rgb = texture3dFrom2dLinear(tColorGrid, cgridPos, uColorGridDim, uColorTexDim).rgb;
|
||||
#endif
|
||||
|
||||
#ifdef dUsePalette
|
||||
@@ -25,7 +25,21 @@ export const assign_color_varying = `
|
||||
#endif
|
||||
|
||||
#ifdef dOverpaint
|
||||
vOverpaint = readFromTexture(tOverpaint, aInstance * float(uGroupCount) + group, uOverpaintTexDim);
|
||||
#if defined(dOverpaintType_groupInstance)
|
||||
vOverpaint = readFromTexture(tOverpaint, aInstance * float(uGroupCount) + group, uOverpaintTexDim);
|
||||
#elif defined(dOverpaintType_vertexInstance)
|
||||
vOverpaint = readFromTexture(tOverpaint, int(aInstance) * uVertexCount + VertexID, uOverpaintTexDim);
|
||||
#elif defined(dOverpaintType_volumeInstance)
|
||||
vec3 ogridPos = (uOverpaintGridTransform.w * (vModelPosition - uOverpaintGridTransform.xyz)) / uOverpaintGridDim;
|
||||
vOverpaint = texture3dFrom2dLinear(tOverpaintGrid, ogridPos, uOverpaintGridDim, uOverpaintTexDim);
|
||||
#endif
|
||||
|
||||
// pre-mix to avoid darkening due to empty overpaint
|
||||
#ifdef dColorType_uniform
|
||||
vOverpaint.rgb = mix(uColor.rgb, vOverpaint.rgb, vOverpaint.a);
|
||||
#else
|
||||
vOverpaint.rgb = mix(vColor.rgb, vOverpaint.rgb, vOverpaint.a);
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(dRenderVariant_pick)
|
||||
#if defined(dRenderVariant_pickObject)
|
||||
@@ -39,6 +53,14 @@ export const assign_color_varying = `
|
||||
|
||||
#ifdef dTransparency
|
||||
vGroup = group;
|
||||
vTransparency = readFromTexture(tTransparency, aInstance * float(uGroupCount) + group, uTransparencyTexDim).a;
|
||||
|
||||
#if defined(dTransparencyType_groupInstance)
|
||||
vTransparency = readFromTexture(tTransparency, aInstance * float(uGroupCount) + group, uTransparencyTexDim).a;
|
||||
#elif defined(dTransparencyType_vertexInstance)
|
||||
vTransparency = readFromTexture(tTransparency, int(aInstance) * uVertexCount + VertexID, uTransparencyTexDim).a;
|
||||
#elif defined(dTransparencyType_volumeInstance)
|
||||
vec3 tgridPos = (uTransparencyGridTransform.w * (vModelPosition - uTransparencyGridTransform.xyz)) / uTransparencyGridDim;
|
||||
vTransparency = texture3dFrom2dLinear(tTransparencyGrid, tgridPos, uTransparencyGridDim, uTransparencyTexDim).a;
|
||||
#endif
|
||||
#endif
|
||||
`;
|
||||
@@ -54,6 +54,9 @@ export const assign_material_color = `
|
||||
// apply screendoor transparency
|
||||
#if defined(dTransparency)
|
||||
float ta = 1.0 - vTransparency;
|
||||
#if defined(dRenderVariant_colorWboit)
|
||||
if (vTransparency < 0.2) ta = 1.0; // hard cutoff looks better with wboit
|
||||
#endif
|
||||
|
||||
#if defined(dRenderVariant_pick)
|
||||
if (ta < uPickingAlphaThreshold)
|
||||
|
||||
@@ -18,9 +18,17 @@ export const color_vert_params = `
|
||||
#endif
|
||||
|
||||
#ifdef dOverpaint
|
||||
varying vec4 vOverpaint;
|
||||
uniform vec2 uOverpaintTexDim;
|
||||
uniform sampler2D tOverpaint;
|
||||
#if defined(dOverpaintType_groupInstance) || defined(dOverpaintType_vertexInstance)
|
||||
varying vec4 vOverpaint;
|
||||
uniform vec2 uOverpaintTexDim;
|
||||
uniform sampler2D tOverpaint;
|
||||
#elif defined(dOverpaintType_volumeInstance)
|
||||
varying vec4 vOverpaint;
|
||||
uniform vec2 uOverpaintTexDim;
|
||||
uniform vec3 uOverpaintGridDim;
|
||||
uniform vec4 uOverpaintGridTransform;
|
||||
uniform sampler2D tOverpaintGrid;
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(dRenderVariant_pick)
|
||||
#if __VERSION__ == 100
|
||||
@@ -32,9 +40,17 @@ export const color_vert_params = `
|
||||
|
||||
#ifdef dTransparency
|
||||
varying float vGroup;
|
||||
varying float vTransparency;
|
||||
uniform vec2 uTransparencyTexDim;
|
||||
uniform sampler2D tTransparency;
|
||||
#if defined(dTransparencyType_groupInstance) || defined(dTransparencyType_vertexInstance)
|
||||
varying float vTransparency;
|
||||
uniform vec2 uTransparencyTexDim;
|
||||
uniform sampler2D tTransparency;
|
||||
#elif defined(dTransparencyType_volumeInstance)
|
||||
varying float vTransparency;
|
||||
uniform vec2 uTransparencyTexDim;
|
||||
uniform vec3 uTransparencyGridDim;
|
||||
uniform vec4 uTransparencyGridTransform;
|
||||
uniform sampler2D tTransparencyGrid;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef dUsePalette
|
||||
|
||||
@@ -8,7 +8,7 @@ export const accumulate_frag = `
|
||||
precision highp float;
|
||||
|
||||
varying vec3 vPosition;
|
||||
varying vec3 vColor;
|
||||
varying vec4 vColor;
|
||||
|
||||
uniform float uCurrentSlice;
|
||||
uniform float uCurrentX;
|
||||
@@ -23,6 +23,8 @@ void main() {
|
||||
float dist = distance(fragPos, vPosition);
|
||||
if (dist > p) discard;
|
||||
|
||||
gl_FragColor = vec4(vColor, 1.0) * (p - dist);
|
||||
float f = p - dist;
|
||||
gl_FragColor = vColor * f;
|
||||
gl_FragData[1] = vec4(f);
|
||||
}
|
||||
`;
|
||||
@@ -27,7 +27,7 @@ uniform vec2 uColorTexDim;
|
||||
uniform sampler2D tColor;
|
||||
|
||||
varying vec3 vPosition;
|
||||
varying vec3 vColor;
|
||||
varying vec4 vColor;
|
||||
|
||||
uniform vec3 uBboxSize;
|
||||
uniform vec3 uBboxMin;
|
||||
@@ -43,9 +43,9 @@ void main() {
|
||||
gl_Position = vec4(((position - uBboxMin) / uBboxSize) * 2.0 - 1.0, 1.0);
|
||||
|
||||
#if defined(dColorType_group)
|
||||
vColor = readFromTexture(tColor, group, uColorTexDim).rgb;
|
||||
vColor = readFromTexture(tColor, group, uColorTexDim);
|
||||
#elif defined(dColorType_groupInstance)
|
||||
vColor = readFromTexture(tColor, aInstance * float(uGroupCount) + group, uColorTexDim).rgb;
|
||||
vColor = readFromTexture(tColor, aInstance * float(uGroupCount) + group, uColorTexDim);
|
||||
#endif
|
||||
}
|
||||
`;
|
||||
@@ -9,12 +9,14 @@ precision highp float;
|
||||
precision highp sampler2D;
|
||||
|
||||
uniform sampler2D tColor;
|
||||
uniform sampler2D tCount;
|
||||
uniform vec2 uTexSize;
|
||||
|
||||
void main(void) {
|
||||
vec2 coords = gl_FragCoord.xy / uTexSize;
|
||||
vec4 color = texture2D(tColor, coords);
|
||||
float count = texture2D(tCount, coords).r;
|
||||
|
||||
gl_FragColor.rgb = color.rgb / color.a;
|
||||
gl_FragColor = color / count;
|
||||
}
|
||||
`;
|
||||
@@ -110,9 +110,10 @@ uniform mat4 uCartnToUnit;
|
||||
#endif
|
||||
|
||||
#ifdef dOverpaint
|
||||
varying vec4 vOverpaint;
|
||||
uniform vec2 uOverpaintTexDim;
|
||||
uniform sampler2D tOverpaint;
|
||||
#if defined(dOverpaintType_groupInstance) || defined(dOverpaintType_vertexInstance)
|
||||
uniform vec2 uOverpaintTexDim;
|
||||
uniform sampler2D tOverpaint;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -192,6 +193,8 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
|
||||
float nextValue;
|
||||
|
||||
vec3 color = vec3(0.45, 0.55, 0.8);
|
||||
vec4 overpaint = vec4(0.0);
|
||||
|
||||
vec3 gradient = vec3(1.0);
|
||||
vec3 dx = vec3(gradOffset * scaleVol.x, 0.0, 0.0);
|
||||
vec3 dy = vec3(0.0, gradOffset * scaleVol.y, 0.0);
|
||||
@@ -297,6 +300,16 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
|
||||
color = texture3dFrom1dTrilinear(tColor, isoPos, uGridDim, uColorTexDim, vInstance * float(uVertexCount)).rgb;
|
||||
#endif
|
||||
|
||||
#ifdef dOverpaint
|
||||
#if defined(dOverpaintType_groupInstance)
|
||||
overpaint = readFromTexture(tOverpaint, vInstance * float(uGroupCount) + group, uOverpaintTexDim);
|
||||
#elif defined(dOverpaintType_vertexInstance)
|
||||
overpaint = texture3dFrom1dTrilinear(tOverpaint, isoPos, uGridDim, uOverpaintTexDim, vInstance * float(uVertexCount)).rgb;
|
||||
#endif
|
||||
|
||||
color = mix(color, overpaint.rgb, overpaint.a);
|
||||
#endif
|
||||
|
||||
// handle flipping and negative isosurfaces
|
||||
#ifdef dFlipSided
|
||||
bool flipped = value < uIsoValue.y; // flipped
|
||||
|
||||
@@ -14,10 +14,7 @@ precision highp sampler2D;
|
||||
#include common_vert_params
|
||||
#include color_vert_params
|
||||
#include common_clip
|
||||
|
||||
#if defined(dColorType_grid)
|
||||
#include texture3d_from_2d_linear
|
||||
#endif
|
||||
#include texture3d_from_2d_linear
|
||||
|
||||
#ifdef dGeoTexture
|
||||
uniform vec2 uGeoTexDim;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { GLRenderingContext, COMPAT_instanced_arrays, COMPAT_standard_derivatives, COMPAT_vertex_array_object, getInstancedArrays, getStandardDerivatives, getVertexArrayObject, COMPAT_element_index_uint, getElementIndexUint, COMPAT_texture_float, getTextureFloat, COMPAT_texture_float_linear, getTextureFloatLinear, COMPAT_blend_minmax, getBlendMinMax, getFragDepth, COMPAT_frag_depth, COMPAT_color_buffer_float, getColorBufferFloat, COMPAT_draw_buffers, getDrawBuffers, getShaderTextureLod, COMPAT_shader_texture_lod, getDepthTexture, COMPAT_depth_texture, COMPAT_sRGB, getSRGB, getTextureHalfFloat, getTextureHalfFloatLinear, COMPAT_texture_half_float, COMPAT_texture_half_float_linear, COMPAT_color_buffer_half_float, getColorBufferHalfFloat } from './compat';
|
||||
import { GLRenderingContext, COMPAT_instanced_arrays, COMPAT_standard_derivatives, COMPAT_vertex_array_object, getInstancedArrays, getStandardDerivatives, COMPAT_element_index_uint, getElementIndexUint, COMPAT_texture_float, getTextureFloat, COMPAT_texture_float_linear, getTextureFloatLinear, COMPAT_blend_minmax, getBlendMinMax, getFragDepth, COMPAT_frag_depth, COMPAT_color_buffer_float, getColorBufferFloat, COMPAT_draw_buffers, getDrawBuffers, getShaderTextureLod, COMPAT_shader_texture_lod, getDepthTexture, COMPAT_depth_texture, COMPAT_sRGB, getSRGB, getTextureHalfFloat, getTextureHalfFloatLinear, COMPAT_texture_half_float, COMPAT_texture_half_float_linear, COMPAT_color_buffer_half_float, getColorBufferHalfFloat, getVertexArrayObject } from './compat';
|
||||
import { isDebugMode } from '../../mol-util/debug';
|
||||
|
||||
export type WebGLExtensions = {
|
||||
|
||||
@@ -290,11 +290,7 @@ export function createRenderItem<T extends string>(ctx: WebGLContext, drawMode:
|
||||
}
|
||||
|
||||
if (elementsBuffer && values.elements.ref.version !== versions.elements) {
|
||||
if (elementsBuffer.length >= values.elements.ref.value.length &&
|
||||
// whenever a VAO update will be triggered, also recreate elements
|
||||
// workaround for https://bugs.chromium.org/p/chromium/issues/detail?id=1272238
|
||||
!(valueChanges.attributes || valueChanges.defines)
|
||||
) {
|
||||
if (elementsBuffer.length >= values.elements.ref.value.length) {
|
||||
// console.log('elements array large enough to update', values.elements.ref.id, values.elements.ref.version);
|
||||
elementsBuffer.updateSubData(values.elements.ref.value, 0, elementsBuffer.length);
|
||||
} else {
|
||||
|
||||
@@ -133,7 +133,7 @@ export function createResources(gl: GLRenderingContext, state: WebGLState, stats
|
||||
return wrap('texture', createTexture(gl, extensions, kind, format, type, filter));
|
||||
},
|
||||
vertexArray: (program: Program, attributeBuffers: AttributeBuffers, elementsBuffer?: ElementsBuffer) => {
|
||||
return wrap('vertexArray', createVertexArray(extensions, program, attributeBuffers, elementsBuffer));
|
||||
return wrap('vertexArray', createVertexArray(gl, extensions, program, attributeBuffers, elementsBuffer));
|
||||
},
|
||||
|
||||
getByteCounts: () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -8,6 +8,7 @@ import { Program } from './program';
|
||||
import { ElementsBuffer, AttributeBuffers } from './buffer';
|
||||
import { WebGLExtensions } from './extensions';
|
||||
import { idFactory } from '../../mol-util/id-factory';
|
||||
import { GLRenderingContext } from './compat';
|
||||
|
||||
const getNextVertexArrayId = idFactory();
|
||||
|
||||
@@ -40,7 +41,7 @@ export interface VertexArray {
|
||||
destroy: () => void
|
||||
}
|
||||
|
||||
export function createVertexArray(extensions: WebGLExtensions, program: Program, attributeBuffers: AttributeBuffers, elementsBuffer?: ElementsBuffer): VertexArray {
|
||||
export function createVertexArray(gl: GLRenderingContext, extensions: WebGLExtensions, program: Program, attributeBuffers: AttributeBuffers, elementsBuffer?: ElementsBuffer): VertexArray {
|
||||
const id = getNextVertexArrayId();
|
||||
let vertexArray = getVertexArray(extensions);
|
||||
let vertexArrayObject = getVertexArrayObject(extensions);
|
||||
@@ -68,6 +69,13 @@ export function createVertexArray(extensions: WebGLExtensions, program: Program,
|
||||
},
|
||||
destroy: () => {
|
||||
if (destroyed) return;
|
||||
if (elementsBuffer) {
|
||||
// workaround for ANGLE/Chromium bug
|
||||
// - https://bugs.chromium.org/p/angleproject/issues/detail?id=6599
|
||||
// - https://bugs.chromium.org/p/chromium/issues/detail?id=1272238
|
||||
vertexArrayObject.bindVertexArray(vertexArray);
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
|
||||
}
|
||||
vertexArrayObject.deleteVertexArray(vertexArray);
|
||||
destroyed = true;
|
||||
}
|
||||
|
||||
@@ -332,6 +332,80 @@ M END
|
||||
$$$$
|
||||
`;
|
||||
|
||||
const V3000SdfString = `FYI-001
|
||||
FYICenter.com
|
||||
123456789012345678901234567890123456789012345678901234567890
|
||||
0 0 0 0 0 999 V3000
|
||||
M V30 BEGIN CTAB
|
||||
M V30 COUNTS 13 14 0 0 0
|
||||
M V30 BEGIN ATOM
|
||||
M V30 1 N 0.84 -0.16 0 0
|
||||
M V30 2 N 1.48 0.43 0 0
|
||||
M V30 3 N 0.09 0.27 0 0
|
||||
M V30 4 C 1.11 1.21 0 0
|
||||
M V30 5 C 0.27 1.12 0 0
|
||||
M V30 6 C 0.84 -1.03 0 0
|
||||
M V30 7 C 1.53 1.99 0 0
|
||||
M V30 8 Cl 1.07 2.74 0.01 0
|
||||
M V30 9 C 1.59 -1.46 0 0
|
||||
M V30 10 C 0.08 -1.46 0 0
|
||||
M V30 11 C 1.59 -2.33 0 0
|
||||
M V30 12 C 0.07 -2.32 0 0
|
||||
M V30 13 C 0.84 -2.76 0 0
|
||||
M V30 END ATOM
|
||||
M V30 BEGIN BOND
|
||||
M V30 1 1 2 1
|
||||
M V30 2 1 3 1
|
||||
M V30 3 1 6 1
|
||||
M V30 4 2 4 2
|
||||
M V30 5 2 5 3
|
||||
M V30 6 1 7 4
|
||||
M V30 7 1 4 5
|
||||
M V30 8 1 9 6
|
||||
M V30 9 2 10 6
|
||||
M V30 10 1 8 7
|
||||
M V30 11 2 11 9
|
||||
M V30 12 1 12 10
|
||||
M V30 13 1 13 11
|
||||
M V30 14 2 13 12
|
||||
M V30 END BOND
|
||||
M V30 END CTAB
|
||||
M END
|
||||
> <Comment>
|
||||
This is an SDF example.
|
||||
With a multi-line comment.
|
||||
|
||||
> <source>
|
||||
This was retrieved from biotech.fyicenter.com
|
||||
|
||||
$$$$
|
||||
L-Alanine
|
||||
GSMACCS-II07189510252D 1 0.00366 0.00000 0
|
||||
Figure 1, J. Chem. Inf. Comput. Sci., Vol 32, No. 3., 1992
|
||||
0 0 0 0 0 999 V3000
|
||||
M V30 BEGIN CTAB
|
||||
M V30 COUNTS 6 5 0 0 1
|
||||
M V30 BEGIN ATOM
|
||||
M V30 1 C -0.6622 0.5342 0 0 CFG=2
|
||||
M V30 2 C 0.6622 -0.3 0 0
|
||||
M V30 3 C -0.7207 2.0817 0 0 MASS=13
|
||||
M V30 4 N -1.8622 -0.3695 0 0 CHG=1
|
||||
M V30 5 O 0.622 -1.8037 0 0
|
||||
M V30 6 O 1.9464 0.4244 0 0 CHG=-1
|
||||
M V30 END ATOM
|
||||
M V30 BEGIN BOND
|
||||
M V30 1 1 1 2
|
||||
M V30 2 1 1 3 CFG=1
|
||||
M V30 3 1 1 4
|
||||
M V30 4 2 2 5
|
||||
M V30 5 1 2 6
|
||||
M V30 END BOND
|
||||
M V30 END CTAB
|
||||
M END
|
||||
|
||||
$$$$
|
||||
`;
|
||||
|
||||
describe('sdf reader', () => {
|
||||
it('basic', async () => {
|
||||
const parsed = await parseSdf(SdfString).run();
|
||||
@@ -383,4 +457,58 @@ describe('sdf reader', () => {
|
||||
expect(compound3.dataItems.dataHeader.value(21)).toBe('<PUBCHEM_COORDINATE_TYPE>');
|
||||
expect(compound3.dataItems.data.value(21)).toBe('2\n5\n10');
|
||||
});
|
||||
|
||||
it('v3000', async () => {
|
||||
const parsed = await parseSdf(V3000SdfString).run();
|
||||
if (parsed.isError) {
|
||||
throw new Error(parsed.message);
|
||||
}
|
||||
|
||||
expect(parsed.result.compounds.length).toBe(2);
|
||||
|
||||
const compound1 = parsed.result.compounds[0];
|
||||
expect(compound1.molFile.atoms.count).toBe(13);
|
||||
expect(compound1.molFile.atoms.x.rowCount).toBe(13);
|
||||
expect(compound1.molFile.atoms.y.rowCount).toBe(13);
|
||||
expect(compound1.molFile.atoms.z.rowCount).toBe(13);
|
||||
expect(compound1.molFile.atoms.type_symbol.rowCount).toBe(13);
|
||||
expect(compound1.molFile.bonds.count).toBe(14);
|
||||
expect(compound1.molFile.bonds.atomIdxA.rowCount).toBe(14);
|
||||
expect(compound1.molFile.bonds.atomIdxB.rowCount).toBe(14);
|
||||
expect(compound1.molFile.bonds.order.rowCount).toBe(14);
|
||||
|
||||
expect(compound1.molFile.atoms.x.value(7)).toBe(1.07);
|
||||
expect(compound1.molFile.atoms.y.value(7)).toBe(2.74);
|
||||
expect(compound1.molFile.atoms.z.value(7)).toBe(0.01);
|
||||
expect(compound1.molFile.atoms.type_symbol.value(7)).toBe('Cl');
|
||||
|
||||
expect(compound1.molFile.bonds.atomIdxA.value(10)).toBe(11);
|
||||
expect(compound1.molFile.bonds.atomIdxB.value(10)).toBe(9);
|
||||
expect(compound1.molFile.bonds.order.value(10)).toBe(2);
|
||||
|
||||
expect(compound1.dataItems.dataHeader.rowCount).toBe(2);
|
||||
expect(compound1.dataItems.data.rowCount).toBe(2);
|
||||
|
||||
expect(compound1.dataItems.dataHeader.value(0)).toBe('<Comment>');
|
||||
expect(compound1.dataItems.data.value(0)).toBe(`This is an SDF example.\nWith a multi-line comment.`);
|
||||
|
||||
expect(compound1.dataItems.dataHeader.value(1)).toBe('<source>');
|
||||
expect(compound1.dataItems.data.value(1)).toBe('This was retrieved from biotech.fyicenter.com');
|
||||
|
||||
const compound2 = parsed.result.compounds[1];
|
||||
expect(compound2.molFile.atoms.count).toBe(6);
|
||||
expect(compound2.molFile.bonds.count).toBe(5);
|
||||
|
||||
expect(compound2.molFile.atoms.x.value(4)).toBe(0.622);
|
||||
expect(compound2.molFile.atoms.y.value(4)).toBe(-1.8037);
|
||||
expect(compound2.molFile.atoms.z.value(4)).toBe(0);
|
||||
expect(compound2.molFile.atoms.type_symbol.value(4)).toBe('O');
|
||||
|
||||
expect(compound2.molFile.bonds.atomIdxA.value(1)).toBe(1);
|
||||
expect(compound2.molFile.bonds.atomIdxB.value(1)).toBe(3);
|
||||
expect(compound2.molFile.bonds.order.value(1)).toBe(1);
|
||||
|
||||
expect(compound2.dataItems.dataHeader.rowCount).toBe(0);
|
||||
expect(compound2.dataItems.data.rowCount).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.350, IHM 1.17, CARB draft.
|
||||
* Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.352, IHM 1.17, CARB draft.
|
||||
*
|
||||
* @author molstar/ciftools package
|
||||
*/
|
||||
@@ -487,7 +487,7 @@ export const BIRD_Schema = {
|
||||
/**
|
||||
* An identifier for the wwPDB site creating or modifying the molecule.
|
||||
*/
|
||||
processing_site: Aliased<'RCSB' | 'PDBe' | 'PDBJ' | 'BMRB' | 'PDBC'>(str),
|
||||
processing_site: Aliased<'RCSB' | 'PDBE' | 'PDBJ' | 'BMRB' | 'PDBC'>(str),
|
||||
/**
|
||||
* The action associated with this audit record.
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.350, IHM 1.17, CARB draft.
|
||||
* Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.352, IHM 1.17, CARB draft.
|
||||
*
|
||||
* @author molstar/ciftools package
|
||||
*/
|
||||
|
||||
@@ -114,7 +114,7 @@ export const CifCore_Schema = {
|
||||
* Abstracts. This ordering is used in _chemical_formula.moiety
|
||||
* and _chemical_formula.sum.
|
||||
*
|
||||
* _chemical_formula.iupac '[Mo (C O)4 (C18 H33 P)2]'
|
||||
* _chemical_formula.IUPAC '[Mo (C O)4 (C18 H33 P)2]'
|
||||
* _chemical_formula.moiety 'C40 H66 Mo O4 P2'
|
||||
* _chemical_formula.structural '((C O)4 (P (C6 H11)3)2)Mo'
|
||||
* _chemical_formula.sum 'C40 H66 Mo O4 P2'
|
||||
@@ -146,7 +146,7 @@ export const CifCore_Schema = {
|
||||
sum: str,
|
||||
/**
|
||||
* Mass corresponding to the formulae _chemical_formula.structural,
|
||||
* *_iupac, *_moiety or *_sum and, together with the Z value and cell
|
||||
* *_IUPAC, *_moiety or *_sum and, together with the Z value and cell
|
||||
* parameters yield the density given as _exptl_crystal.density_diffrn.
|
||||
*/
|
||||
weight: float,
|
||||
@@ -478,7 +478,7 @@ export const CifCore_Schema = {
|
||||
* A concatenated series of single-letter codes which indicate the
|
||||
* refinement restraints or constraints applied to this site. This
|
||||
* item should not be used. It has been replaced by
|
||||
* _atom_site.refinement_flags_posn, _adp and _occupancy. It is
|
||||
* _atom_site.refinement_flags_posn, _ADP and _occupancy. It is
|
||||
* retained in this dictionary only to provide compatibility with
|
||||
* legacy CIFs.
|
||||
*/
|
||||
@@ -505,9 +505,9 @@ export const CifCore_Schema = {
|
||||
* atomic displacement parameter, U(equiv), in angstroms squared,
|
||||
* calculated from anisotropic atomic displacement parameters.
|
||||
*
|
||||
* U(equiv) = (1/3) sum~i~[sum~j~(U^ij^ a*~i~ a*~j~ a~i~ a~j~)]
|
||||
* U(equiv) = (1/3) sum~i~[sum~j~(U^ij^ a*~i~ a*~j~ a~i~.a~j~)]
|
||||
*
|
||||
* a = the real-space cell lengths
|
||||
* a = the real-space cell vectors
|
||||
* a* = the reciprocal-space cell lengths
|
||||
* Ref: Fischer, R. X. & Tillmanns, E. (1988). Acta Cryst. C44, 775-776.
|
||||
*/
|
||||
@@ -683,12 +683,21 @@ export const CifCore_Aliases = {
|
||||
'geom_bond.distance': [
|
||||
'geom_bond_dist',
|
||||
],
|
||||
'audit.block_doi': [
|
||||
'audit_block_DOI',
|
||||
],
|
||||
'database_code.cod': [
|
||||
'database_code_COD',
|
||||
],
|
||||
'database_code.csd': [
|
||||
'database_code_CSD',
|
||||
],
|
||||
'database_code.depnum_ccdc_archive': [
|
||||
'database_code_depnum_CCDC_archive',
|
||||
],
|
||||
'database_code.depnum_ccdc_fiz': [
|
||||
'database_code_depnum_CCDC_fiz',
|
||||
],
|
||||
'database_code.icsd': [
|
||||
'database_code_ICSD',
|
||||
],
|
||||
@@ -699,6 +708,7 @@ export const CifCore_Aliases = {
|
||||
'database_code_NBS',
|
||||
],
|
||||
'atom_site.adp_type': [
|
||||
'atom_site_ADP_type',
|
||||
'atom_site_thermal_displace_type',
|
||||
],
|
||||
'atom_site.label': [
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.350, IHM 1.17, CARB draft.
|
||||
* Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.352, IHM 1.17, CARB draft.
|
||||
*
|
||||
* @author molstar/ciftools package
|
||||
*/
|
||||
|
||||
109
src/mol-io/reader/sdf/parser-v3-util.ts
Normal file
109
src/mol-io/reader/sdf/parser-v3-util.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { Column } from '../../../mol-data/db';
|
||||
import { MolFile } from '../mol/parser';
|
||||
import { Tokenizer, TokenBuilder, Tokens } from '../common/text/tokenizer';
|
||||
import { TokenColumnProvider as TokenColumn } from '../common/text/column/token';
|
||||
|
||||
export function isV3(
|
||||
versionLine: string
|
||||
): boolean {
|
||||
return versionLine.trim().endsWith('V3000');
|
||||
}
|
||||
|
||||
export function handleCountsV3(
|
||||
tokenizer: Tokenizer
|
||||
): { atomCount: number, bondCount: number } {
|
||||
const atomCount = TokenBuilder.create(tokenizer.data, 1);
|
||||
const bondCount = TokenBuilder.create(tokenizer.data, 1);
|
||||
|
||||
Tokenizer.eatLine(tokenizer); // BEGIN CTAB
|
||||
skipSingleValue(tokenizer); // M
|
||||
skipSingleValue(tokenizer); // V30
|
||||
skipSingleValue(tokenizer); // COUNTS
|
||||
|
||||
addSingleValue(tokenizer, atomCount);
|
||||
addSingleValue(tokenizer, bondCount);
|
||||
Tokenizer.eatLine(tokenizer);
|
||||
|
||||
return {
|
||||
atomCount: TokenColumn(atomCount)(Column.Schema.int).value(0),
|
||||
bondCount: TokenColumn(bondCount)(Column.Schema.int).value(0)
|
||||
};
|
||||
}
|
||||
|
||||
export function handleAtomsV3(
|
||||
tokenizer: Tokenizer,
|
||||
atomCount: number
|
||||
): MolFile['atoms'] {
|
||||
const x = TokenBuilder.create(tokenizer.data, atomCount * 2);
|
||||
const y = TokenBuilder.create(tokenizer.data, atomCount * 2);
|
||||
const z = TokenBuilder.create(tokenizer.data, atomCount * 2);
|
||||
const type_symbol = TokenBuilder.create(tokenizer.data, atomCount * 2);
|
||||
|
||||
for (let i = 0; i < atomCount; ++i) {
|
||||
Tokenizer.markLine(tokenizer);
|
||||
skipSingleValue(tokenizer); // M
|
||||
skipSingleValue(tokenizer); // V30
|
||||
skipSingleValue(tokenizer); // Index
|
||||
|
||||
const { position } = tokenizer;
|
||||
addSingleValue(tokenizer, type_symbol);
|
||||
addSingleValue(tokenizer, x);
|
||||
addSingleValue(tokenizer, y);
|
||||
addSingleValue(tokenizer, z);
|
||||
tokenizer.position = position;
|
||||
}
|
||||
Tokenizer.eatLine(tokenizer); // Previous Line
|
||||
Tokenizer.eatLine(tokenizer); // END ATOM
|
||||
|
||||
return {
|
||||
count: atomCount,
|
||||
x: TokenColumn(x)(Column.Schema.float),
|
||||
y: TokenColumn(y)(Column.Schema.float),
|
||||
z: TokenColumn(z)(Column.Schema.float),
|
||||
type_symbol: TokenColumn(type_symbol)(Column.Schema.str),
|
||||
};
|
||||
}
|
||||
|
||||
export function handleBondsV3(
|
||||
tokenizer: Tokenizer,
|
||||
bondCount: number
|
||||
): MolFile['bonds'] {
|
||||
const atomIdxA = TokenBuilder.create(tokenizer.data, bondCount * 2);
|
||||
const atomIdxB = TokenBuilder.create(tokenizer.data, bondCount * 2);
|
||||
const order = TokenBuilder.create(tokenizer.data, bondCount * 2);
|
||||
|
||||
for (let i = 0; i < bondCount; ++i) {
|
||||
Tokenizer.markLine(tokenizer);
|
||||
skipSingleValue(tokenizer); // M
|
||||
skipSingleValue(tokenizer); // V30
|
||||
skipSingleValue(tokenizer); // Index
|
||||
|
||||
const { position } = tokenizer;
|
||||
addSingleValue(tokenizer, order);
|
||||
addSingleValue(tokenizer, atomIdxA);
|
||||
addSingleValue(tokenizer, atomIdxB);
|
||||
tokenizer.position = position;
|
||||
}
|
||||
Tokenizer.eatLine(tokenizer); // Previous Line
|
||||
Tokenizer.eatLine(tokenizer); // END BOND
|
||||
|
||||
return {
|
||||
count: bondCount,
|
||||
atomIdxA: TokenColumn(atomIdxA)(Column.Schema.float),
|
||||
atomIdxB: TokenColumn(atomIdxB)(Column.Schema.float),
|
||||
order: TokenColumn(order)(Column.Schema.float),
|
||||
};
|
||||
}
|
||||
|
||||
function skipSingleValue(tokenizer: Tokenizer) {
|
||||
Tokenizer.skipWhitespace(tokenizer);
|
||||
Tokenizer.eatValue(tokenizer);
|
||||
}
|
||||
|
||||
function addSingleValue(tokenizer: Tokenizer, tokens: Tokens) {
|
||||
const { position: valueStart } = tokenizer;
|
||||
Tokenizer.skipWhitespace(tokenizer);
|
||||
Tokenizer.eatValue(tokenizer);
|
||||
Tokenizer.trim(tokenizer, valueStart, tokenizer.position);
|
||||
TokenBuilder.addUnchecked(tokens, tokenizer.tokenStart, tokenizer.tokenEnd);
|
||||
}
|
||||
@@ -11,8 +11,9 @@ import { Task } from '../../../mol-task';
|
||||
import { ReaderResult as Result } from '../result';
|
||||
import { Tokenizer, TokenBuilder } from '../common/text/tokenizer';
|
||||
import { TokenColumnProvider as TokenColumn } from '../common/text/column/token';
|
||||
import { handleAtomsV3, handleBondsV3, handleCountsV3, isV3 } from './parser-v3-util';
|
||||
|
||||
/** http://c4.cabrillo.edu/404/ctfile.pdf - page 41 */
|
||||
/** http://c4.cabrillo.edu/404/ctfile.pdf - page 41 & 79 */
|
||||
|
||||
export interface SdfFileCompound {
|
||||
readonly molFile: MolFile,
|
||||
@@ -66,14 +67,22 @@ function handleDataItems(tokenizer: Tokenizer): { dataHeader: Column<string>, da
|
||||
};
|
||||
}
|
||||
|
||||
function handleCountsV2(countsAndVersion: string): { atomCount: number, bondCount: number } {
|
||||
return {
|
||||
atomCount: +countsAndVersion.substr(0, 3),
|
||||
bondCount: +countsAndVersion.substr(3, 3)
|
||||
};
|
||||
}
|
||||
|
||||
function handleMolFile(tokenizer: Tokenizer) {
|
||||
const title = Tokenizer.readLine(tokenizer).trim();
|
||||
const program = Tokenizer.readLine(tokenizer).trim();
|
||||
const comment = Tokenizer.readLine(tokenizer).trim();
|
||||
|
||||
const counts = Tokenizer.readLine(tokenizer);
|
||||
const countsAndVersion = Tokenizer.readLine(tokenizer);
|
||||
const molIsV3 = isV3(countsAndVersion);
|
||||
|
||||
const atomCount = +counts.substr(0, 3), bondCount = +counts.substr(3, 3);
|
||||
const { atomCount, bondCount } = molIsV3 ? handleCountsV3(tokenizer) : handleCountsV2(countsAndVersion);
|
||||
|
||||
if (Number.isNaN(atomCount) || Number.isNaN(bondCount)) {
|
||||
// try to skip to next molecule
|
||||
@@ -84,8 +93,8 @@ function handleMolFile(tokenizer: Tokenizer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const atoms = handleAtoms(tokenizer, atomCount);
|
||||
const bonds = handleBonds(tokenizer, bondCount);
|
||||
const atoms = molIsV3 ? handleAtomsV3(tokenizer, atomCount) : handleAtoms(tokenizer, atomCount);
|
||||
const bonds = molIsV3 ? handleBondsV3(tokenizer, bondCount) : handleBonds(tokenizer, bondCount);
|
||||
const dataItems = handleDataItems(tokenizer);
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2020 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 David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
@@ -99,12 +99,12 @@ const residue = {
|
||||
secondary_structure_type: p(l => {
|
||||
if (!Unit.isAtomic(l.unit)) notAtomic();
|
||||
const secStruc = SecondaryStructureProvider.get(l.structure).value?.get(l.unit.invariantId);
|
||||
return secStruc?.type[l.unit.residueIndex[l.element]] ?? SecondaryStructureType.Flag.NA;
|
||||
return secStruc ? secStruc.type[secStruc.getIndex(l.unit.residueIndex[l.element])] : SecondaryStructureType.Flag.NA;
|
||||
}),
|
||||
secondary_structure_key: p(l => {
|
||||
if (!Unit.isAtomic(l.unit)) notAtomic();
|
||||
const secStruc = SecondaryStructureProvider.get(l.structure).value?.get(l.unit.invariantId);
|
||||
return secStruc?.key[l.unit.residueIndex[l.element]] ?? -1;
|
||||
return secStruc ? secStruc.key[secStruc.getIndex(l.unit.residueIndex[l.element])] : -1;
|
||||
}),
|
||||
chem_comp_type: p(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.properties.chemicalComponentMap.get(compId(l))!.type),
|
||||
};
|
||||
|
||||
@@ -53,7 +53,8 @@ export const OpenFiles = StateAction.build({
|
||||
await provider.visuals?.(plugin, parsed);
|
||||
}
|
||||
} catch (e) {
|
||||
plugin.log.error(e);
|
||||
console.error(e);
|
||||
plugin.log.error(`Error opening file '${file.name}'`);
|
||||
}
|
||||
}
|
||||
}).runInContext(taskCtx);
|
||||
@@ -88,7 +89,8 @@ export const DownloadFile = StateAction.build({
|
||||
await provider.visuals?.(plugin, parsed);
|
||||
}
|
||||
} catch (e) {
|
||||
plugin.log.error(e);
|
||||
console.error(e);
|
||||
plugin.log.error(`Error downloading '${typeof params.url === 'string' ? params.url : params.url.url}'`);
|
||||
}
|
||||
}).runInContext(taskCtx);
|
||||
}));
|
||||
@@ -33,7 +33,9 @@ async function tryObtainRecommendedIsoValue(plugin: PluginContext, volume?: Volu
|
||||
try {
|
||||
const absIsoLevel = await getContourLevelEmdb(plugin, ctx, entryId);
|
||||
RecommendedIsoValue.Provider.set(volume, Volume.IsoValue.absolute(absIsoLevel));
|
||||
} catch (e) { }
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
@@ -18,6 +18,7 @@ import { SetUtils } from '../../mol-util/set';
|
||||
import { PluginStateObject } from '../objects';
|
||||
import { StateTransforms } from '../transforms';
|
||||
import { ElementNames } from '../../mol-model/structure/model/properties/atomic/types';
|
||||
import { SecondaryStructureProvider } from '../../mol-model-props/computed/secondary-structure';
|
||||
|
||||
export enum StructureSelectionCategory {
|
||||
Type = 'Type',
|
||||
@@ -250,7 +251,12 @@ const helix = StructureSelectionQuery('Helix', MS.struct.modifier.union([
|
||||
MS.core.type.bitflags([SecondaryStructureType.Flag.Helix])
|
||||
])
|
||||
})
|
||||
]), { category: StructureSelectionCategory.Structure });
|
||||
]), {
|
||||
category: StructureSelectionCategory.Structure,
|
||||
ensureCustomProperties: (ctx: CustomProperty.Context, structure: Structure) => {
|
||||
return SecondaryStructureProvider.attach(ctx, structure);
|
||||
}
|
||||
});
|
||||
|
||||
const beta = StructureSelectionQuery('Beta Strand/Sheet', MS.struct.modifier.union([
|
||||
MS.struct.generator.atomGroups({
|
||||
@@ -260,7 +266,12 @@ const beta = StructureSelectionQuery('Beta Strand/Sheet', MS.struct.modifier.uni
|
||||
MS.core.type.bitflags([SecondaryStructureType.Flag.Beta])
|
||||
])
|
||||
})
|
||||
]), { category: StructureSelectionCategory.Structure });
|
||||
]), {
|
||||
category: StructureSelectionCategory.Structure,
|
||||
ensureCustomProperties: (ctx: CustomProperty.Context, structure: Structure) => {
|
||||
return SecondaryStructureProvider.attach(ctx, structure);
|
||||
}
|
||||
});
|
||||
|
||||
const water = StructureSelectionQuery('Water', MS.struct.modifier.union([
|
||||
MS.struct.generator.atomGroups({
|
||||
|
||||
@@ -261,7 +261,8 @@ class PluginStateSnapshotManager extends StatefulPluginComponent<{
|
||||
return this.setStateSnapshot(snapshot);
|
||||
}
|
||||
} catch (e) {
|
||||
this.plugin.log.error(`Reading state: ${e}`);
|
||||
console.error(e);
|
||||
this.plugin.log.error('Error reading state');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import { unwindStructureAssembly, explodeStructure, spinStructure, SpinStructure
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { Overpaint } from '../../mol-theme/overpaint';
|
||||
import { Transparency } from '../../mol-theme/transparency';
|
||||
import { BaseGeometry } from '../../mol-geo/geometry/base';
|
||||
import { BaseGeometry, hasColorSmoothingProp } from '../../mol-geo/geometry/base';
|
||||
import { Script } from '../../mol-script/script';
|
||||
import { UnitcellParams, UnitcellRepresentation, getUnitcellData } from '../../mol-repr/shape/model/unitcell';
|
||||
import { DistanceParams, DistanceRepresentation } from '../../mol-repr/shape/loci/distance';
|
||||
@@ -328,25 +328,31 @@ const OverpaintStructureRepresentation3DFromScript = PluginStateTransform.BuiltI
|
||||
},
|
||||
apply({ a, params }) {
|
||||
const structure = a.data.sourceData;
|
||||
const geometryVersion = a.data.repr.geometryVersion;
|
||||
const overpaint = Overpaint.ofScript(params.layers, structure);
|
||||
|
||||
return new SO.Molecule.Structure.Representation3DState({
|
||||
state: { overpaint },
|
||||
initialState: { overpaint: Overpaint.Empty },
|
||||
info: structure,
|
||||
info: { structure, geometryVersion },
|
||||
repr: a.data.repr
|
||||
}, { label: `Overpaint (${overpaint.layers.length} Layers)` });
|
||||
},
|
||||
update({ a, b, newParams, oldParams }) {
|
||||
const oldStructure = b.data.info as Structure;
|
||||
const info = b.data.info as { structure: Structure, geometryVersion: number };
|
||||
const newStructure = a.data.sourceData;
|
||||
if (newStructure !== oldStructure) return StateTransformer.UpdateResult.Recreate;
|
||||
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 oldOverpaint = b.data.state.overpaint!;
|
||||
const newOverpaint = Overpaint.ofScript(newParams.layers, newStructure);
|
||||
if (Overpaint.areEqual(oldOverpaint, newOverpaint)) return StateTransformer.UpdateResult.Unchanged;
|
||||
|
||||
info.geometryVersion = newGeometryVersion;
|
||||
b.data.state.overpaint = newOverpaint;
|
||||
b.data.repr = a.data.repr;
|
||||
b.label = `Overpaint (${newOverpaint.layers.length} Layers)`;
|
||||
@@ -380,25 +386,31 @@ const OverpaintStructureRepresentation3DFromBundle = PluginStateTransform.BuiltI
|
||||
},
|
||||
apply({ a, params }) {
|
||||
const structure = a.data.sourceData;
|
||||
const geometryVersion = a.data.repr.geometryVersion;
|
||||
const overpaint = Overpaint.ofBundle(params.layers, structure);
|
||||
|
||||
return new SO.Molecule.Structure.Representation3DState({
|
||||
state: { overpaint },
|
||||
initialState: { overpaint: Overpaint.Empty },
|
||||
info: structure,
|
||||
info: { structure, geometryVersion },
|
||||
repr: a.data.repr
|
||||
}, { label: `Overpaint (${overpaint.layers.length} Layers)` });
|
||||
},
|
||||
update({ a, b, newParams, oldParams }) {
|
||||
const oldStructure = b.data.info as Structure;
|
||||
const info = b.data.info as { structure: Structure, geometryVersion: number };
|
||||
const newStructure = a.data.sourceData;
|
||||
if (newStructure !== oldStructure) return StateTransformer.UpdateResult.Recreate;
|
||||
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 oldOverpaint = b.data.state.overpaint!;
|
||||
const newOverpaint = Overpaint.ofBundle(newParams.layers, newStructure);
|
||||
if (Overpaint.areEqual(oldOverpaint, newOverpaint)) return StateTransformer.UpdateResult.Unchanged;
|
||||
|
||||
info.geometryVersion = newGeometryVersion;
|
||||
b.data.state.overpaint = newOverpaint;
|
||||
b.data.repr = a.data.repr;
|
||||
b.label = `Overpaint (${newOverpaint.layers.length} Layers)`;
|
||||
@@ -429,24 +441,31 @@ const TransparencyStructureRepresentation3DFromScript = PluginStateTransform.Bui
|
||||
},
|
||||
apply({ a, params }) {
|
||||
const structure = a.data.sourceData;
|
||||
const geometryVersion = a.data.repr.geometryVersion;
|
||||
const transparency = Transparency.ofScript(params.layers, structure);
|
||||
|
||||
return new SO.Molecule.Structure.Representation3DState({
|
||||
state: { transparency },
|
||||
initialState: { transparency: Transparency.Empty },
|
||||
info: structure,
|
||||
info: { structure, geometryVersion },
|
||||
repr: a.data.repr
|
||||
}, { label: `Transparency (${transparency.layers.length} Layers)` });
|
||||
},
|
||||
update({ a, b, newParams, oldParams }) {
|
||||
const structure = b.data.info as Structure;
|
||||
if (a.data.sourceData !== structure) return StateTransformer.UpdateResult.Recreate;
|
||||
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 oldTransparency = b.data.state.transparency!;
|
||||
const newTransparency = Transparency.ofScript(newParams.layers, structure);
|
||||
const newTransparency = Transparency.ofScript(newParams.layers, newStructure);
|
||||
if (Transparency.areEqual(oldTransparency, newTransparency)) return StateTransformer.UpdateResult.Unchanged;
|
||||
|
||||
info.geometryVersion = newGeometryVersion;
|
||||
b.data.state.transparency = newTransparency;
|
||||
b.data.repr = a.data.repr;
|
||||
b.label = `Transparency (${newTransparency.layers.length} Layers)`;
|
||||
@@ -478,24 +497,31 @@ const TransparencyStructureRepresentation3DFromBundle = PluginStateTransform.Bui
|
||||
},
|
||||
apply({ a, params }) {
|
||||
const structure = a.data.sourceData;
|
||||
const geometryVersion = a.data.repr.geometryVersion;
|
||||
const transparency = Transparency.ofBundle(params.layers, structure);
|
||||
|
||||
return new SO.Molecule.Structure.Representation3DState({
|
||||
state: { transparency },
|
||||
initialState: { transparency: Transparency.Empty },
|
||||
info: structure,
|
||||
info: { structure, geometryVersion },
|
||||
repr: a.data.repr
|
||||
}, { label: `Transparency (${transparency.layers.length} Layers)` });
|
||||
},
|
||||
update({ a, b, newParams, oldParams }) {
|
||||
const structure = b.data.info as Structure;
|
||||
if (a.data.sourceData !== structure) return StateTransformer.UpdateResult.Recreate;
|
||||
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 oldTransparency = b.data.state.transparency!;
|
||||
const newTransparency = Transparency.ofBundle(newParams.layers, structure);
|
||||
const newTransparency = Transparency.ofBundle(newParams.layers, newStructure);
|
||||
if (Transparency.areEqual(oldTransparency, newTransparency)) return StateTransformer.UpdateResult.Unchanged;
|
||||
|
||||
info.geometryVersion = newGeometryVersion;
|
||||
b.data.state.transparency = newTransparency;
|
||||
b.data.repr = a.data.repr;
|
||||
b.label = `Transparency (${newTransparency.layers.length} Layers)`;
|
||||
|
||||
@@ -157,7 +157,8 @@ abstract class TransformControlBase<P, S extends TransformControlBase.ComponentS
|
||||
this.setState({ busy: true });
|
||||
try {
|
||||
await this.applyAction();
|
||||
} catch {
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
// eat errors because they should be handled elsewhere
|
||||
} finally {
|
||||
this.props.onApply?.();
|
||||
|
||||
@@ -246,7 +246,8 @@ export class RemoteStateSnapshots extends PluginUIComponent<
|
||||
|
||||
if (this._mounted) this.setState({ entries: entries.asImmutable(), isBusy: false });
|
||||
} catch (e) {
|
||||
this.plugin.log.error('Fetching Remote Snapshots: ' + e);
|
||||
console.error(e);
|
||||
this.plugin.log.error('Error fetching remote snapshots');
|
||||
if (this._mounted) this.setState({ entries: OrderedMap(), isBusy: false });
|
||||
}
|
||||
}
|
||||
@@ -294,7 +295,9 @@ export class RemoteStateSnapshots extends PluginUIComponent<
|
||||
|
||||
try {
|
||||
await fetch(entry.removeUrl);
|
||||
} catch { }
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -401,7 +401,8 @@ function ResidueListSelectionHelper({ modifier, plugin, close }: { modifier: Str
|
||||
const query = compileIdListSelection(state.identifiers, state.idType);
|
||||
plugin.managers.structure.selection.fromCompiledQuery(modifier, query, false);
|
||||
} catch (e) {
|
||||
plugin.log.error(`Failed to create selection: ${e}`);
|
||||
console.error(e);
|
||||
plugin.log.error('Failed to create selection');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ import { BaseGeometry } from '../mol-geo/geometry/base';
|
||||
import { Visual } from './visual';
|
||||
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';
|
||||
|
||||
export type RepresentationProps = { [k: string]: any }
|
||||
|
||||
@@ -143,6 +145,7 @@ interface Representation<D, P extends PD.Params = {}, S extends Representation.S
|
||||
/** Number of addressable groups in all visuals of the representation */
|
||||
readonly groupCount: number
|
||||
readonly renderObjects: ReadonlyArray<GraphicsRenderObject>
|
||||
readonly geometryVersion: number
|
||||
readonly props: Readonly<PD.Values<P>>
|
||||
readonly params: Readonly<P>
|
||||
readonly state: Readonly<S>
|
||||
@@ -215,7 +218,7 @@ namespace Representation {
|
||||
|
||||
export type Any = Representation<any, any, any>
|
||||
export const Empty: Any = {
|
||||
label: '', groupCount: 0, renderObjects: [], props: {}, params: {}, updated: new Subject(), state: createState(), theme: Theme.createEmpty(),
|
||||
label: '', groupCount: 0, renderObjects: [], geometryVersion: -1, props: {}, params: {}, updated: new Subject(), state: createState(), theme: Theme.createEmpty(),
|
||||
createOrUpdate: () => Task.constant('', undefined),
|
||||
setState: () => {},
|
||||
setTheme: () => {},
|
||||
@@ -226,9 +229,32 @@ namespace Representation {
|
||||
|
||||
export type Def<D, P extends PD.Params = {}, S extends State = State> = { [k: string]: RepresentationFactory<D, P, S> }
|
||||
|
||||
export class GeometryState {
|
||||
private curr = new Set<number>();
|
||||
private next = new Set<number>();
|
||||
|
||||
private _version = -1;
|
||||
get version() {
|
||||
return this._version;
|
||||
}
|
||||
|
||||
add(id: number, version: number) {
|
||||
this.next.add(cantorPairing(id, version));
|
||||
}
|
||||
|
||||
snapshot() {
|
||||
if (!SetUtils.areEqual(this.curr, this.next)) {
|
||||
this._version += 1;
|
||||
}
|
||||
[this.curr, this.next] = [this.next, this.curr];
|
||||
this.next.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export function createMulti<D, P extends PD.Params = {}, S extends State = State>(label: string, ctx: RepresentationContext, getParams: RepresentationParamsGetter<D, P>, stateBuilder: StateBuilder<S>, reprDefs: Def<D, P>): Representation<D, P, S> {
|
||||
let version = 0;
|
||||
const updated = new Subject<number>();
|
||||
const geometryState = new GeometryState();
|
||||
const currentState = stateBuilder.create();
|
||||
let currentTheme = Theme.createEmpty();
|
||||
|
||||
@@ -271,6 +297,7 @@ namespace Representation {
|
||||
}
|
||||
return renderObjects;
|
||||
},
|
||||
get geometryVersion() { return geometryState.version; },
|
||||
get props() { return currentProps; },
|
||||
get params() { return currentParams; },
|
||||
createOrUpdate: (props: Partial<P> = {}, data?: D) => {
|
||||
@@ -288,7 +315,9 @@ namespace Representation {
|
||||
if (!visuals || visuals.includes(reprMap[i])) {
|
||||
await reprList[i].createOrUpdate(currentProps, currentData).runInContext(runtime);
|
||||
}
|
||||
geometryState.add(i, reprList[i].geometryVersion);
|
||||
}
|
||||
geometryState.snapshot();
|
||||
updated.next(version++);
|
||||
});
|
||||
},
|
||||
@@ -314,7 +343,7 @@ namespace Representation {
|
||||
setState: (state: Partial<S>) => {
|
||||
stateBuilder.update(currentState, state);
|
||||
for (let i = 0, il = reprList.length; i < il; ++i) {
|
||||
reprList[i].setState(currentState);
|
||||
reprList[i].setState(state); // only set the new (partial) state
|
||||
}
|
||||
},
|
||||
setTheme: (theme: Theme) => {
|
||||
@@ -334,6 +363,7 @@ namespace Representation {
|
||||
export function fromRenderObject(label: string, renderObject: GraphicsRenderObject): Representation<GraphicsRenderObject, BaseGeometry.Params> {
|
||||
let version = 0;
|
||||
const updated = new Subject<number>();
|
||||
const geometryState = new GeometryState();
|
||||
const currentState = Representation.createState();
|
||||
const currentTheme = Theme.createEmpty();
|
||||
|
||||
@@ -345,6 +375,7 @@ namespace Representation {
|
||||
updated,
|
||||
get groupCount() { return renderObject.values.uGroupCount.ref.value; },
|
||||
get renderObjects() { return [renderObject]; },
|
||||
get geometryVersion() { return geometryState.version; },
|
||||
get props() { return currentProps; },
|
||||
get params() { return currentParams; },
|
||||
createOrUpdate: (props: Partial<PD.Values<BaseGeometry.Params>> = {}) => {
|
||||
@@ -353,6 +384,8 @@ namespace Representation {
|
||||
|
||||
return Task.create(`Updating '${label}' representation`, async runtime => {
|
||||
// TODO
|
||||
geometryState.add(0, renderObject.id);
|
||||
geometryState.snapshot();
|
||||
updated.next(version++);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -44,6 +44,7 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa
|
||||
const renderObjects: GraphicsRenderObject<G['kind']>[] = [];
|
||||
let _renderObject: GraphicsRenderObject<G['kind']> | undefined;
|
||||
let _shape: Shape<G>;
|
||||
let geometryVersion = -1;
|
||||
const _theme = Theme.createEmpty();
|
||||
let currentProps: PD.Values<P> = PD.getDefaultValues(geometryUtils.Params as P); // TODO avoid casting
|
||||
let currentParams: P;
|
||||
@@ -157,6 +158,9 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa
|
||||
}
|
||||
|
||||
currentProps = newProps;
|
||||
if (updateState.createGeometry || updateState.createNew) {
|
||||
geometryVersion += 1;
|
||||
}
|
||||
// increment version
|
||||
updated.next(version++);
|
||||
});
|
||||
@@ -178,6 +182,7 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa
|
||||
get state() { return _state; },
|
||||
get theme() { return _theme; },
|
||||
renderObjects,
|
||||
get geometryVersion() { return geometryVersion; },
|
||||
updated,
|
||||
createOrUpdate,
|
||||
getLoci(pickingId?: PickingId) {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { ComplexVisual, StructureRepresentation, StructureRepresentationStateBuilder, StructureRepresentationState } from './representation';
|
||||
import { RepresentationContext, RepresentationParamsGetter } from '../representation';
|
||||
import { Representation, RepresentationContext, RepresentationParamsGetter } from '../representation';
|
||||
import { Structure, StructureElement, Bond } from '../../mol-model/structure';
|
||||
import { Subject } from 'rxjs';
|
||||
import { getNextMaterialId, GraphicsRenderObject } from '../../mol-gl/render-object';
|
||||
@@ -26,6 +26,7 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
|
||||
let version = 0;
|
||||
const { webgl } = ctx;
|
||||
const updated = new Subject<number>();
|
||||
const geometryState = new Representation.GeometryState();
|
||||
const materialId = getNextMaterialId();
|
||||
const renderObjects: GraphicsRenderObject[] = [];
|
||||
const _state = StructureRepresentationStateBuilder.create();
|
||||
@@ -59,9 +60,14 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
|
||||
if (newVisual) setState(_state); // current state for new visual
|
||||
// update list of renderObjects
|
||||
renderObjects.length = 0;
|
||||
if (visual && visual.renderObject) renderObjects.push(visual.renderObject);
|
||||
if (visual && visual.renderObject) {
|
||||
renderObjects.push(visual.renderObject);
|
||||
geometryState.add(visual.renderObject.id, visual.geometryVersion);
|
||||
}
|
||||
geometryState.snapshot();
|
||||
// increment version
|
||||
updated.next(version++);
|
||||
version += 1;
|
||||
updated.next(version);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -100,12 +106,12 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
|
||||
if (state.overpaint !== undefined && visual) {
|
||||
// Remap loci from equivalent structure to the current structure
|
||||
const remappedOverpaint = Overpaint.remap(state.overpaint, _structure);
|
||||
visual.setOverpaint(remappedOverpaint);
|
||||
visual.setOverpaint(remappedOverpaint, webgl);
|
||||
}
|
||||
if (state.transparency !== undefined && visual) {
|
||||
// Remap loci from equivalent structure to the current structure
|
||||
const remappedTransparency = Transparency.remap(state.transparency, _structure);
|
||||
visual.setTransparency(remappedTransparency);
|
||||
visual.setTransparency(remappedTransparency, webgl);
|
||||
}
|
||||
if (state.clipping !== undefined && visual) {
|
||||
// Remap loci from equivalent structure to the current structure
|
||||
@@ -138,6 +144,7 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
|
||||
get params() { return _params; },
|
||||
get state() { return _state; },
|
||||
get theme() { return _theme; },
|
||||
get geometryVersion() { return geometryState.version; },
|
||||
renderObjects,
|
||||
updated,
|
||||
createOrUpdate,
|
||||
|
||||
@@ -80,6 +80,7 @@ export function ComplexVisual<G extends Geometry, P extends StructureParams & Ge
|
||||
let currentStructure: Structure;
|
||||
|
||||
let geometry: G;
|
||||
let geometryVersion = -1;
|
||||
let locationIt: LocationIterator;
|
||||
let positionIt: LocationIterator;
|
||||
|
||||
@@ -187,7 +188,10 @@ export function ComplexVisual<G extends Geometry, P extends StructureParams & Ge
|
||||
currentProps = newProps;
|
||||
currentTheme = newTheme;
|
||||
currentStructure = newStructure;
|
||||
if (newGeometry) geometry = newGeometry;
|
||||
if (newGeometry) {
|
||||
geometry = newGeometry;
|
||||
geometryVersion += 1;
|
||||
}
|
||||
}
|
||||
|
||||
function lociIsSuperset(loci: Loci) {
|
||||
@@ -216,6 +220,7 @@ export function ComplexVisual<G extends Geometry, P extends StructureParams & Ge
|
||||
return {
|
||||
get groupCount() { return locationIt ? locationIt.count : 0; },
|
||||
get renderObject() { return locationIt && locationIt.count ? renderObject : undefined; },
|
||||
get geometryVersion() { return geometryVersion; },
|
||||
createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<PD.Values<P>> = {}, structure?: Structure) {
|
||||
prepareUpdate(theme, props, structure || currentStructure);
|
||||
if (updateState.createGeometry) {
|
||||
@@ -253,11 +258,13 @@ export function ComplexVisual<G extends Geometry, P extends StructureParams & Ge
|
||||
setTransform(matrix?: Mat4, instanceMatrices?: Float32Array | null) {
|
||||
Visual.setTransform(renderObject, matrix, instanceMatrices);
|
||||
},
|
||||
setOverpaint(overpaint: Overpaint) {
|
||||
Visual.setOverpaint(renderObject, overpaint, lociApply, true);
|
||||
setOverpaint(overpaint: Overpaint, webgl?: WebGLContext) {
|
||||
const smoothing = { geometry, props: currentProps, webgl };
|
||||
Visual.setOverpaint(renderObject, overpaint, lociApply, true, smoothing);
|
||||
},
|
||||
setTransparency(transparency: Transparency) {
|
||||
Visual.setTransparency(renderObject, transparency, lociApply, true);
|
||||
setTransparency(transparency: Transparency, webgl?: WebGLContext) {
|
||||
const smoothing = { geometry, props: currentProps, webgl };
|
||||
Visual.setTransparency(renderObject, transparency, lociApply, true, smoothing);
|
||||
},
|
||||
setClipping(clipping: Clipping) {
|
||||
Visual.setClipping(renderObject, clipping, lociApply, true);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { StructureRepresentation, StructureRepresentationStateBuilder, StructureRepresentationState } from './representation';
|
||||
import { Visual } from '../visual';
|
||||
import { RepresentationContext, RepresentationParamsGetter } from '../representation';
|
||||
import { Representation, RepresentationContext, RepresentationParamsGetter } from '../representation';
|
||||
import { Structure, Unit, StructureElement, Bond } from '../../mol-model/structure';
|
||||
import { Subject } from 'rxjs';
|
||||
import { getNextMaterialId, GraphicsRenderObject } from '../../mol-gl/render-object';
|
||||
@@ -34,6 +34,7 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
|
||||
const updated = new Subject<number>();
|
||||
const materialId = getNextMaterialId();
|
||||
const renderObjects: GraphicsRenderObject[] = [];
|
||||
const geometryState = new Representation.GeometryState();
|
||||
const _state = StructureRepresentationStateBuilder.create();
|
||||
let visuals = new Map<number, { group: Unit.SymmetryGroup, visual: UnitsVisual<P> }>();
|
||||
|
||||
@@ -170,8 +171,12 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
|
||||
// update list of renderObjects
|
||||
renderObjects.length = 0;
|
||||
visuals.forEach(({ visual }) => {
|
||||
if (visual.renderObject) renderObjects.push(visual.renderObject);
|
||||
if (visual.renderObject) {
|
||||
renderObjects.push(visual.renderObject);
|
||||
geometryState.add(visual.renderObject.id, visual.geometryVersion);
|
||||
}
|
||||
});
|
||||
geometryState.snapshot();
|
||||
// set new structure
|
||||
if (structure) _structure = structure;
|
||||
// increment version
|
||||
@@ -218,8 +223,8 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
|
||||
if (visible !== undefined) visual.setVisibility(visible);
|
||||
if (alphaFactor !== undefined) visual.setAlphaFactor(alphaFactor);
|
||||
if (pickable !== undefined) visual.setPickable(pickable);
|
||||
if (overpaint !== undefined) visual.setOverpaint(overpaint);
|
||||
if (transparency !== undefined) visual.setTransparency(transparency);
|
||||
if (overpaint !== undefined) visual.setOverpaint(overpaint, webgl);
|
||||
if (transparency !== undefined) visual.setTransparency(transparency, webgl);
|
||||
if (clipping !== undefined) visual.setClipping(clipping);
|
||||
if (transform !== undefined) visual.setTransform(transform);
|
||||
if (unitTransforms !== undefined) {
|
||||
@@ -239,20 +244,14 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
|
||||
if (visible !== _state.visible) newState.visible = visible;
|
||||
if (alphaFactor !== _state.alphaFactor) newState.alphaFactor = alphaFactor;
|
||||
if (pickable !== _state.pickable) newState.pickable = pickable;
|
||||
if (overpaint !== undefined && !Overpaint.areEqual(overpaint, _state.overpaint)) {
|
||||
if (_structure) {
|
||||
newState.overpaint = Overpaint.remap(overpaint, _structure);
|
||||
}
|
||||
if (overpaint !== undefined && _structure) {
|
||||
newState.overpaint = Overpaint.remap(overpaint, _structure);
|
||||
}
|
||||
if (transparency !== undefined && !Transparency.areEqual(transparency, _state.transparency)) {
|
||||
if (_structure) {
|
||||
newState.transparency = Transparency.remap(transparency, _structure);
|
||||
}
|
||||
if (transparency !== undefined && _structure) {
|
||||
newState.transparency = Transparency.remap(transparency, _structure);
|
||||
}
|
||||
if (clipping !== undefined && !Clipping.areEqual(clipping, _state.clipping)) {
|
||||
if (_structure) {
|
||||
newState.clipping = Clipping.remap(clipping, _structure);
|
||||
}
|
||||
if (clipping !== undefined && _structure) {
|
||||
newState.clipping = Clipping.remap(clipping, _structure);
|
||||
}
|
||||
if (transform !== undefined && !Mat4.areEqual(transform, _state.transform, EPSILON)) {
|
||||
newState.transform = transform;
|
||||
@@ -287,6 +286,7 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
|
||||
});
|
||||
return groupCount;
|
||||
},
|
||||
get geometryVersion() { return geometryState.version; },
|
||||
get props() { return _props; },
|
||||
get params() { return _params; },
|
||||
get state() { return _state; },
|
||||
|
||||
@@ -84,6 +84,7 @@ export function UnitsVisual<G extends Geometry, P extends StructureParams & Geom
|
||||
let currentStructureGroup: StructureGroup;
|
||||
|
||||
let geometry: G;
|
||||
let geometryVersion = -1;
|
||||
let locationIt: LocationIterator;
|
||||
let positionIt: LocationIterator;
|
||||
|
||||
@@ -235,7 +236,10 @@ export function UnitsVisual<G extends Geometry, P extends StructureParams & Geom
|
||||
currentProps = newProps;
|
||||
currentTheme = newTheme;
|
||||
currentStructureGroup = newStructureGroup;
|
||||
if (newGeometry) geometry = newGeometry;
|
||||
if (newGeometry) {
|
||||
geometry = newGeometry;
|
||||
geometryVersion += 1;
|
||||
}
|
||||
}
|
||||
|
||||
function _createGeometry(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<P>, geometry?: G) {
|
||||
@@ -270,6 +274,7 @@ export function UnitsVisual<G extends Geometry, P extends StructureParams & Geom
|
||||
return {
|
||||
get groupCount() { return locationIt ? locationIt.count : 0; },
|
||||
get renderObject() { return locationIt && locationIt.count ? renderObject : undefined; },
|
||||
get geometryVersion() { return geometryVersion; },
|
||||
createOrUpdate(ctx: VisualContext, theme: Theme, props: PD.Values<P>, structureGroup?: StructureGroup) {
|
||||
prepareUpdate(theme, props, structureGroup || currentStructureGroup);
|
||||
if (updateState.createGeometry) {
|
||||
@@ -318,11 +323,13 @@ export function UnitsVisual<G extends Geometry, P extends StructureParams & Geom
|
||||
setTransform(matrix?: Mat4, instanceMatrices?: Float32Array | null) {
|
||||
Visual.setTransform(renderObject, matrix, instanceMatrices);
|
||||
},
|
||||
setOverpaint(overpaint: Overpaint) {
|
||||
Visual.setOverpaint(renderObject, overpaint, lociApply, true);
|
||||
setOverpaint(overpaint: Overpaint, webgl?: WebGLContext) {
|
||||
const smoothing = { geometry, props: currentProps, webgl };
|
||||
Visual.setOverpaint(renderObject, overpaint, lociApply, true, smoothing);
|
||||
},
|
||||
setTransparency(transparency: Transparency) {
|
||||
Visual.setTransparency(renderObject, transparency, lociApply, true);
|
||||
setTransparency(transparency: Transparency, webgl?: WebGLContext) {
|
||||
const smoothing = { geometry, props: currentProps, webgl };
|
||||
Visual.setTransparency(renderObject, transparency, lociApply, true, smoothing);
|
||||
},
|
||||
setClipping(clipping: Clipping) {
|
||||
Visual.setClipping(renderObject, clipping, lociApply, true);
|
||||
|
||||
@@ -23,7 +23,9 @@ import { WebGLContext } from '../../../mol-gl/webgl/context';
|
||||
import { MeshValues } from '../../../mol-gl/renderable/mesh';
|
||||
import { TextureMeshValues } from '../../../mol-gl/renderable/texture-mesh';
|
||||
import { Texture } from '../../../mol-gl/webgl/texture';
|
||||
import { applyMeshColorSmoothing, applyTextureMeshColorSmoothing, ColorSmoothingParams, getColorSmoothingProps } from './util/color';
|
||||
import { applyMeshColorSmoothing } from '../../../mol-geo/geometry/mesh/color-smoothing';
|
||||
import { applyTextureMeshColorSmoothing } from '../../../mol-geo/geometry/texture-mesh/color-smoothing';
|
||||
import { ColorSmoothingParams, getColorSmoothingProps } from '../../../mol-geo/geometry/base';
|
||||
|
||||
const SharedParams = {
|
||||
...GaussianDensityParams,
|
||||
@@ -131,7 +133,7 @@ export function GaussianSurfaceMeshVisual(materialId: number): UnitsVisual<Gauss
|
||||
},
|
||||
processValues: (values: MeshValues, geometry: Mesh, props: PD.Values<GaussianSurfaceMeshParams>, theme: Theme, webgl?: WebGLContext) => {
|
||||
const { resolution, colorTexture } = geometry.meta as GaussianSurfaceMeta;
|
||||
const csp = getColorSmoothingProps(props, theme, resolution);
|
||||
const csp = getColorSmoothingProps(props.smoothColors, theme.color.preferSmoothing, resolution);
|
||||
if (csp) {
|
||||
applyMeshColorSmoothing(values, csp.resolution, csp.stride, webgl, colorTexture);
|
||||
(geometry.meta.colorTexture as GaussianSurfaceMeta['colorTexture']) = values.tColorGrid.ref.value;
|
||||
@@ -191,7 +193,7 @@ export function StructureGaussianSurfaceMeshVisual(materialId: number): ComplexV
|
||||
},
|
||||
processValues: (values: MeshValues, geometry: Mesh, props: PD.Values<GaussianSurfaceMeshParams>, theme: Theme, webgl?: WebGLContext) => {
|
||||
const { resolution, colorTexture } = geometry.meta as GaussianSurfaceMeta;
|
||||
const csp = getColorSmoothingProps(props, theme, resolution);
|
||||
const csp = getColorSmoothingProps(props.smoothColors, theme.color.preferSmoothing, resolution);
|
||||
if (csp) {
|
||||
applyMeshColorSmoothing(values, csp.resolution, csp.stride, webgl, colorTexture);
|
||||
(geometry.meta.colorTexture as GaussianSurfaceMeta['colorTexture']) = values.tColorGrid.ref.value;
|
||||
@@ -233,7 +235,7 @@ async function createGaussianSurfaceTextureMesh(ctx: VisualContext, unit: Unit,
|
||||
|
||||
const boundingSphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, props.radiusOffset + getStructureExtraRadius(structure));
|
||||
const surface = TextureMesh.create(gv.vertexCount, 1, gv.vertexTexture, gv.groupTexture, gv.normalTexture, boundingSphere, textureMesh);
|
||||
(surface.meta as GaussianSurfaceMeta) = { resolution: densityTextureData.resolution };
|
||||
(surface.meta as GaussianSurfaceMeta).resolution = densityTextureData.resolution;
|
||||
|
||||
return surface;
|
||||
}
|
||||
@@ -264,7 +266,7 @@ export function GaussianSurfaceTextureMeshVisual(materialId: number): UnitsVisua
|
||||
},
|
||||
processValues: (values: TextureMeshValues, geometry: TextureMesh, props: PD.Values<GaussianSurfaceMeshParams>, theme: Theme, webgl?: WebGLContext) => {
|
||||
const { resolution, colorTexture } = geometry.meta as GaussianSurfaceMeta;
|
||||
const csp = getColorSmoothingProps(props, theme, resolution);
|
||||
const csp = getColorSmoothingProps(props.smoothColors, theme.color.preferSmoothing, resolution);
|
||||
if (csp && webgl) {
|
||||
applyTextureMeshColorSmoothing(values, csp.resolution, csp.stride, webgl, colorTexture);
|
||||
(geometry.meta as GaussianSurfaceMeta).colorTexture = values.tColorGrid.ref.value;
|
||||
@@ -309,7 +311,7 @@ async function createStructureGaussianSurfaceTextureMesh(ctx: VisualContext, str
|
||||
|
||||
const boundingSphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, props.radiusOffset + getStructureExtraRadius(structure));
|
||||
const surface = TextureMesh.create(gv.vertexCount, 1, gv.vertexTexture, gv.groupTexture, gv.normalTexture, boundingSphere, textureMesh);
|
||||
(surface.meta as GaussianSurfaceMeta) = { resolution: densityTextureData.resolution };
|
||||
(surface.meta as GaussianSurfaceMeta).resolution = densityTextureData.resolution;
|
||||
|
||||
return surface;
|
||||
}
|
||||
@@ -340,7 +342,7 @@ export function StructureGaussianSurfaceTextureMeshVisual(materialId: number): C
|
||||
},
|
||||
processValues: (values: TextureMeshValues, geometry: TextureMesh, props: PD.Values<GaussianSurfaceMeshParams>, theme: Theme, webgl?: WebGLContext) => {
|
||||
const { resolution, colorTexture } = geometry.meta as GaussianSurfaceMeta;
|
||||
const csp = getColorSmoothingProps(props, theme, resolution);
|
||||
const csp = getColorSmoothingProps(props.smoothColors, theme.color.preferSmoothing, resolution);
|
||||
if (csp && webgl) {
|
||||
applyTextureMeshColorSmoothing(values, csp.resolution, csp.stride, webgl, colorTexture);
|
||||
(geometry.meta as GaussianSurfaceMeta).colorTexture = values.tColorGrid.ref.value;
|
||||
|
||||
@@ -20,7 +20,8 @@ import { Sphere3D } from '../../../mol-math/geometry';
|
||||
import { MeshValues } from '../../../mol-gl/renderable/mesh';
|
||||
import { Texture } from '../../../mol-gl/webgl/texture';
|
||||
import { WebGLContext } from '../../../mol-gl/webgl/context';
|
||||
import { applyMeshColorSmoothing, ColorSmoothingParams, getColorSmoothingProps } from './util/color';
|
||||
import { applyMeshColorSmoothing } from '../../../mol-geo/geometry/mesh/color-smoothing';
|
||||
import { ColorSmoothingParams, getColorSmoothingProps } from '../../../mol-geo/geometry/base';
|
||||
|
||||
export const MolecularSurfaceMeshParams = {
|
||||
...UnitsMeshParams,
|
||||
@@ -58,7 +59,7 @@ async function createMolecularSurfaceMesh(ctx: VisualContext, unit: Unit, struct
|
||||
|
||||
const sphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, props.probeRadius + getUnitExtraRadius(unit));
|
||||
surface.setBoundingSphere(sphere);
|
||||
(surface.meta.resolution as MolecularSurfaceMeta['resolution']) = resolution;
|
||||
(surface.meta as MolecularSurfaceMeta).resolution = resolution;
|
||||
|
||||
return surface;
|
||||
}
|
||||
@@ -87,10 +88,10 @@ export function MolecularSurfaceMeshVisual(materialId: number): UnitsVisual<Mole
|
||||
},
|
||||
processValues: (values: MeshValues, geometry: Mesh, props: PD.Values<MolecularSurfaceMeshParams>, theme: Theme, webgl?: WebGLContext) => {
|
||||
const { resolution, colorTexture } = geometry.meta as MolecularSurfaceMeta;
|
||||
const csp = getColorSmoothingProps(props, theme, resolution);
|
||||
const csp = getColorSmoothingProps(props.smoothColors, theme.color.preferSmoothing, resolution);
|
||||
if (csp) {
|
||||
applyMeshColorSmoothing(values, csp.resolution, csp.stride, webgl, colorTexture);
|
||||
(geometry.meta.colorTexture as MolecularSurfaceMeta['colorTexture']) = values.tColorGrid.ref.value;
|
||||
(geometry.meta as MolecularSurfaceMeta).colorTexture = values.tColorGrid.ref.value;
|
||||
}
|
||||
},
|
||||
dispose: (geometry: Mesh) => {
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { calcMeshColorSmoothing } from '../../../../mol-geo/geometry/mesh/color-smoothing';
|
||||
import { calcTextureMeshColorSmoothing } from '../../../../mol-geo/geometry/texture-mesh/color-smoothing';
|
||||
import { MeshValues } from '../../../../mol-gl/renderable/mesh';
|
||||
import { TextureMeshValues } from '../../../../mol-gl/renderable/texture-mesh';
|
||||
import { WebGLContext } from '../../../../mol-gl/webgl/context';
|
||||
import { Texture } from '../../../../mol-gl/webgl/texture';
|
||||
import { smoothstep } from '../../../../mol-math/interpolate';
|
||||
import { Theme } from '../../../../mol-theme/theme';
|
||||
import { ValueCell } from '../../../../mol-util';
|
||||
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
|
||||
|
||||
export const ColorSmoothingParams = {
|
||||
smoothColors: PD.MappedStatic('auto', {
|
||||
auto: PD.Group({}),
|
||||
on: PD.Group({
|
||||
resolutionFactor: PD.Numeric(2, { min: 0.5, max: 6, step: 0.1 }),
|
||||
sampleStride: PD.Numeric(3, { min: 1, max: 12, step: 1 }),
|
||||
}),
|
||||
off: PD.Group({})
|
||||
}),
|
||||
};
|
||||
export type ColorSmoothingParams = typeof ColorSmoothingParams
|
||||
|
||||
export function getColorSmoothingProps(props: PD.Values<ColorSmoothingParams>, theme: Theme, resolution?: number) {
|
||||
if ((props.smoothColors.name === 'on' || (props.smoothColors.name === 'auto' && theme.color.preferSmoothing)) && resolution && resolution < 3) {
|
||||
let stride = 3;
|
||||
if (props.smoothColors.name === 'on') {
|
||||
resolution *= props.smoothColors.params.resolutionFactor;
|
||||
stride = props.smoothColors.params.sampleStride;
|
||||
} else {
|
||||
// https://graphtoy.com/?f1(x,t)=(2-smoothstep(0,1.1,x))*x&coords=0.7,0.6,1.8
|
||||
resolution *= 2 - smoothstep(0, 1.1, resolution);
|
||||
resolution = Math.max(0.5, resolution);
|
||||
if (resolution > 1.2) stride = 2;
|
||||
}
|
||||
return { resolution, stride };
|
||||
};
|
||||
}
|
||||
|
||||
function isSupportedColorType(x: string): x is 'group' | 'groupInstance' {
|
||||
return x === 'group' || x === 'groupInstance';
|
||||
}
|
||||
|
||||
export function applyMeshColorSmoothing(values: MeshValues, resolution: number, stride: number, webgl?: WebGLContext, colorTexture?: Texture) {
|
||||
if (!isSupportedColorType(values.dColorType.ref.value)) return;
|
||||
|
||||
const smoothingData = calcMeshColorSmoothing({
|
||||
vertexCount: values.uVertexCount.ref.value,
|
||||
instanceCount: values.uInstanceCount.ref.value,
|
||||
groupCount: values.uGroupCount.ref.value,
|
||||
transformBuffer: values.aTransform.ref.value,
|
||||
instanceBuffer: values.aInstance.ref.value,
|
||||
positionBuffer: values.aPosition.ref.value,
|
||||
groupBuffer: values.aGroup.ref.value,
|
||||
colorData: values.tColor.ref.value,
|
||||
colorType: values.dColorType.ref.value,
|
||||
boundingSphere: values.boundingSphere.ref.value,
|
||||
invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
|
||||
}, resolution, stride, webgl, colorTexture);
|
||||
|
||||
if (smoothingData.kind === 'volume') {
|
||||
ValueCell.updateIfChanged(values.dColorType, smoothingData.type);
|
||||
ValueCell.update(values.tColorGrid, smoothingData.texture);
|
||||
ValueCell.update(values.uColorTexDim, smoothingData.gridTexDim);
|
||||
ValueCell.update(values.uColorGridDim, smoothingData.gridDim);
|
||||
ValueCell.update(values.uColorGridTransform, smoothingData.gridTransform);
|
||||
} else if (smoothingData.kind === 'vertex') {
|
||||
ValueCell.updateIfChanged(values.dColorType, smoothingData.type);
|
||||
ValueCell.update(values.tColor, smoothingData.texture);
|
||||
ValueCell.update(values.uColorTexDim, smoothingData.texDim);
|
||||
}
|
||||
}
|
||||
|
||||
export function applyTextureMeshColorSmoothing(values: TextureMeshValues, resolution: number, stride: number, webgl: WebGLContext, colorTexture?: Texture) {
|
||||
if (!isSupportedColorType(values.dColorType.ref.value)) return;
|
||||
|
||||
stride *= 3; // triple because TextureMesh is never indexed (no elements buffer)
|
||||
|
||||
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: values.tColor.ref.value,
|
||||
colorType: values.dColorType.ref.value,
|
||||
boundingSphere: values.boundingSphere.ref.value,
|
||||
invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
|
||||
}, resolution, stride, webgl, colorTexture);
|
||||
|
||||
ValueCell.updateIfChanged(values.dColorType, smoothingData.type);
|
||||
ValueCell.update(values.tColorGrid, smoothingData.texture);
|
||||
ValueCell.update(values.uColorTexDim, smoothingData.gridTexDim);
|
||||
ValueCell.update(values.uColorGridDim, smoothingData.gridDim);
|
||||
ValueCell.update(values.uColorGridTransform, smoothingData.gridTransform);
|
||||
}
|
||||
@@ -24,6 +24,11 @@ import { createTransparency, clearTransparency, applyTransparencyValue, getTrans
|
||||
import { Clipping } from '../mol-theme/clipping';
|
||||
import { createClipping, applyClippingGroups, clearClipping } from '../mol-geo/geometry/clipping-data';
|
||||
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';
|
||||
|
||||
export interface VisualContext {
|
||||
readonly runtime: RuntimeContext
|
||||
@@ -35,6 +40,7 @@ interface Visual<D, P extends PD.Params> {
|
||||
/** Number of addressable groups in all instances of the visual */
|
||||
readonly groupCount: number
|
||||
readonly renderObject: GraphicsRenderObject | undefined
|
||||
readonly geometryVersion: number
|
||||
createOrUpdate: (ctx: VisualContext, theme: Theme, props: PD.Values<P>, data?: D) => Promise<void> | void
|
||||
getLoci: (pickingId: PickingId) => Loci
|
||||
mark: (loci: Loci, action: MarkerAction) => boolean
|
||||
@@ -43,8 +49,8 @@ interface Visual<D, P extends PD.Params> {
|
||||
setPickable: (pickable: boolean) => void
|
||||
setColorOnly: (colorOnly: boolean) => void
|
||||
setTransform: (matrix?: Mat4, instanceMatrices?: Float32Array | null) => void
|
||||
setOverpaint: (overpaint: Overpaint) => void
|
||||
setTransparency: (transparency: Transparency) => void
|
||||
setOverpaint: (overpaint: Overpaint, webgl?: WebGLContext) => void
|
||||
setTransparency: (transparency: Transparency, webgl?: WebGLContext) => void
|
||||
setClipping: (clipping: Clipping) => void
|
||||
destroy: () => void
|
||||
mustRecreate?: (data: D, props: PD.Values<P>, webgl?: WebGLContext) => boolean
|
||||
@@ -133,10 +139,22 @@ namespace Visual {
|
||||
return changed;
|
||||
}
|
||||
|
||||
export function setOverpaint(renderObject: GraphicsRenderObject | undefined, overpaint: Overpaint, lociApply: LociApply, clear: boolean) {
|
||||
type SurfaceMeta = {
|
||||
resolution?: number
|
||||
overpaintTexture?: Texture
|
||||
transparencyTexture?: Texture
|
||||
}
|
||||
|
||||
type SmoothingContext = {
|
||||
geometry: Geometry,
|
||||
props: PD.Values<any>,
|
||||
webgl?: WebGLContext
|
||||
}
|
||||
|
||||
export function setOverpaint(renderObject: GraphicsRenderObject | undefined, overpaint: Overpaint, lociApply: LociApply, clear: boolean, smoothing?: SmoothingContext) {
|
||||
if (!renderObject) return;
|
||||
|
||||
const { tOverpaint, uGroupCount, instanceCount } = renderObject.values;
|
||||
const { tOverpaint, dOverpaintType, uGroupCount, instanceCount } = renderObject.values;
|
||||
const count = uGroupCount.ref.value * instanceCount.ref.value;
|
||||
|
||||
// ensure texture has right size
|
||||
@@ -158,12 +176,34 @@ namespace Visual {
|
||||
lociApply(loci, apply, false);
|
||||
}
|
||||
ValueCell.update(tOverpaint, tOverpaint.ref.value);
|
||||
ValueCell.updateIfChanged(dOverpaintType, 'groupInstance');
|
||||
|
||||
if (overpaint.layers.length === 0) return;
|
||||
|
||||
if (smoothing && hasColorSmoothingProp(smoothing.props)) {
|
||||
const { geometry, props, webgl } = smoothing;
|
||||
if (geometry.kind === 'mesh') {
|
||||
const { resolution, overpaintTexture } = geometry.meta as SurfaceMeta;
|
||||
const csp = getColorSmoothingProps(props.smoothColors, true, resolution);
|
||||
if (csp) {
|
||||
applyMeshOverpaintSmoothing(renderObject.values as any, csp.resolution, csp.stride, webgl, overpaintTexture);
|
||||
(geometry.meta as SurfaceMeta).overpaintTexture = renderObject.values.tOverpaintGrid.ref.value;
|
||||
}
|
||||
} else if (webgl && geometry.kind === 'texture-mesh') {
|
||||
const { resolution, overpaintTexture } = geometry.meta as SurfaceMeta;
|
||||
const csp = getColorSmoothingProps(props.smoothColors, true, resolution);
|
||||
if (csp) {
|
||||
applyTextureMeshOverpaintSmoothing(renderObject.values as any, csp.resolution, csp.stride, webgl, overpaintTexture);
|
||||
(geometry.meta as SurfaceMeta).overpaintTexture = renderObject.values.tOverpaintGrid.ref.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function setTransparency(renderObject: GraphicsRenderObject | undefined, transparency: Transparency, lociApply: LociApply, clear: boolean) {
|
||||
export function setTransparency(renderObject: GraphicsRenderObject | undefined, transparency: Transparency, lociApply: LociApply, clear: boolean, smoothing?: SmoothingContext) {
|
||||
if (!renderObject) return;
|
||||
|
||||
const { tTransparency, transparencyAverage, uGroupCount, instanceCount } = renderObject.values;
|
||||
const { tTransparency, dTransparencyType, transparencyAverage, uGroupCount, instanceCount } = renderObject.values;
|
||||
const count = uGroupCount.ref.value * instanceCount.ref.value;
|
||||
|
||||
// ensure texture has right size and variant
|
||||
@@ -184,6 +224,28 @@ namespace Visual {
|
||||
}
|
||||
ValueCell.update(tTransparency, tTransparency.ref.value);
|
||||
ValueCell.updateIfChanged(transparencyAverage, getTransparencyAverage(array, count));
|
||||
ValueCell.updateIfChanged(dTransparencyType, 'groupInstance');
|
||||
|
||||
if (transparency.layers.length === 0) return;
|
||||
|
||||
if (smoothing && hasColorSmoothingProp(smoothing.props)) {
|
||||
const { geometry, props, webgl } = smoothing;
|
||||
if (geometry.kind === 'mesh') {
|
||||
const { resolution, transparencyTexture } = geometry.meta as SurfaceMeta;
|
||||
const csp = getColorSmoothingProps(props.smoothColors, true, resolution);
|
||||
if (csp) {
|
||||
applyMeshTransparencySmoothing(renderObject.values as any, csp.resolution, csp.stride, webgl, transparencyTexture);
|
||||
(geometry.meta as SurfaceMeta).transparencyTexture = renderObject.values.tTransparencyGrid.ref.value;
|
||||
}
|
||||
} else if (webgl && geometry.kind === 'texture-mesh') {
|
||||
const { resolution, transparencyTexture } = geometry.meta as SurfaceMeta;
|
||||
const csp = getColorSmoothingProps(props.smoothColors, true, resolution);
|
||||
if (csp) {
|
||||
applyTextureMeshTransparencySmoothing(renderObject.values as any, csp.resolution, csp.stride, webgl, transparencyTexture);
|
||||
(geometry.meta as SurfaceMeta).transparencyTexture = renderObject.values.tTransparencyGrid.ref.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function setClipping(renderObject: GraphicsRenderObject | undefined, clipping: Clipping, lociApply: LociApply, clear: boolean) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -74,6 +74,7 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet
|
||||
let currentVolume: Volume;
|
||||
|
||||
let geometry: G;
|
||||
let geometryVersion = -1;
|
||||
let locationIt: LocationIterator;
|
||||
let positionIt: LocationIterator;
|
||||
|
||||
@@ -156,7 +157,10 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet
|
||||
currentProps = newProps;
|
||||
currentTheme = newTheme;
|
||||
currentVolume = newVolume;
|
||||
if (newGeometry) geometry = newGeometry;
|
||||
if (newGeometry) {
|
||||
geometry = newGeometry;
|
||||
geometryVersion += 1;
|
||||
}
|
||||
}
|
||||
|
||||
function lociApply(loci: Loci, apply: (interval: Interval) => boolean) {
|
||||
@@ -170,6 +174,7 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet
|
||||
return {
|
||||
get groupCount() { return locationIt ? locationIt.count : 0; },
|
||||
get renderObject() { return renderObject; },
|
||||
get geometryVersion() { return geometryVersion; },
|
||||
async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<PD.Values<P>> = {}, volume?: Volume) {
|
||||
prepareUpdate(theme, props, volume || currentVolume);
|
||||
if (updateState.createGeometry) {
|
||||
@@ -236,6 +241,7 @@ export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx:
|
||||
let version = 0;
|
||||
const { webgl } = ctx;
|
||||
const updated = new Subject<number>();
|
||||
const geometryState = new Representation.GeometryState();
|
||||
const materialId = getNextMaterialId();
|
||||
const renderObjects: GraphicsRenderObject[] = [];
|
||||
const _state = Representation.createState();
|
||||
@@ -266,7 +272,11 @@ export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx:
|
||||
if (promise) await promise;
|
||||
// update list of renderObjects
|
||||
renderObjects.length = 0;
|
||||
if (visual && visual.renderObject) renderObjects.push(visual.renderObject);
|
||||
if (visual && visual.renderObject) {
|
||||
renderObjects.push(visual.renderObject);
|
||||
geometryState.add(visual.renderObject.id, visual.geometryVersion);
|
||||
}
|
||||
geometryState.snapshot();
|
||||
// increment version
|
||||
updated.next(version++);
|
||||
});
|
||||
@@ -304,6 +314,7 @@ export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx:
|
||||
get params() { return _params; },
|
||||
get state() { return _state; },
|
||||
get theme() { return _theme; },
|
||||
get geometryVersion() { return geometryState.version; },
|
||||
renderObjects,
|
||||
updated,
|
||||
createOrUpdate,
|
||||
|
||||
@@ -20,7 +20,6 @@ import { now, formatTimespan } from '../mol-util/now';
|
||||
import { ParamDefinition } from '../mol-util/param-definition';
|
||||
import { StateTreeSpine } from './tree/spine';
|
||||
import { AsyncQueue } from '../mol-util/async-queue';
|
||||
import { isProductionMode } from '../mol-util/debug';
|
||||
import { arraySetAdd, arraySetRemove } from '../mol-util/array';
|
||||
import { UniqueArray } from '../mol-data/generic';
|
||||
import { assignIfUndefined } from '../mol-util/object';
|
||||
@@ -207,14 +206,18 @@ class State {
|
||||
if (!restored) {
|
||||
restored = true;
|
||||
await this.updateTree(snapshot).runInContext(ctx);
|
||||
this.events.log.next(LogEntry.error('' + e));
|
||||
this.events.log.next(LogEntry.error('Error during state transaction, reverting'));
|
||||
}
|
||||
if (isNested) {
|
||||
this.inTransactionError = true;
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (options?.rethrowErrors) throw e;
|
||||
if (options?.rethrowErrors) {
|
||||
throw e;
|
||||
} else {
|
||||
console.error(e);
|
||||
}
|
||||
} finally {
|
||||
if (!isNested) {
|
||||
this.inTransaction = false;
|
||||
@@ -829,7 +832,7 @@ async function updateSubtree(ctx: UpdateContext, root: Ref) {
|
||||
ctx.changed = true;
|
||||
if (!ctx.hadError) ctx.newCurrent = root;
|
||||
doError(ctx, root, e, false);
|
||||
if (!isProductionMode) console.error(e);
|
||||
console.error(e);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import { Progress } from './progress';
|
||||
import { now } from '../../mol-util/now';
|
||||
import { Scheduler } from '../util/scheduler';
|
||||
import { UserTiming } from '../util/user-timing';
|
||||
import { isDebugMode } from '../../mol-util/debug';
|
||||
|
||||
interface ExposedTask<T> extends Task<T> {
|
||||
f: (ctx: RuntimeContext) => Promise<T>,
|
||||
@@ -116,7 +115,6 @@ async function execute<T>(task: ExposedTask<T>, ctx: ObservableRuntimeContext) {
|
||||
task.onAbort();
|
||||
}
|
||||
}
|
||||
if (isDebugMode) console.error(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user