Compare commits

...

8 Commits

Author SHA1 Message Date
dsehnal
19c1088209 1.2.8 2021-01-06 15:29:29 +01:00
dsehnal
ee6c2e0841 canvas3d: add commited event 2021-01-06 15:22:54 +01:00
Alexander Rose
20ee659b00 wip, gaussian surface
- fix webgl1 gaussian volume broken
- fix 2d volume slice missing first row
- use scissor test to avoid useless calculations
- reduce radius for which gaussians are calculated
-
2021-01-03 15:15:03 -08:00
Alexander Rose
b6514a4a50 fix structure bond count calculation 2021-01-03 14:57:18 -08:00
Alexander Rose
07b8bdb951 noClip option for renderables
- exclude handle and axes helper from clip objects
2021-01-03 14:56:40 -08:00
dsehnal
249e5a3e0b SequenceView improvements
- add ability to show all polymers/chains at once
- do not redraw when structure list doesn't change (saves 3 re-renders for a typical structure load)
2020-12-29 16:29:16 +01:00
Alexander Rose
6d2578d3d0 repr/geo param update fixes
- texture-mesh geo
- text visual
2020-12-20 13:40:33 -08:00
Alexander Rose
146022dc12 wip, gaussian surface & mc
- fix iso-level
- reuse gpu resources for mc (patched many memory leaks)
2020-12-20 12:55:54 -08:00
33 changed files with 447 additions and 213 deletions

4
package-lock.json generated
View File

@@ -1,11 +1,11 @@
{
"name": "molstar",
"version": "1.2.7",
"version": "1.2.8",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"version": "1.2.7",
"version": "1.2.8",
"license": "MIT",
"dependencies": {
"@types/argparse": "^1.0.38",

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "1.2.7",
"version": "1.2.8",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {

View File

@@ -47,7 +47,7 @@ export const Canvas3DParams = {
on: PD.Group(StereoCameraParams),
off: PD.Group({})
}, { cycle: true, hideIf: p => p?.mode !== 'perspective' }),
manualReset: PD.Boolean(false, { isHidden: true })
manualReset: PD.Boolean(false, { isHidden: true }),
}, { pivot: 'mode' }),
cameraFog: PD.MappedStatic('on', {
on: PD.Group({
@@ -117,6 +117,7 @@ interface Canvas3D {
notifyDidDraw: boolean,
readonly didDraw: BehaviorSubject<now.Timestamp>
readonly commited: BehaviorSubject<now.Timestamp>
readonly reprCount: BehaviorSubject<number>
readonly resized: BehaviorSubject<any>
@@ -218,6 +219,7 @@ namespace Canvas3D {
let startTime = now();
const didDraw = new BehaviorSubject<now.Timestamp>(0 as now.Timestamp);
const commited = new BehaviorSubject<now.Timestamp>(0 as now.Timestamp);
const { gl, contextRestored } = webgl;
@@ -391,6 +393,7 @@ namespace Canvas3D {
draw(true);
forceDrawAfterAllCommited = false;
}
commited.next(now());
}
}
@@ -616,6 +619,7 @@ namespace Canvas3D {
get notifyDidDraw() { return notifyDidDraw; },
set notifyDidDraw(v: boolean) { notifyDidDraw = v; },
didDraw,
commited,
reprCount,
resized,
setProps: (properties, doNotRequestDraw = false) => {

View File

@@ -160,5 +160,5 @@ const instanceMaterialId = getNextMaterialId();
function createBoundingSphereRenderObject(mesh: Mesh, color: Color, materialId: number, transform?: TransformData) {
const values = Mesh.Utils.createValuesSimple(mesh, { alpha: 0.1, doubleSided: false }, color, 1, transform);
return createRenderObject('mesh', values, { visible: true, alphaFactor: 1, pickable: false, colorOnly: false, opaque: false, writeDepth: false }, materialId);
return createRenderObject('mesh', values, { visible: true, alphaFactor: 1, pickable: false, colorOnly: false, opaque: false, writeDepth: false, noClip: false }, materialId);
}

View File

@@ -70,6 +70,7 @@ export class CameraHelper {
this.scene.clear();
const params = { ...props.axes.params, scale: props.axes.params.scale * this.webgl.pixelRatio };
this.renderObject = createAxesRenderObject(params);
this.renderObject.state.noClip = true;
this.scene.add(this.renderObject);
this.scene.commit();

View File

@@ -72,6 +72,7 @@ export class HandleHelper {
this.scene.clear();
const params = { ...props.handle.params, scale: props.handle.params.scale * this.webgl.pixelRatio };
this.renderObject = createHandleRenderObject(params);
this.renderObject.state.noClip = true;
this.scene.add(this.renderObject);
this.scene.commit();

View File

@@ -81,6 +81,7 @@ export namespace BaseGeometry {
colorOnly: false,
opaque,
writeDepth: opaque,
noClip: false,
};
}

View File

@@ -75,6 +75,8 @@ export namespace TextureMesh {
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
flipSided: PD.Boolean(false, BaseGeometry.ShadingCategory),
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
};
export type Params = typeof Params
@@ -126,6 +128,8 @@ export namespace TextureMesh {
dDoubleSided: ValueCell.create(props.doubleSided),
dFlatShaded: ValueCell.create(props.flatShaded),
dFlipSided: ValueCell.create(props.flipSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
dGeoTexture: ValueCell.create(true),
};
}
@@ -142,6 +146,8 @@ export namespace TextureMesh {
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded);
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
if (values.drawCount.ref.value > values.aGroup.ref.value.length) {
// console.log('updating vertex ids in aGroup to handle larger drawCount')

View File

@@ -94,7 +94,8 @@ function createPoints() {
pickable: true,
colorOnly: false,
opaque: true,
writeDepth: true
writeDepth: true,
noClip: false,
};
return createRenderObject('points', values, state, -1);

View File

@@ -1,18 +1,18 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { createComputeRenderable, ComputeRenderable } from '../../renderable';
import { createComputeRenderable } from '../../renderable';
import { WebGLContext } from '../../webgl/context';
import { createComputeRenderItem } from '../../webgl/render-item';
import { Values, TextureSpec, UniformSpec } from '../../renderable/schema';
import { Texture } from '../../../mol-gl/webgl/texture';
import { Texture, TextureFilter, TextureFormat, TextureKind, TextureType } from '../../../mol-gl/webgl/texture';
import { ShaderCode } from '../../../mol-gl/shader-code';
import { ValueCell } from '../../../mol-util';
import { QuadSchema, QuadValues } from '../util';
import { Vec2 } from '../../../mol-math/linear-algebra';
import { Vec2, Vec3 } from '../../../mol-math/linear-algebra';
import { getHistopyramidSum } from './sum';
import { Framebuffer } from '../../../mol-gl/webgl/framebuffer';
import { isPowerOfTwo } from '../../../mol-math/misc';
@@ -26,37 +26,44 @@ const HistopyramidReductionSchema = {
uTexSize: UniformSpec('f'),
};
let HistopyramidReductionRenderable: ComputeRenderable<Values<typeof HistopyramidReductionSchema>>;
const HistogramPyramidName = 'histogram-pyramid';
function getHistopyramidReductionRenderable(ctx: WebGLContext, initialTexture: Texture) {
if (HistopyramidReductionRenderable) {
ValueCell.update(HistopyramidReductionRenderable.values.tPreviousLevel, initialTexture);
HistopyramidReductionRenderable.update();
return HistopyramidReductionRenderable;
if (ctx.namedComputeRenderables[HistogramPyramidName]) {
const v = ctx.namedComputeRenderables[HistogramPyramidName].values;
ValueCell.update(v.tPreviousLevel, initialTexture);
ctx.namedComputeRenderables[HistogramPyramidName].update();
} else {
const values: Values<typeof HistopyramidReductionSchema> = {
...QuadValues,
tPreviousLevel: ValueCell.create(initialTexture),
uSize: ValueCell.create(0),
uTexSize: ValueCell.create(0),
};
const schema = { ...HistopyramidReductionSchema };
const shaderCode = ShaderCode('reduction', quad_vert, reduction_frag);
const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values);
HistopyramidReductionRenderable = createComputeRenderable(renderItem, values);
return HistopyramidReductionRenderable;
ctx.namedComputeRenderables[HistogramPyramidName] = createHistopyramidReductionRenderable(ctx, initialTexture);
}
return ctx.namedComputeRenderables[HistogramPyramidName];
}
function createHistopyramidReductionRenderable(ctx: WebGLContext, initialTexture: Texture) {
const values: Values<typeof HistopyramidReductionSchema> = {
...QuadValues,
tPreviousLevel: ValueCell.create(initialTexture),
uSize: ValueCell.create(0),
uTexSize: ValueCell.create(0),
};
const schema = { ...HistopyramidReductionSchema };
const shaderCode = ShaderCode('reduction', quad_vert, reduction_frag);
const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values);
return createComputeRenderable(renderItem, values);
}
type TextureFramebuffer = { texture: Texture, framebuffer: Framebuffer }
const LevelTexturesFramebuffers: TextureFramebuffer[] = [];
function getLevelTextureFramebuffer(ctx: WebGLContext, level: number) {
let textureFramebuffer = LevelTexturesFramebuffers[level];
let textureFramebuffer = LevelTexturesFramebuffers[level];
const size = Math.pow(2, level);
if (textureFramebuffer === undefined) {
const texture = ctx.resources.texture('image-float32', 'rgba', 'float', 'nearest');
const framebuffer = ctx.resources.framebuffer();
const texture = getTexture(`level${level}`, ctx, 'image-float32', 'rgba', 'float', 'nearest');
const framebuffer = getFramebuffer(`level${level}`, ctx);
texture.attachFramebuffer(framebuffer, 0);
textureFramebuffer = { texture, framebuffer };
textureFramebuffer.texture.define(size, size);
@@ -70,12 +77,28 @@ function setRenderingDefaults(ctx: WebGLContext) {
state.disable(gl.CULL_FACE);
state.disable(gl.BLEND);
state.disable(gl.DEPTH_TEST);
state.disable(gl.SCISSOR_TEST);
state.enable(gl.SCISSOR_TEST);
state.depthMask(false);
state.colorMask(true, true, true, true);
state.clearColor(0, 0, 0, 0);
}
function getFramebuffer(name: string, webgl: WebGLContext): Framebuffer {
const _name = `${HistogramPyramidName}-${name}`;
if (!webgl.namedFramebuffers[_name]) {
webgl.namedFramebuffers[_name] = webgl.resources.framebuffer();
}
return webgl.namedFramebuffers[_name];
}
function getTexture(name: string, webgl: WebGLContext, kind: TextureKind, format: TextureFormat, type: TextureType, filter: TextureFilter): Texture {
const _name = `${HistogramPyramidName}-${name}`;
if (!webgl.namedTextures[_name]) {
webgl.namedTextures[_name] = webgl.resources.texture(kind, format, type, filter);
}
return webgl.namedTextures[_name];
}
export interface HistogramPyramid {
pyramidTex: Texture
count: number
@@ -84,8 +107,8 @@ export interface HistogramPyramid {
scale: Vec2
}
export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture, scale: Vec2): HistogramPyramid {
const { gl, resources } = ctx;
export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture, scale: Vec2, gridTexDim: Vec3): HistogramPyramid {
const { gl } = ctx;
// printTexture(ctx, inputTexture, 2)
if (inputTexture.getWidth() !== inputTexture.getHeight() || !isPowerOfTwo(inputTexture.getWidth())) {
@@ -95,13 +118,14 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture,
// This part set the levels
const levels = Math.ceil(Math.log(inputTexture.getWidth()) / Math.log(2));
const maxSize = Math.pow(2, levels);
// console.log('levels', levels, 'maxSize', maxSize)
// console.log('levels', levels, 'maxSize', maxSize, 'input', inputTexture.getWidth());
const pyramidTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest');
const pyramidTexture = getTexture('pyramid', ctx, 'image-float32', 'rgba', 'float', 'nearest');
pyramidTexture.define(maxSize, maxSize);
const framebuffer = resources.framebuffer();
const framebuffer = getFramebuffer('pyramid', ctx);
pyramidTexture.attachFramebuffer(framebuffer, 0);
gl.viewport(0, 0, maxSize, maxSize);
gl.clear(gl.COLOR_BUFFER_BIT);
const levelTexturesFramebuffers: TextureFramebuffer[] = [];
@@ -120,8 +144,6 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture,
const size = Math.pow(2, currLevel);
// console.log('size', size, 'draw-level', currLevel, 'read-level', levels - i)
gl.clear(gl.COLOR_BUFFER_BIT);
gl.viewport(0, 0, size, size);
ValueCell.update(renderable.values.uSize, Math.pow(2, i + 1) / maxSize);
ValueCell.update(renderable.values.uTexSize, size);
@@ -130,6 +152,10 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture,
renderable.update();
}
ctx.state.currentRenderItemId = -1;
gl.viewport(0, 0, size, size);
gl.scissor(0, 0, size, size);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.scissor(0, 0, gridTexDim[0], gridTexDim[1]);
renderable.render();
pyramidTexture.bind(0);

View File

@@ -1,10 +1,10 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { createComputeRenderable, ComputeRenderable } from '../../renderable';
import { createComputeRenderable } from '../../renderable';
import { WebGLContext } from '../../webgl/context';
import { createComputeRenderItem } from '../../webgl/render-item';
import { Values, TextureSpec } from '../../renderable/schema';
@@ -21,33 +21,32 @@ const HistopyramidSumSchema = {
tTexture: TextureSpec('texture', 'rgba', 'float', 'nearest'),
};
let HistopyramidSumRenderable: ComputeRenderable<Values<typeof HistopyramidSumSchema>>;
const HistopyramidSumName = 'histopyramid-sum';
function getHistopyramidSumRenderable(ctx: WebGLContext, texture: Texture) {
if (HistopyramidSumRenderable) {
ValueCell.update(HistopyramidSumRenderable.values.tTexture, texture);
HistopyramidSumRenderable.update();
return HistopyramidSumRenderable;
if (ctx.namedComputeRenderables[HistopyramidSumName]) {
const v = ctx.namedComputeRenderables[HistopyramidSumName].values;
ValueCell.update(v.tTexture, texture);
ctx.namedComputeRenderables[HistopyramidSumName].update();
} else {
const values: Values<typeof HistopyramidSumSchema> = {
...QuadValues,
tTexture: ValueCell.create(texture),
};
const schema = { ...HistopyramidSumSchema };
const shaderCode = ShaderCode('sum', quad_vert, sum_frag);
const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values);
HistopyramidSumRenderable = createComputeRenderable(renderItem, values);
return HistopyramidSumRenderable;
ctx.namedComputeRenderables[HistopyramidSumName] = createHistopyramidSumRenderable(ctx, texture);
}
return ctx.namedComputeRenderables[HistopyramidSumName];
}
let SumTexture: Texture;
function getSumTexture(ctx: WebGLContext) {
if (SumTexture) return SumTexture;
SumTexture = ctx.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
SumTexture.define(1, 1);
return SumTexture;
function createHistopyramidSumRenderable(ctx: WebGLContext, texture: Texture) {
const values: Values<typeof HistopyramidSumSchema> = {
...QuadValues,
tTexture: ValueCell.create(texture),
};
const schema = { ...HistopyramidSumSchema };
const shaderCode = ShaderCode('sum', quad_vert, sum_frag);
const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values);
return createComputeRenderable(renderItem, values);
}
function setRenderingDefaults(ctx: WebGLContext) {
@@ -68,8 +67,16 @@ export function getHistopyramidSum(ctx: WebGLContext, pyramidTopTexture: Texture
const renderable = getHistopyramidSumRenderable(ctx, pyramidTopTexture);
ctx.state.currentRenderItemId = -1;
const framebuffer = resources.framebuffer();
const sumTexture = getSumTexture(ctx);
if (!ctx.namedFramebuffers[HistopyramidSumName]) {
ctx.namedFramebuffers[HistopyramidSumName] = resources.framebuffer();
}
const framebuffer = ctx.namedFramebuffers[HistopyramidSumName];
if (!ctx.namedTextures[HistopyramidSumName]) {
ctx.namedTextures[HistopyramidSumName] = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
ctx.namedTextures[HistopyramidSumName].define(1, 1);
}
const sumTexture = ctx.namedTextures[HistopyramidSumName];
sumTexture.attachFramebuffer(framebuffer, 0);
setRenderingDefaults(ctx);

View File

@@ -30,18 +30,36 @@ const ActiveVoxelsSchema = {
uScale: UniformSpec('v2'),
};
const ActiveVoxelsName = 'active-voxels';
function getActiveVoxelsRenderable(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, isoValue: number, scale: Vec2) {
if (ctx.namedComputeRenderables[ActiveVoxelsName]) {
const v = ctx.namedComputeRenderables[ActiveVoxelsName].values;
ValueCell.update(v.uQuadScale, scale);
ValueCell.update(v.tVolumeData, volumeData);
ValueCell.updateIfChanged(v.uIsoValue, isoValue);
ValueCell.update(v.uGridDim, gridDim);
ValueCell.update(v.uGridTexDim, gridTexDim);
ValueCell.update(v.uScale, scale);
ctx.namedComputeRenderables[ActiveVoxelsName].update();
} else {
ctx.namedComputeRenderables[ActiveVoxelsName] = createActiveVoxelsRenderable(ctx, volumeData, gridDim, gridTexDim, isoValue, scale);
}
return ctx.namedComputeRenderables[ActiveVoxelsName];
}
function createActiveVoxelsRenderable(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, isoValue: number, scale: Vec2) {
const values: Values<typeof ActiveVoxelsSchema> = {
...QuadValues,
uQuadScale: ValueCell.create(scale),
tTriCount: ValueCell.create(getTriCount()),
uQuadScale: ValueCell.create(scale),
tVolumeData: ValueCell.create(volumeData),
uIsoValue: ValueCell.create(isoValue),
uGridDim: ValueCell.create(gridDim),
uGridTexDim: ValueCell.create(gridTexDim),
uScale: ValueCell.create(scale),
};
@@ -57,7 +75,7 @@ function setRenderingDefaults(ctx: WebGLContext) {
state.disable(gl.CULL_FACE);
state.disable(gl.BLEND);
state.disable(gl.DEPTH_TEST);
state.disable(gl.SCISSOR_TEST);
state.enable(gl.SCISSOR_TEST);
state.depthMask(false);
state.colorMask(true, true, true, true);
state.clearColor(0, 0, 0, 0);
@@ -68,10 +86,16 @@ export function calcActiveVoxels(ctx: WebGLContext, volumeData: Texture, gridDim
const width = volumeData.getWidth();
const height = volumeData.getHeight();
const framebuffer = resources.framebuffer();
if (!ctx.namedFramebuffers[ActiveVoxelsName]) {
ctx.namedFramebuffers[ActiveVoxelsName] = resources.framebuffer();
}
const framebuffer = ctx.namedFramebuffers[ActiveVoxelsName];
framebuffer.bind();
const activeVoxelsTex = resources.texture('image-float32', 'rgba', 'float', 'nearest');
if (!ctx.namedTextures[ActiveVoxelsName]) {
ctx.namedTextures[ActiveVoxelsName] = resources.texture('image-float32', 'rgba', 'float', 'nearest');
}
const activeVoxelsTex = ctx.namedTextures[ActiveVoxelsName];
activeVoxelsTex.define(width, height);
const renderable = getActiveVoxelsRenderable(ctx, volumeData, gridDim, gridTexDim, isoValue, gridScale);
@@ -80,6 +104,9 @@ export function calcActiveVoxels(ctx: WebGLContext, volumeData: Texture, gridDim
activeVoxelsTex.attachFramebuffer(framebuffer, 0);
setRenderingDefaults(ctx);
gl.viewport(0, 0, width, height);
gl.scissor(0, 0, width, height);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.scissor(0, 0, gridTexDim[0], gridTexDim[1]);
renderable.render();
// console.log('gridScale', gridScale, 'gridTexDim', gridTexDim, 'gridDim', gridDim)

View File

@@ -38,18 +38,46 @@ const IsosurfaceSchema = {
uScale: UniformSpec('v2'),
};
const IsosurfaceName = 'isosurface';
function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, height: number) {
if (ctx.namedComputeRenderables[IsosurfaceName]) {
const v = ctx.namedComputeRenderables[IsosurfaceName].values;
ValueCell.update(v.uQuadScale, Vec2.create(1, height / Math.pow(2, levels)));
ValueCell.update(v.tActiveVoxelsPyramid, activeVoxelsPyramid);
ValueCell.update(v.tActiveVoxelsBase, activeVoxelsBase);
ValueCell.update(v.tVolumeData, volumeData);
ValueCell.updateIfChanged(v.uIsoValue, isoValue);
ValueCell.updateIfChanged(v.uSize, Math.pow(2, levels));
ValueCell.updateIfChanged(v.uLevels, levels);
ValueCell.updateIfChanged(v.uCount, count);
ValueCell.update(v.uGridDim, gridDim);
ValueCell.update(v.uGridTexDim, gridTexDim);
ValueCell.update(v.uGridTransform, transform);
ValueCell.update(v.uScale, scale);
ctx.namedComputeRenderables[IsosurfaceName].update();
} else {
ctx.namedComputeRenderables[IsosurfaceName] = createIsosurfaceRenderable(ctx, activeVoxelsPyramid, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, height);
}
return ctx.namedComputeRenderables[IsosurfaceName];
}
function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, height: number) {
// console.log('uSize', Math.pow(2, levels))
const values: Values<typeof IsosurfaceSchema> = {
...QuadValues,
uQuadScale: ValueCell.create(Vec2.create(1, height / Math.pow(2, levels))),
tTriIndices: ValueCell.create(getTriIndices()),
uQuadScale: ValueCell.create(Vec2.create(1, height / Math.pow(2, levels))),
tActiveVoxelsPyramid: ValueCell.create(activeVoxelsPyramid),
tActiveVoxelsBase: ValueCell.create(activeVoxelsBase),
tVolumeData: ValueCell.create(volumeData),
uIsoValue: ValueCell.create(isoValue),
uIsoValue: ValueCell.create(isoValue),
uSize: ValueCell.create(Math.pow(2, levels)),
uLevels: ValueCell.create(levels),
uCount: ValueCell.create(count),
@@ -57,7 +85,6 @@ function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture
uGridDim: ValueCell.create(gridDim),
uGridTexDim: ValueCell.create(gridTexDim),
uGridTransform: ValueCell.create(transform),
uScale: ValueCell.create(scale),
};
@@ -86,27 +113,23 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
// console.log('iso', 'gridDim', gridDim, 'scale', scale, 'gridTexDim', gridTexDim)
// console.log('iso volumeData', volumeData)
const framebuffer = resources.framebuffer();
if (!ctx.namedFramebuffers[IsosurfaceName]) {
ctx.namedFramebuffers[IsosurfaceName] = resources.framebuffer();
}
const framebuffer = ctx.namedFramebuffers[IsosurfaceName];
let needsClear = false;
const w = pyramidTex.getWidth();
const h = pyramidTex.getHeight();
if (!vertexGroupTexture) {
vertexGroupTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest');
vertexGroupTexture.define(pyramidTex.getWidth(), pyramidTex.getHeight());
} else if (vertexGroupTexture.getWidth() !== pyramidTex.getWidth() || vertexGroupTexture.getHeight() !== pyramidTex.getHeight()) {
vertexGroupTexture.define(pyramidTex.getWidth(), pyramidTex.getHeight());
} else {
needsClear = true;
}
vertexGroupTexture.define(w, h);
if (!normalTexture) {
normalTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest');
normalTexture.define(pyramidTex.getWidth(), pyramidTex.getHeight());
} else if (normalTexture.getWidth() !== pyramidTex.getWidth() || normalTexture.getHeight() !== pyramidTex.getHeight()) {
normalTexture.define(pyramidTex.getWidth(), pyramidTex.getHeight());
} else {
needsClear = true;
}
normalTexture.define(w, h);
// const infoTex = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
// infoTex.define(pyramidTex.width, pyramidTex.height)
@@ -147,11 +170,11 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
]);
setRenderingDefaults(ctx);
gl.viewport(0, 0, pyramidTex.getWidth(), pyramidTex.getHeight());
if (needsClear) gl.clear(gl.COLOR_BUFFER_BIT);
gl.viewport(0, 0, w, h);
gl.clear(gl.COLOR_BUFFER_BIT);
renderable.render();
gl.finish();
gl.flush();
// const vgt = readTexture(ctx, vertexGroupTexture, pyramidTex.width, height)
// console.log('vertexGroupTexture', vgt.array.subarray(0, 4 * count))

View File

@@ -21,6 +21,7 @@ export type RenderableState = {
colorOnly: boolean
opaque: boolean
writeDepth: boolean
noClip: boolean
}
export interface Renderable<T extends RenderableValues> {

View File

@@ -22,6 +22,8 @@ export const TextureMeshSchema = {
dFlatShaded: DefineSpec('boolean'),
dDoubleSided: DefineSpec('boolean'),
dFlipSided: DefineSpec('boolean'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
dGeoTexture: DefineSpec('boolean'),
};
export type TextureMeshSchema = typeof TextureMeshSchema

View File

@@ -76,6 +76,7 @@ export function printImageData(imageData: ImageData, scale = 1, pixelated = fals
img.style.top = '0px';
img.style.left = '0px';
img.style.border = 'solid grey';
img.style.pointerEvents = 'none';
document.body.appendChild(img);
}, 'image/png');
}

View File

@@ -262,13 +262,20 @@ namespace Renderer {
}
let definesNeedUpdate = false;
if (r.values.dClipObjectCount.ref.value !== clip.objects.count) {
ValueCell.update(r.values.dClipObjectCount, clip.objects.count);
definesNeedUpdate = true;
}
if (r.values.dClipVariant.ref.value !== clip.variant) {
ValueCell.update(r.values.dClipVariant, clip.variant);
definesNeedUpdate = true;
if (r.state.noClip) {
if (r.values.dClipObjectCount.ref.value !== 0) {
ValueCell.update(r.values.dClipObjectCount, 0);
definesNeedUpdate = true;
}
} else {
if (r.values.dClipObjectCount.ref.value !== clip.objects.count) {
ValueCell.update(r.values.dClipObjectCount, clip.objects.count);
definesNeedUpdate = true;
}
if (r.values.dClipVariant.ref.value !== clip.variant) {
ValueCell.update(r.values.dClipVariant, clip.variant);
definesNeedUpdate = true;
}
}
if (definesNeedUpdate) r.update();

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Michael Krone <michael.krone@uni-tuebingen.de>
@@ -31,7 +31,7 @@ uniform float uCurrentX;
uniform float uCurrentY;
uniform float uAlpha;
uniform float uResolution;
uniform float uRadiusFactor;
uniform float uRadiusFactorInv;
void main() {
vec2 v = gl_FragCoord.xy - vec2(uCurrentX, uCurrentY) - 0.5;
@@ -40,16 +40,16 @@ void main() {
#if defined(dCalcType_density)
float density = exp(-uAlpha * ((dist * dist) * vRadiusSqInv));
gl_FragColor.a = density / uRadiusFactor;
gl_FragColor.a = density * uRadiusFactorInv;
#elif defined(dCalcType_minDistance)
gl_FragColor.a = 1.0 - dist / uRadiusFactor;
gl_FragColor.a = 1.0 - dist * uRadiusFactorInv;
#elif defined(dCalcType_groupId)
#if defined(dGridTexType_2d)
float minDistance = 1.0 - texture2D(tMinDistanceTex, (gl_FragCoord.xy) / (uGridTexDim.xy / uGridTexScale)).a;
#elif defined(dGridTexType_3d)
float minDistance = 1.0 - texelFetch(tMinDistanceTex, ivec3(gl_FragCoord.xy, uCurrentSlice), 0).a;
#endif
if (dist / uRadiusFactor > minDistance + uResolution * 0.05)
if (dist * uRadiusFactorInv > minDistance + uResolution * 0.05)
discard;
gl_FragColor.rgb = encodeFloatRGB(vGroup);
#endif

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Michael Krone <michael.krone@uni-tuebingen.de>
@@ -21,7 +21,6 @@ varying float vRadiusSqInv;
uniform vec3 uBboxSize;
uniform vec3 uBboxMin;
uniform float uCurrentSlice;
uniform float uResolution;
void main() {
@@ -29,7 +28,7 @@ void main() {
#if defined(dCalcType_groupId)
vGroup = aGroup;
#endif
gl_PointSize = floor(((aRadius * 6.0) / uResolution) + 0.5);
gl_PointSize = ceil(((aRadius * 3.0) / uResolution) + uResolution);
vPosition = (aPosition - uBboxMin) / uResolution;
gl_Position = vec4(((aPosition - uBboxMin) / uBboxSize) * 2.0 - 1.0, 1.0);
}

View File

@@ -4,9 +4,9 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Box3D } from '../geometry';
import { Box3D, DensityData, DensityTextureData } from '../geometry';
import { RuntimeContext, Task } from '../../mol-task';
import { PositionData, DensityData } from './common';
import { PositionData } from './common';
import { GaussianDensityCPU } from './gaussian-density/cpu';
import { WebGLContext } from '../../mol-gl/webgl/context';
import { Texture } from '../../mol-gl/webgl/texture';
@@ -33,13 +33,21 @@ export const DefaultGaussianDensityProps = {
};
export type GaussianDensityProps = typeof DefaultGaussianDensityProps
export type GaussianDensityData = {
radiusFactor: number
} & DensityData
export type GaussianDensityTextureData = {
radiusFactor: number
} & DensityTextureData
export function computeGaussianDensity(position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps, webgl?: WebGLContext) {
return Task.create('Gaussian Density', async ctx => {
return await GaussianDensity(ctx, position, box, radius, props, webgl);
});
}
export async function GaussianDensity(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps, webgl?: WebGLContext): Promise<DensityData> {
export async function GaussianDensity(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps, webgl?: WebGLContext): Promise<GaussianDensityData> {
if (props.useGpu) {
if (!GaussianDensityGPU) throw 'GPU computation not supported on this platform';
if (!webgl) throw 'No WebGL context provided';
@@ -65,7 +73,7 @@ function _computeGaussianDensityTexture(type: '2d' | '3d', position: PositionDat
if (!GaussianDensityTexture) throw 'GPU computation not supported on this platform';
return Task.create('Gaussian Density', async ctx => {
return type === '2d' ?
GaussianDensityTexture2d(webgl, position, box, radius, props, texture) :
GaussianDensityTexture2d(webgl, position, box, radius, false, props, texture) :
GaussianDensityTexture3d(webgl, position, box, radius, props, texture);
});
}

View File

@@ -7,12 +7,12 @@
import { Box3D, fillGridDim } from '../../geometry';
import { Vec3, Mat4, Tensor } from '../../linear-algebra';
import { RuntimeContext } from '../../../mol-task';
import { PositionData, DensityData } from '../common';
import { PositionData } from '../common';
import { OrderedSet } from '../../../mol-data/int';
import { GaussianDensityProps } from '../gaussian-density';
import { GaussianDensityProps, GaussianDensityData } from '../gaussian-density';
import { fasterExp } from '../../approx';
export async function GaussianDensityCPU(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps): Promise<DensityData> {
export async function GaussianDensityCPU(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps): Promise<GaussianDensityData> {
const { resolution, radiusOffset, smoothness } = props;
const scaleFactor = 1 / resolution;
@@ -129,5 +129,5 @@ export async function GaussianDensityCPU(ctx: RuntimeContext, position: Position
Mat4.fromScaling(transform, Vec3.create(resolution, resolution, resolution));
Mat4.setTranslation(transform, expandedBox.min);
return { field, idField, transform };
return { field, idField, transform, radiusFactor: 1 };
}

View File

@@ -1,13 +1,13 @@
/**
* 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 Alexander Rose <alexander.rose@weirdbyte.de>
* @author Michael Krone <michael.krone@uni-tuebingen.de>
*/
import { PositionData, DensityData, DensityTextureData } from '../common';
import { PositionData } from '../common';
import { Box3D } from '../../geometry';
import { GaussianDensityGPUProps } from '../gaussian-density';
import { GaussianDensityGPUProps, GaussianDensityData, GaussianDensityTextureData } from '../gaussian-density';
import { OrderedSet } from '../../../mol-data/int';
import { Vec3, Tensor, Mat4, Vec2 } from '../../linear-algebra';
import { ValueCell } from '../../../mol-util';
@@ -40,7 +40,7 @@ export const GaussianDensitySchema = {
uGridTexScale: UniformSpec('v2', true),
uAlpha: UniformSpec('f', true),
uResolution: UniformSpec('f', true),
uRadiusFactor: UniformSpec('f', true),
uRadiusFactorInv: UniformSpec('f', true),
tMinDistanceTex: TextureSpec('texture', 'rgba', 'float', 'nearest'),
dGridTexType: DefineSpec('string', ['2d', '3d']),
@@ -68,35 +68,35 @@ function getTexture(name: string, webgl: WebGLContext, kind: TextureKind, format
return webgl.namedTextures[_name];
}
export function GaussianDensityGPU(position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, webgl: WebGLContext): DensityData {
export function GaussianDensityGPU(position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, webgl: WebGLContext): GaussianDensityData {
// always use texture2d when the gaussian density needs to be downloaded from the GPU,
// it's faster than texture3d
// console.time('GaussianDensityTexture2d')
const tmpTexture = getTexture('tmp', webgl, 'image-uint8', 'rgba', 'ubyte', 'linear');
const { scale, bbox, texture, gridDim, gridTexDim } = calcGaussianDensityTexture2d(webgl, position, box, radius, props, tmpTexture);
const { scale, bbox, texture, gridDim, gridTexDim, radiusFactor } = calcGaussianDensityTexture2d(webgl, position, box, radius, false, props, tmpTexture);
// webgl.waitForGpuCommandsCompleteSync()
// console.timeEnd('GaussianDensityTexture2d')
const { field, idField } = fieldFromTexture2d(webgl, texture, gridDim, gridTexDim);
return { field, idField, transform: getTransform(scale, bbox) };
return { field, idField, transform: getTransform(scale, bbox), radiusFactor };
}
export function GaussianDensityTexture(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, oldTexture?: Texture): DensityTextureData {
export function GaussianDensityTexture(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, oldTexture?: Texture): GaussianDensityTextureData {
return webgl.isWebGL2 ?
GaussianDensityTexture3d(webgl, position, box, radius, props, oldTexture) :
GaussianDensityTexture2d(webgl, position, box, radius, props, oldTexture);
GaussianDensityTexture2d(webgl, position, box, radius, false, props, oldTexture);
}
export function GaussianDensityTexture2d(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, oldTexture?: Texture): DensityTextureData {
return finalizeGaussianDensityTexture(calcGaussianDensityTexture2d(webgl, position, box, radius, props, oldTexture));
export function GaussianDensityTexture2d(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, powerOfTwo: boolean, props: GaussianDensityGPUProps, oldTexture?: Texture): GaussianDensityTextureData {
return finalizeGaussianDensityTexture(calcGaussianDensityTexture2d(webgl, position, box, radius, powerOfTwo, props, oldTexture));
}
export function GaussianDensityTexture3d(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, oldTexture?: Texture): DensityTextureData {
export function GaussianDensityTexture3d(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, oldTexture?: Texture): GaussianDensityTextureData {
return finalizeGaussianDensityTexture(calcGaussianDensityTexture3d(webgl, position, box, radius, props, oldTexture));
}
function finalizeGaussianDensityTexture({ texture, scale, bbox, gridDim, gridTexDim, gridTexScale }: GaussianDensityTextureData): DensityTextureData {
return { transform: getTransform(scale, bbox), texture, bbox, gridDim, gridTexDim, gridTexScale };
function finalizeGaussianDensityTexture({ texture, scale, bbox, gridDim, gridTexDim, gridTexScale, radiusFactor }: _GaussianDensityTextureData): GaussianDensityTextureData {
return { transform: getTransform(scale, bbox), texture, bbox, gridDim, gridTexDim, gridTexScale, radiusFactor };
}
function getTransform(scale: Vec3, bbox: Box3D) {
@@ -108,30 +108,34 @@ function getTransform(scale: Vec3, bbox: Box3D) {
//
type GaussianDensityTextureData = {
type _GaussianDensityTextureData = {
texture: Texture,
scale: Vec3,
bbox: Box3D,
gridDim: Vec3,
gridTexDim: Vec3
gridTexScale: Vec2
radiusFactor: number
}
function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, texture?: Texture): GaussianDensityTextureData {
function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, powerOfTwo: boolean, props: GaussianDensityGPUProps, texture?: Texture): _GaussianDensityTextureData {
// console.log('2d');
const { gl, resources, state, extensions: { colorBufferFloat, textureFloat } } = webgl;
const { smoothness, resolution } = props;
const { drawCount, positions, radii, groups, scale, expandedBox, dim, maxRadius } = prepareGaussianDensityData(position, box, radius, props);
const [ dx, dy, dz ] = dim;
const { texDimX, texDimY, texCols } = getTexture2dSize(dim);
// console.log({ texDimX, texDimY, texCols, texSize, dim });
const { texDimX, texDimY, texCols, powerOfTwoSize } = getTexture2dSize(dim);
// console.log({ texDimX, texDimY, texCols, powerOfTwoSize, dim });
const gridTexDim = Vec3.create(texDimX, texDimY, 0);
const gridTexScale = Vec2.create(texDimX / texDimX, texDimY / texDimY);
const gridTexScale = Vec2.create(texDimX / powerOfTwoSize, texDimY / powerOfTwoSize);
const radiusFactor = maxRadius * 2;
const width = powerOfTwo ? powerOfTwoSize : texDimX;
const height = powerOfTwo ? powerOfTwoSize : texDimY;
const minDistTex = getTexture('min-dist-2d', webgl, 'image-uint8', 'rgba', 'ubyte', 'nearest');
minDistTex.define(texDimX, texDimY);
minDistTex.define(width, height);
const renderable = getGaussianDensityRenderable(webgl, drawCount, positions, radii, groups, minDistTex, expandedBox, dim, gridTexDim, gridTexScale, smoothness, resolution, radiusFactor);
@@ -146,14 +150,19 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat
if (!texture) texture = colorBufferFloat && textureFloat
? resources.texture('image-float32', 'rgba', 'float', 'linear')
: resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
texture.define(texDimX, texDimY);
texture.define(width, height);
// console.log(renderable)
function render(fbTex: Texture, clear: boolean) {
state.currentRenderItemId = -1;
fbTex.attachFramebuffer(framebuffer, 0);
if (clear) gl.clear(gl.COLOR_BUFFER_BIT);
if (clear) {
gl.viewport(0, 0, width, height);
gl.scissor(0, 0, width, height);
gl.clear(gl.COLOR_BUFFER_BIT);
}
ValueCell.update(uCurrentY, 0);
let currCol = 0;
let currY = 0;
let currX = 0;
@@ -164,10 +173,11 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat
currX = 0;
ValueCell.update(uCurrentY, currY);
}
// console.log({ i, currX, currY })
// console.log({ i, currX, currY });
ValueCell.update(uCurrentX, currX);
ValueCell.update(uCurrentSlice, i);
gl.viewport(currX, currY, dx, dy);
gl.scissor(currX, currY, dx, dy);
renderable.render();
++currCol;
currX += dx;
@@ -184,12 +194,12 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat
setupGroupIdRendering(webgl, renderable);
render(texture, false);
// printTexture(webgl, texture, 1);
// printTexture(webgl, minDistTex, 0.75);
return { texture, scale, bbox: expandedBox, gridDim: dim, gridTexDim, gridTexScale };
return { texture, scale, bbox: expandedBox, gridDim: dim, gridTexDim, gridTexScale, radiusFactor };
}
function calcGaussianDensityTexture3d(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, texture?: Texture): GaussianDensityTextureData {
function calcGaussianDensityTexture3d(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, texture?: Texture): _GaussianDensityTextureData {
// console.log('3d');
const { gl, resources, state, extensions: { colorBufferFloat, textureFloat } } = webgl;
const { smoothness, resolution } = props;
@@ -213,6 +223,7 @@ function calcGaussianDensityTexture3d(webgl: WebGLContext, position: PositionDat
framebuffer.bind();
setRenderingDefaults(webgl);
gl.viewport(0, 0, dx, dy);
gl.scissor(0, 0, dx, dy);
if (!texture) texture = colorBufferFloat && textureFloat
? resources.texture('volume-float32', 'rgba', 'float', 'linear')
@@ -239,7 +250,7 @@ function calcGaussianDensityTexture3d(webgl: WebGLContext, position: PositionDat
setupGroupIdRendering(webgl, renderable);
render(texture, false);
return { texture, scale, bbox: expandedBox, gridDim: dim, gridTexDim: dim, gridTexScale };
return { texture, scale, bbox: expandedBox, gridDim: dim, gridTexDim: dim, gridTexScale, radiusFactor };
}
//
@@ -303,7 +314,7 @@ function getGaussianDensityRenderable(webgl: WebGLContext, drawCount: number, po
ValueCell.update(v.uGridTexScale, gridTexScale);
ValueCell.updateIfChanged(v.uAlpha, smoothness);
ValueCell.updateIfChanged(v.uResolution, resolution);
ValueCell.updateIfChanged(v.uRadiusFactor, radiusFactor);
ValueCell.updateIfChanged(v.uRadiusFactorInv, 1 / radiusFactor);
ValueCell.update(v.tMinDistanceTex, minDistanceTexture);
ValueCell.updateIfChanged(v.dGridTexType, minDistanceTexture.getDepth() > 0 ? '3d' : '2d');
@@ -337,7 +348,7 @@ function createGaussianDensityRenderable(webgl: WebGLContext, drawCount: number,
uGridTexScale: ValueCell.create(gridTexScale),
uAlpha: ValueCell.create(smoothness),
uResolution: ValueCell.create(resolution),
uRadiusFactor: ValueCell.create(radiusFactor),
uRadiusFactorInv: ValueCell.create(1 / radiusFactor),
tMinDistanceTex: ValueCell.create(minDistanceTexture),
dGridTexType: ValueCell.create(minDistanceTexture.getDepth() > 0 ? '3d' : '2d'),
@@ -355,7 +366,7 @@ function setRenderingDefaults(ctx: WebGLContext) {
state.disable(gl.CULL_FACE);
state.enable(gl.BLEND);
state.disable(gl.DEPTH_TEST);
state.disable(gl.SCISSOR_TEST);
state.enable(gl.SCISSOR_TEST);
state.depthMask(false);
state.clearColor(0, 0, 0, 0);
}
@@ -409,7 +420,8 @@ function getTexture2dSize(gridDim: Vec3) {
} else {
texDimX = gridDim[0] * gridDim[2];
}
return { texDimX, texDimY, texRows, texCols, powerOfTwoSize: texDimY };
// console.log(texDimX, texDimY, texDimY < powerOfTwoSize ? powerOfTwoSize : powerOfTwoSize * 2);
return { texDimX, texDimY, texRows, texCols, powerOfTwoSize: texDimY < powerOfTwoSize ? powerOfTwoSize : powerOfTwoSize * 2 };
}
export function fieldFromTexture2d(ctx: WebGLContext, texture: Texture, dim: Vec3, texDim: Vec3) {

View File

@@ -97,7 +97,7 @@ class Structure {
/** Count of all bonds (intra- and inter-unit) in the structure */
get bondCount() {
if (!this._props.bondCount) {
if (this._props.bondCount === -1) {
this._props.bondCount = this.interUnitBonds.edgeCount + Bond.getIntraUnitBondCount(this);
}
return this._props.bondCount;

View File

@@ -162,7 +162,7 @@ namespace Bond {
let count = 0;
for (let i = 0, il = structure.units.length; i < il; ++i) {
const u = structure.units[i];
if (Unit.isAtomic(u)) count += u.bonds.edgeCount / 2; // only count one direction
if (Unit.isAtomic(u)) count += u.bonds.edgeCount;
}
return count;
}

View File

@@ -22,6 +22,7 @@ import { ElementSequenceWrapper } from './sequence/element';
import { elementLabel } from '../mol-theme/label';
import { Icon, HelpOutlineSvg } from './controls/icons';
import { StructureSelectionManager } from '../mol-plugin-state/manager/structure/selection';
import { arrayEqual } from '../mol-util/array';
const MaxDisplaySequenceLength = 5000;
@@ -34,14 +35,14 @@ function opKey(l: StructureElement.Location) {
}
function splitModelEntityId(modelEntityId: string) {
const [ modelIdx, entityId ] = modelEntityId.split('|');
return [ parseInt(modelIdx), entityId ];
const [modelIdx, entityId] = modelEntityId.split('|');
return [parseInt(modelIdx), entityId];
}
function getSequenceWrapper(state: SequenceViewState, structureSelection: StructureSelectionManager): SequenceWrapper.Any | string {
function getSequenceWrapper(state: { structure: Structure, modelEntityId: string, chainGroupId: number, operatorKey: string }, structureSelection: StructureSelectionManager): SequenceWrapper.Any | string {
const { structure, modelEntityId, chainGroupId, operatorKey } = state;
const l = StructureElement.Location.create(structure);
const [ modelIdx, entityId ] = splitModelEntityId(modelEntityId);
const [modelIdx, entityId] = splitModelEntityId(modelEntityId);
const units: Unit[] = [];
@@ -93,7 +94,7 @@ function getSequenceWrapper(state: SequenceViewState, structureSelection: Struct
}
}
function getModelEntityOptions(structure: Structure) {
function getModelEntityOptions(structure: Structure, polymersOnly = false) {
const options: [string, string][] = [];
const l = StructureElement.Location.create(structure);
const seen = new Set<string>();
@@ -104,17 +105,18 @@ function getModelEntityOptions(structure: Structure) {
const modelIdx = structure.getModelIndex(unit.model);
const key = `${modelIdx}|${id}`;
if (seen.has(key)) continue;
if (polymersOnly && SP.entity.type(l) !== 'polymer') continue;
let description = SP.entity.pdbx_description(l).join(', ');
if (structure.models.length) {
if (structure.representativeModel) { // indicates model trajectory
description += ` (Model ${structure.models[modelIdx].modelNum})`;
} else if (description.startsWith('Polymer ')) { // indicates generic entity name
} else if (description.startsWith('Polymer ')) { // indicates generic entity name
description += ` (${structure.models[modelIdx].entry})`;
}
}
const label = `${id}: ${description}`;
options.push([ key, label ]);
options.push([key, label]);
seen.add(key);
}
@@ -126,7 +128,7 @@ function getChainOptions(structure: Structure, modelEntityId: string) {
const options: [number, string][] = [];
const l = StructureElement.Location.create(structure);
const seen = new Set<number>();
const [ modelIdx, entityId ] = splitModelEntityId(modelEntityId);
const [modelIdx, entityId] = splitModelEntityId(modelEntityId);
for (const unit of structure.units) {
StructureElement.Location.set(l, structure, unit, unit.elements[0]);
@@ -140,7 +142,7 @@ function getChainOptions(structure: Structure, modelEntityId: string) {
// - more than one chain in a unit
let label = elementLabel(l, { granularity: 'chain', hidePrefix: true, htmlStyling: false });
options.push([ id, label ]);
options.push([id, label]);
seen.add(id);
}
@@ -152,7 +154,7 @@ function getOperatorOptions(structure: Structure, modelEntityId: string, chainGr
const options: [string, string][] = [];
const l = StructureElement.Location.create(structure);
const seen = new Set<string>();
const [ modelIdx, entityId ] = splitModelEntityId(modelEntityId);
const [modelIdx, entityId] = splitModelEntityId(modelEntityId);
for (const unit of structure.units) {
StructureElement.Location.set(l, structure, unit, unit.elements[0]);
@@ -164,7 +166,7 @@ function getOperatorOptions(structure: Structure, modelEntityId: string, chainGr
if (seen.has(id)) continue;
const label = unit.conformation.operator.name;
options.push([ id, label ]);
options.push([id, label]);
seen.add(id);
}
@@ -174,49 +176,64 @@ function getOperatorOptions(structure: Structure, modelEntityId: string, chainGr
function getStructureOptions(state: State) {
const options: [string, string][] = [];
const all: Structure[] = [];
const structures = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure));
for (const s of structures) {
if (!s.obj?.data) continue;
all.push(s.obj.data);
options.push([s.transform.ref, s.obj!.data.label]);
}
if (options.length === 0) options.push(['', 'No structure']);
return options;
return { options, all };
}
export type SequenceViewMode = 'single' | 'polymers' | 'all'
const SequenceViewModeParam = PD.Select<SequenceViewMode>('single', [['single', 'Chain'], ['polymers', 'Polymers'], ['all', 'Everything']]);
type SequenceViewState = {
structureOptions: { options: [string, string][], all: Structure[] },
structure: Structure,
structureRef: string,
modelEntityId: string,
chainGroupId: number,
operatorKey: string
operatorKey: string,
mode: SequenceViewMode
}
export class SequenceView extends PluginUIComponent<{ }, SequenceViewState> {
state = { structure: Structure.Empty, structureRef: '', modelEntityId: '', chainGroupId: -1, operatorKey: '' }
export class SequenceView extends PluginUIComponent<{ defaultMode?: SequenceViewMode }, SequenceViewState> {
state: SequenceViewState = { structureOptions: { options: [], all: [] }, structure: Structure.Empty, structureRef: '', modelEntityId: '', chainGroupId: -1, operatorKey: '', mode: 'single' }
componentDidMount() {
if (this.plugin.state.data.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure)).length > 0) this.setState(this.getInitialState());
this.subscribe(this.plugin.state.events.object.updated, ({ ref, obj }) => {
if (ref === this.state.structureRef && obj && obj.type === PSO.Molecule.Structure.type && obj.data !== this.state.structure) {
this.setState(this.getInitialState());
this.sync();
}
});
this.subscribe(this.plugin.state.events.object.created, ({ obj }) => {
if (obj && obj.type === PSO.Molecule.Structure.type) {
this.setState(this.getInitialState());
this.sync();
}
});
this.subscribe(this.plugin.state.events.object.removed, ({ obj }) => {
if (obj && obj.type === PSO.Molecule.Structure.type && obj.data === this.state.structure) {
this.setState(this.getInitialState());
this.sync();
}
});
}
private sync() {
const structureOptions = getStructureOptions(this.plugin.state.data);
if (arrayEqual(structureOptions.all, this.state.structureOptions.all)) return;
this.setState(this.getInitialState());
}
private getStructure(ref: string) {
const state = this.plugin.state.data;
const cell = state.select(ref)[0];
@@ -224,12 +241,40 @@ export class SequenceView extends PluginUIComponent<{ }, SequenceViewState> {
return (cell.obj as PSO.Molecule.Structure).data;
}
private getSequenceWrapper() {
return getSequenceWrapper(this.state, this.plugin.managers.structure.selection);
private getSequenceWrapper(params: SequenceView['params']) {
return {
wrapper: getSequenceWrapper(this.state, this.plugin.managers.structure.selection),
label: `${PD.optionLabel(params.chain, this.state.chainGroupId)} | ${PD.optionLabel(params.entity, this.state.modelEntityId)}`
};
}
private getSequenceWrappers(params: SequenceView['params']) {
if (this.state.mode === 'single') return [this.getSequenceWrapper(params)];
const structure = this.getStructure(this.state.structureRef);
const wrappers: { wrapper: (string | SequenceWrapper.Any), label: string }[] = [];
for (const [modelEntityId, eLabel] of getModelEntityOptions(structure, this.state.mode === 'polymers')) {
for (const [chainGroupId, cLabel] of getChainOptions(structure, modelEntityId)) {
for (const [operatorKey] of getOperatorOptions(structure, modelEntityId, chainGroupId)) {
wrappers.push({
wrapper: getSequenceWrapper({
structure,
modelEntityId,
chainGroupId,
operatorKey
}, this.plugin.managers.structure.selection),
label: `${cLabel} | ${eLabel}`
});
}
}
}
return wrappers;
}
private getInitialState(): SequenceViewState {
const structureRef = getStructureOptions(this.plugin.state.data)[0][0];
const structureOptions = getStructureOptions(this.plugin.state.data);
const structureRef = structureOptions.options[0][0];
const structure = this.getStructure(structureRef);
let modelEntityId = getModelEntityOptions(structure)[0][0];
let chainGroupId = getChainOptions(structure, modelEntityId)[0][0];
@@ -239,20 +284,20 @@ export class SequenceView extends PluginUIComponent<{ }, SequenceViewState> {
chainGroupId = this.state.chainGroupId;
operatorKey = this.state.operatorKey;
}
return { structure, structureRef, modelEntityId, chainGroupId, operatorKey };
return { structureOptions, structure, structureRef, modelEntityId, chainGroupId, operatorKey, mode: this.props.defaultMode ?? 'single' };
}
private get params() {
const { structure, modelEntityId, chainGroupId } = this.state;
const structureOptions = getStructureOptions(this.plugin.state.data);
const { structureOptions, structure, modelEntityId, chainGroupId } = this.state;
const entityOptions = getModelEntityOptions(structure);
const chainOptions = getChainOptions(structure, modelEntityId);
const operatorOptions = getOperatorOptions(structure, modelEntityId, chainGroupId);
return {
structure: PD.Select(structureOptions[0][0], structureOptions, { shortLabel: true }),
structure: PD.Select(structureOptions.options[0][0], structureOptions.options, { shortLabel: true }),
entity: PD.Select(entityOptions[0][0], entityOptions, { shortLabel: true }),
chain: PD.Select(chainOptions[0][0], chainOptions, { shortLabel: true, twoColumns: true, label: 'Chain' }),
operator: PD.Select(operatorOptions[0][0], operatorOptions, { shortLabel: true, twoColumns: true })
operator: PD.Select(operatorOptions[0][0], operatorOptions, { shortLabel: true, twoColumns: true }),
mode: SequenceViewModeParam
};
}
@@ -261,16 +306,24 @@ export class SequenceView extends PluginUIComponent<{ }, SequenceViewState> {
structure: this.state.structureRef,
entity: this.state.modelEntityId,
chain: this.state.chainGroupId,
operator: this.state.operatorKey
operator: this.state.operatorKey,
mode: this.state.mode
};
}
private setParamProps = (p: { param: PD.Base<any>, name: string, value: any }) => {
const state = { ...this.state };
switch (p.name) {
case 'mode':
state.mode = p.value;
if (this.state.mode === state.mode) return;
if (state.mode === 'all' || state.mode === 'polymers') {
break;
}
case 'structure':
state.structureRef = p.value;
state.structure = this.getStructure(p.value);
if (p.name === 'structure') state.structureRef = p.value;
state.structure = this.getStructure(state.structureRef);
state.modelEntityId = getModelEntityOptions(state.structure)[0][0];
state.chainGroupId = getChainOptions(state.structure, state.modelEntityId)[0][0];
state.operatorKey = getOperatorOptions(state.structure, state.modelEntityId, state.chainGroupId)[0][0];
@@ -296,17 +349,16 @@ export class SequenceView extends PluginUIComponent<{ }, SequenceViewState> {
return <div className='msp-sequence'>
<div className='msp-sequence-select'>
<Icon svg={HelpOutlineSvg} style={{ cursor: 'help', position: 'absolute', right: 0, top: 0 }}
title='This shows a single sequence. Use the controls to show a different sequence.'/>
title='Shows a sequence of one or more chains. Use the controls to alter selection.' />
<span>Sequence</span><span style={{ fontWeight: 'normal' }}>No structure available</span>
</div>
</div>;
}
const sequenceWrapper = this.getSequenceWrapper();
const params = this.params;
const values = this.values;
const sequenceWrappers = this.getSequenceWrappers(params);
return <div className='msp-sequence'>
<div className='msp-sequence-select'>
@@ -315,16 +367,27 @@ export class SequenceView extends PluginUIComponent<{ }, SequenceViewState> {
<span>Sequence of</span>
<PureSelectControl title={`[Structure] ${PD.optionLabel(params.structure, values.structure)}`} param={params.structure} name='structure' value={values.structure} onChange={this.setParamProps} />
<PureSelectControl title={`[Entity] ${PD.optionLabel(params.entity, values.entity)}`} param={params.entity} name='entity' value={values.entity} onChange={this.setParamProps} />
<PureSelectControl title={`[Chain] ${PD.optionLabel(params.chain, values.chain)}`} param={params.chain} name='chain' value={values.chain} onChange={this.setParamProps} />
<PureSelectControl title={`[Mode]`} param={SequenceViewModeParam} name='mode' value={values.mode} onChange={this.setParamProps} />
{values.mode === 'single' && <PureSelectControl title={`[Entity] ${PD.optionLabel(params.entity, values.entity)}`} param={params.entity} name='entity' value={values.entity} onChange={this.setParamProps} />}
{values.mode === 'single' && <PureSelectControl title={`[Chain] ${PD.optionLabel(params.chain, values.chain)}`} param={params.chain} name='chain' value={values.chain} onChange={this.setParamProps} />}
{params.operator.options.length > 1 && <>
<PureSelectControl title={`[Instance] ${PD.optionLabel(params.operator, values.operator)}`} param={params.operator} name='operator' value={values.operator} onChange={this.setParamProps} />
</>}
</div>
{typeof sequenceWrapper === 'string'
? <div className='msp-sequence-wrapper msp-sequence-wrapper-non-empty'>{sequenceWrapper}</div>
: <Sequence sequenceWrapper={sequenceWrapper} />}
<NonEmptySequenceWrapper>
{sequenceWrappers.map((s, i) => {
return typeof s.wrapper === 'string'
? <div key={i} className='msp-sequence-wrapper'>{s.wrapper}</div>
: <Sequence key={i} sequenceWrapper={s.wrapper} label={values.mode === 'single' ? void 0 : s.label} />;
})}
</NonEmptySequenceWrapper>
</div>;
}
}
function NonEmptySequenceWrapper({ children }: { children: React.ReactNode }) {
return <div className='msp-sequence-wrapper-non-empty'>
{children}
</div>;
}

View File

@@ -19,7 +19,8 @@ import { Representation } from '../../mol-repr/representation';
type SequenceProps = {
sequenceWrapper: SequenceWrapper.Any,
sequenceNumberPeriod?: number,
hideSequenceNumbers?: boolean
hideSequenceNumbers?: boolean,
label?: string
}
/** Note, if this is changed, the CSS for `msp-sequence-number` needs adjustment too */
@@ -292,7 +293,7 @@ export class Sequence<P extends SequenceProps> extends PluginUIComponent<P> {
this.updateMarker();
return <div
className='msp-sequence-wrapper msp-sequence-wrapper-non-empty'
className='msp-sequence-wrapper'
onContextMenu={this.contextMenu}
onMouseDown={this.mouseDown}
onMouseUp={this.mouseUp}
@@ -300,6 +301,7 @@ export class Sequence<P extends SequenceProps> extends PluginUIComponent<P> {
onMouseLeave={this.mouseLeave}
ref={this.parentDiv}
>
{this.props.label && <div className='msp-sequence-label'>{this.props.label}</div>}
{elems}
</div>;
}

View File

@@ -43,14 +43,6 @@ $sequence-select-height: 24px;
// use $control-spacing for top to have space for sequence numebrs
padding: $control-spacing $control-spacing $info-vertical-padding $control-spacing;
user-select: none;
width: 100%;
overflow-y: auto;
overflow-x: hidden;
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
}
.msp-sequence-wrapper-non-empty {
@@ -58,7 +50,15 @@ $sequence-select-height: 24px;
line-height: 180%;
font-family: "Courier New", monospace;
background: $msp-form-control-background;
width: 100%;
overflow-y: auto;
overflow-x: hidden;
position: absolute;
top: $sequence-select-height + 1px;
left: 0;
bottom: 0;
right: 0;
}
.msp-sequence-wrapper {
@@ -74,6 +74,14 @@ $sequence-select-height: 24px;
margin: 0em 0.2em 0em 0em;
}
.msp-sequence-label {
color: $sequence-number-color;
font-size: 90%;
line-height: 90%;
padding-bottom: 1em;
padding-left: 0.2em;
}
.msp-sequence-number {
color: $sequence-number-color;
word-break: keep-all;

View File

@@ -298,6 +298,12 @@ export function ComplexTextVisual<P extends ComplexTextParams>(builder: ComplexT
if (newProps.tetherLength !== currentProps.tetherLength) state.createGeometry = true;
if (newProps.tetherBaseWidth !== currentProps.tetherBaseWidth) state.createGeometry = true;
if (newProps.attachment !== currentProps.attachment) state.createGeometry = true;
if (newProps.fontFamily !== currentProps.fontFamily) state.createGeometry = true;
if (newProps.fontQuality !== currentProps.fontQuality) state.createGeometry = true;
if (newProps.fontStyle !== currentProps.fontStyle) state.createGeometry = true;
if (newProps.fontVariant !== currentProps.fontVariant) state.createGeometry = true;
if (newProps.fontWeight !== currentProps.fontWeight) state.createGeometry = true;
},
geometryUtils: Text.Utils
}, materialId);

View File

@@ -382,6 +382,12 @@ export function UnitsTextVisual<P extends UnitsTextParams>(builder: UnitsTextVis
if (newProps.tetherLength !== currentProps.tetherLength) state.createGeometry = true;
if (newProps.tetherBaseWidth !== currentProps.tetherBaseWidth) state.createGeometry = true;
if (newProps.attachment !== currentProps.attachment) state.createGeometry = true;
if (newProps.fontFamily !== currentProps.fontFamily) state.createGeometry = true;
if (newProps.fontQuality !== currentProps.fontQuality) state.createGeometry = true;
if (newProps.fontStyle !== currentProps.fontStyle) state.createGeometry = true;
if (newProps.fontVariant !== currentProps.fontVariant) state.createGeometry = true;
if (newProps.fontWeight !== currentProps.fontWeight) state.createGeometry = true;
},
geometryUtils: Text.Utils
}, materialId);

View File

@@ -33,10 +33,10 @@ export type GaussianSurfaceMeshParams = typeof GaussianSurfaceMeshParams
async function createGaussianSurfaceMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityProps, mesh?: Mesh): Promise<Mesh> {
const { smoothness } = props;
const { transform, field, idField } = await computeUnitGaussianDensity(structure, unit, props, ctx.webgl).runInContext(ctx.runtime);
const { transform, field, idField, radiusFactor } = await computeUnitGaussianDensity(structure, unit, props, ctx.webgl).runInContext(ctx.runtime);
const params = {
isoLevel: Math.exp(-smoothness),
isoLevel: Math.exp(-smoothness) / radiusFactor,
scalarField: field,
idField
};
@@ -81,10 +81,10 @@ export type StructureGaussianSurfaceMeshParams = typeof StructureGaussianSurface
async function createStructureGaussianSurfaceMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: GaussianDensityProps, mesh?: Mesh): Promise<Mesh> {
const { smoothness } = props;
const { transform, field, idField } = await computeStructureGaussianDensity(structure, props, ctx.webgl).runInContext(ctx.runtime);
const { transform, field, idField, radiusFactor } = await computeStructureGaussianDensity(structure, props, ctx.webgl).runInContext(ctx.runtime);
const params = {
isoLevel: Math.exp(-smoothness),
isoLevel: Math.exp(-smoothness) / radiusFactor,
scalarField: field,
idField
};
@@ -119,29 +119,49 @@ export function StructureGaussianSurfaceMeshVisual(materialId: number): ComplexV
//
const GaussianSurfaceName = 'gaussian-surface';
async function createGaussianSurfaceTextureMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityTextureProps, textureMesh?: TextureMesh): Promise<TextureMesh> {
if (!ctx.webgl) throw new Error('webgl context required to create gaussian surface texture-mesh');
const isoLevel = Math.exp(-props.smoothness);
const densityTextureData = await computeUnitGaussianDensityTexture2d(structure, unit, props, ctx.webgl).runInContext(ctx.runtime);
const { namedTextures, resources, extensions: { colorBufferFloat, textureFloat } } = ctx.webgl;
if (!namedTextures[GaussianSurfaceName]) {
namedTextures[GaussianSurfaceName] = colorBufferFloat && textureFloat
? resources.texture('image-float32', 'rgba', 'float', 'linear')
: resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
}
// console.time('computeUnitGaussianDensityTexture2d');
const densityTextureData = await computeUnitGaussianDensityTexture2d(structure, unit, true, props, ctx.webgl, namedTextures[GaussianSurfaceName]).runInContext(ctx.runtime);
// console.log(densityTextureData)
// console.log('vertexGroupTexture', readTexture(ctx.webgl, densityTextureData.texture))
// ctx.webgl.waitForGpuCommandsCompleteSync()
// ctx.webgl.waitForGpuCommandsCompleteSync();
// console.timeEnd('computeUnitGaussianDensityTexture2d');
const isoLevel = Math.exp(-props.smoothness) / densityTextureData.radiusFactor;
// console.time('calcActiveVoxels');
const activeVoxelsTex = calcActiveVoxels(ctx.webgl, densityTextureData.texture, densityTextureData.gridDim, densityTextureData.gridTexDim, isoLevel, densityTextureData.gridTexScale);
// ctx.webgl.waitForGpuCommandsCompleteSync()
// ctx.webgl.waitForGpuCommandsCompleteSync();
// console.timeEnd('calcActiveVoxels');
const compacted = createHistogramPyramid(ctx.webgl, activeVoxelsTex, densityTextureData.gridTexScale);
// ctx.webgl.waitForGpuCommandsCompleteSync()
// console.time('createHistogramPyramid');
const compacted = createHistogramPyramid(ctx.webgl, activeVoxelsTex, densityTextureData.gridTexScale, densityTextureData.gridTexDim);
// ctx.webgl.waitForGpuCommandsCompleteSync();
// console.timeEnd('createHistogramPyramid');
// console.time('createIsosurfaceBuffers');
const gv = createIsosurfaceBuffers(ctx.webgl, activeVoxelsTex, densityTextureData.texture, compacted, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.transform, isoLevel, textureMesh ? textureMesh.vertexGroupTexture.ref.value : undefined, textureMesh ? textureMesh.normalTexture.ref.value : undefined);
// ctx.webgl.waitForGpuCommandsCompleteSync()
// ctx.webgl.waitForGpuCommandsCompleteSync();
// console.timeEnd('createIsosurfaceBuffers');
// const boundingSphere = Sphere3D.zero()
// Sphere3D.addVec3(boundingSphere, boundingSphere, densityTextureData.gridDimension)
const boundingSphere = Sphere3D.fromBox3D(Sphere3D(), densityTextureData.bbox);
const boundingSphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, props.radiusOffset + getStructureExtraRadius(structure));
const surface = TextureMesh.create(gv.vertexCount, 1, gv.vertexGroupTexture, gv.normalTexture, boundingSphere, textureMesh);
// console.log({
// renderables: ctx.webgl.namedComputeRenderables,
// framebuffers: ctx.webgl.namedFramebuffers,
// textures: ctx.webgl.namedTextures,
// });
// ctx.webgl.waitForGpuCommandsCompleteSync()
return surface;
}

View File

@@ -54,12 +54,12 @@ export function computeUnitGaussianDensityTexture(structure: Structure, unit: Un
});
}
export function computeUnitGaussianDensityTexture2d(structure: Structure, unit: Unit, props: GaussianDensityTextureProps, webgl: WebGLContext, texture?: Texture) {
export function computeUnitGaussianDensityTexture2d(structure: Structure, unit: Unit, powerOfTwo: boolean, props: GaussianDensityTextureProps, webgl: WebGLContext, texture?: Texture) {
const { box } = unit.lookup3d.boundary;
const p = ensureReasonableResolution(box, props);
const { position, radius } = getUnitConformationAndRadius(structure, unit, p);
return Task.create('Gaussian Density', async ctx => {
return GaussianDensityTexture2d(webgl, position, box, radius, p, texture);
return GaussianDensityTexture2d(webgl, position, box, radius, powerOfTwo, p, texture);
});
}

View File

@@ -113,6 +113,7 @@ export function createDirectVolume2d(ctx: RuntimeContext, webgl: WebGLContext, v
const transform = Grid.getGridToCartesianTransform(volume.grid);
const bbox = getBoundingBox(gridDimension, transform);
// TODO: handle disposal
const texture = directVolume ? directVolume.gridTexture.ref.value : webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
texture.load(textureImage);
@@ -195,6 +196,7 @@ export function createDirectVolume3d(ctx: RuntimeContext, webgl: WebGLContext, v
const transform = Grid.getGridToCartesianTransform(volume.grid);
const bbox = getBoundingBox(gridDimension, transform);
// TODO: handle disposal
const texture = directVolume ? directVolume.gridTexture.ref.value : webgl.resources.texture('volume-uint8', 'rgba', 'ubyte', 'linear');
texture.load(textureVolume);

View File

@@ -68,7 +68,7 @@ async function init() {
console.timeEnd('gpu mc active2');
console.time('gpu mc pyramid2');
const compacted2 = createHistogramPyramid(webgl, activeVoxelsTex2, densityTextureData2.gridTexScale);
const compacted2 = createHistogramPyramid(webgl, activeVoxelsTex2, densityTextureData2.gridTexScale, densityTextureData2.gridTexDim);
webgl.waitForGpuCommandsCompleteSync();
console.timeEnd('gpu mc pyramid2');
@@ -91,7 +91,7 @@ async function init() {
console.timeEnd('gpu mc active');
console.time('gpu mc pyramid');
const compacted = createHistogramPyramid(webgl, activeVoxelsTex, densityTextureData.gridTexScale);
const compacted = createHistogramPyramid(webgl, activeVoxelsTex, densityTextureData.gridTexScale, densityTextureData.gridTexDim);
webgl.waitForGpuCommandsCompleteSync();
console.timeEnd('gpu mc pyramid');