diff --git a/src/mol-geo/geometry/direct-volume/direct-volume.ts b/src/mol-geo/geometry/direct-volume/direct-volume.ts index cd3e99c97a6d7dbe57b65ec79c37d2a5d70eddf7..b1df70de41a8d31d2c308c1d7b4d609890671957 100644 --- a/src/mol-geo/geometry/direct-volume/direct-volume.ts +++ b/src/mol-geo/geometry/direct-volume/direct-volume.ts @@ -5,7 +5,7 @@ */ import { hashFnv32a } from '../../../mol-data/util'; -import { LocationIterator } from '../../../mol-geo/util/location-iterator'; +import { LocationIterator, PositionLocation } from '../../../mol-geo/util/location-iterator'; import { RenderableState } from '../../../mol-gl/renderable'; import { DirectVolumeValues } from '../../../mol-gl/renderable/direct-volume'; import { calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util'; @@ -28,7 +28,6 @@ import { createTransferFunctionTexture, getControlPointsFromVec2Array } from './ import { createEmptyClipping } from '../clipping-data'; import { Grid, Volume } from '../../../mol-model/volume'; import { ColorNames } from '../../../mol-util/color/names'; -import { NullLocation } from '../../../mol-model/location'; const VolumeBox = Box(); @@ -182,9 +181,31 @@ export namespace DirectVolume { updateBoundingSphere, createRenderableState, updateRenderableState, - createPositionIterator: () => LocationIterator(1, 1, 1, () => NullLocation) + createPositionIterator }; + function createPositionIterator(directVolume: DirectVolume, transform: TransformData): LocationIterator { + const t = directVolume.transform.ref.value; + const [x, y, z] = directVolume.gridDimension.ref.value; + const groupCount = x * y * z; + const instanceCount = transform.instanceCount.ref.value; + const location = PositionLocation(); + const p = location.position; + const m = transform.aTransform.ref.value; + const getLocation = (groupIndex: number, instanceIndex: number) => { + const k = Math.floor(groupIndex / z); + p[0] = Math.floor(k / y); + p[1] = k % y; + p[2] = groupIndex % z; + Vec3.transformMat4(p, p, t); + if (instanceIndex >= 0) { + Vec3.transformMat4Offset(p, p, m, 0, 0, instanceIndex * 16); + } + return location; + }; + return LocationIterator(groupCount, instanceCount, 1, getLocation); + } + function getNormalizedIsoValue(out: Vec2, isoValue: Volume.IsoValue, stats: Vec4) { const [min, max, mean, sigma] = stats; const value = Volume.IsoValue.toAbsolute(isoValue, { min, max, mean, sigma }).absoluteValue; @@ -205,7 +226,8 @@ export namespace DirectVolume { const transparency = createEmptyTransparency(); const clipping = createEmptyClipping(); - const counts = { drawCount: VolumeBox.indices.length, vertexCount: VolumeBox.vertices.length / 3, groupCount, instanceCount }; + const [x, y, z] = gridDimension.ref.value; + const counts = { drawCount: VolumeBox.indices.length, vertexCount: x * y * z, groupCount, instanceCount }; const invariantBoundingSphere = Sphere3D.clone(directVolume.boundingSphere); const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount); diff --git a/src/mol-geo/geometry/geometry.ts b/src/mol-geo/geometry/geometry.ts index 867097cd9ae03d84994646d7edc43954bd8a1474..8a0993ee97af21237d6709092ab03012b8faffc2 100644 --- a/src/mol-geo/geometry/geometry.ts +++ b/src/mol-geo/geometry/geometry.ts @@ -80,7 +80,9 @@ export namespace Geometry { case 'spheres': return geometry.sphereCount * 4; case 'text': return geometry.charCount * 4; case 'lines': return geometry.lineCount * 4; - case 'direct-volume': return 24; + case 'direct-volume': + const [x, y, z] = geometry.gridDimension.ref.value; + return x * y * z; case 'image': return 4; case 'texture-mesh': return geometry.vertexCount / 3; } diff --git a/src/mol-gl/shader-code.ts b/src/mol-gl/shader-code.ts index 28c9160a2aa0c46153118a8657b77fc7a70732cd..211cd41ea709cd369a830b659ff08bcc4f48037f 100644 --- a/src/mol-gl/shader-code.ts +++ b/src/mol-gl/shader-code.ts @@ -55,6 +55,7 @@ import matrix_scale from './shader/chunks/matrix-scale.glsl'; import normal_frag_params from './shader/chunks/normal-frag-params.glsl'; import read_from_texture from './shader/chunks/read-from-texture.glsl'; import size_vert_params from './shader/chunks/size-vert-params.glsl'; +import texture3d_from_1d_trilinear from './shader/chunks/texture3d-from-1d-trilinear.glsl'; import texture3d_from_2d_linear from './shader/chunks/texture3d-from-2d-linear.glsl'; import texture3d_from_2d_nearest from './shader/chunks/texture3d-from-2d-nearest.glsl'; @@ -84,6 +85,7 @@ const ShaderChunks: { [k: string]: string } = { normal_frag_params, read_from_texture, size_vert_params, + texture3d_from_1d_trilinear, texture3d_from_2d_linear, texture3d_from_2d_nearest }; diff --git a/src/mol-gl/shader/chunks/apply-marker-color.glsl.ts b/src/mol-gl/shader/chunks/apply-marker-color.glsl.ts index 82d1de8f443755acda3691ede795ac90e294590e..8e9cb2270eed2fd8bf72eef4d17e34f7edd28ae5 100644 --- a/src/mol-gl/shader/chunks/apply-marker-color.glsl.ts +++ b/src/mol-gl/shader/chunks/apply-marker-color.glsl.ts @@ -3,6 +3,7 @@ float marker = floor(vMarker * 255.0 + 0.5); // rounding required to work on som if (marker > 0.1) { if (intMod(marker, 2.0) > 0.1) { gl_FragColor.rgb = mix(uHighlightColor, gl_FragColor.rgb, 0.3); + gl_FragColor.a = max(0.02, gl_FragColor.a); // for direct-volume rendering } else { gl_FragColor.rgb = mix(uSelectColor, gl_FragColor.rgb, 0.3); } diff --git a/src/mol-gl/shader/chunks/texture3d-from-1d-trilinear.glsl.ts b/src/mol-gl/shader/chunks/texture3d-from-1d-trilinear.glsl.ts new file mode 100644 index 0000000000000000000000000000000000000000..78f0c417abd680ee54ad4858d35a1b4703c1758a --- /dev/null +++ b/src/mol-gl/shader/chunks/texture3d-from-1d-trilinear.glsl.ts @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +export default ` +vec4 texture3dFrom1dTrilinear(const in sampler2D tex, const in vec3 pos, const in vec3 gridDim, const in vec2 texDim, const in float offset) { + float gdYZ = gridDim.z * gridDim.y; + float gdZ = gridDim.z; + vec3 p0 = floor(pos * gridDim); + vec3 p1 = ceil(pos * gridDim); + vec3 pd = (pos * gridDim - p0) / (p1 - p0); + vec4 s000 = readFromTexture(tex, offset + p0.z + p0.y * gdZ + p0.x * gdYZ, texDim); + vec4 s100 = readFromTexture(tex, offset + p0.z + p0.y * gdZ + p1.x * gdYZ, texDim); + vec4 s001 = readFromTexture(tex, offset + p1.z + p0.y * gdZ + p0.x * gdYZ, texDim); + vec4 s101 = readFromTexture(tex, offset + p1.z + p0.y * gdZ + p1.x * gdYZ, texDim); + vec4 s010 = readFromTexture(tex, offset + p0.z + p1.y * gdZ + p0.x * gdYZ, texDim); + vec4 s110 = readFromTexture(tex, offset + p0.z + p1.y * gdZ + p1.x * gdYZ, texDim); + vec4 s011 = readFromTexture(tex, offset + p1.z + p1.y * gdZ + p0.x * gdYZ, texDim); + vec4 s111 = readFromTexture(tex, offset + p1.z + p1.y * gdZ + p1.x * gdYZ, texDim); + vec4 s00 = mix(s000, s100, pd.x); + vec4 s01 = mix(s001, s101, pd.x); + vec4 s10 = mix(s010, s110, pd.x); + vec4 s11 = mix(s011, s111, pd.x); + vec4 s0 = mix(s00, s10, pd.y); + vec4 s1 = mix(s01, s11, pd.y); + return mix(s0, s1, pd.z); +} +`; \ No newline at end of file diff --git a/src/mol-gl/shader/direct-volume.frag.ts b/src/mol-gl/shader/direct-volume.frag.ts index 4d392f4d1b5e8b055a70648e0a26c4d49468872c..33a61fb1855d74e0370e8a5c5e1b2611a94d9890 100644 --- a/src/mol-gl/shader/direct-volume.frag.ts +++ b/src/mol-gl/shader/direct-volume.frag.ts @@ -13,6 +13,7 @@ precision highp int; #include light_frag_params #include read_from_texture +#include texture3d_from_1d_trilinear #include texture3d_from_2d_nearest #include texture3d_from_2d_linear @@ -35,6 +36,7 @@ uniform sampler2D tTransferTex; uniform float uStepFactor; uniform int uObjectId; +uniform int uVertexCount; uniform int uInstanceCount; uniform int uGroupCount; @@ -212,14 +214,18 @@ vec4 raymarch(vec3 startLoc, vec3 step) { float group = g.z + g.y * uGridDim.z + g.x * uGridDim.z * uGridDim.y; #endif - #if defined(dColorType_instance) + #if defined(dColorType_uniform) + color = uColor; + #elif defined(dColorType_instance) color = readFromTexture(tColor, instance, uColorTexDim).rgb; #elif defined(dColorType_group) color = readFromTexture(tColor, group, uColorTexDim).rgb; #elif defined(dColorType_groupInstance) color = readFromTexture(tColor, instance * float(uGroupCount) + group, uColorTexDim).rgb; - #elif defined(dColorType_uniform) - color = uColor; + #elif defined(dColorType_vertex) + color = texture3dFrom1dTrilinear(tColor, isoPos, uGridDim, uColorTexDim, 0.0).rgb; + #elif defined(dColorType_vertexInstance) + color = texture3dFrom1dTrilinear(tColor, isoPos, uGridDim, uColorTexDim, instance * float(uVertexCount)).rgb; #endif // handle flipping and negative isosurfaces @@ -304,6 +310,16 @@ vec4 raymarch(vec3 startLoc, vec3 step) { } gl_FragColor.a = material.a * uAlpha; + + #ifdef dPackedGroup + float group = decodeFloatRGB(textureGroup(floor(isoPos * uGridDim + 0.5) / uGridDim).rgb); + #else + vec3 g = floor(isoPos * uGridDim + 0.5); + float group = g.z + g.y * uGridDim.z + g.x * uGridDim.z * uGridDim.y; + #endif + + float vMarker = readFromTexture(tMarker, instance * float(uGroupCount) + group, uMarkerTexDim).a; + #include apply_marker_color #include apply_fog src = gl_FragColor; @@ -334,7 +350,7 @@ vec4 raymarch(vec3 startLoc, vec3 step) { // TODO support float texture for higher precision values??? void main () { - // TODO handle on CPU in renderloop + // TODO handle on CPU in renderloop? #if defined(dRenderVariant_pick) #if defined(dRenderMode_volume) discard; @@ -344,13 +360,13 @@ void main () { #endif #endif - vec3 cameraPos = uInvView[3].xyz / uInvView[3].w; vec3 rayDir = mix(normalize(origPos - uCameraPosition), uCameraDir, uIsOrtho);; // TODO: set the scale as uniform? float stepScale = min(uCellDim.x, min(uCellDim.y, uCellDim.z)) * uStepFactor; vec3 step = rayDir * stepScale; - gl_FragColor = raymarch(origPos, step); + float d = uNear - distance(origPos, uCameraPosition); + gl_FragColor = raymarch(origPos + (d * rayDir), step); } `; \ No newline at end of file diff --git a/src/mol-repr/structure/visual/gaussian-density-volume.ts b/src/mol-repr/structure/visual/gaussian-density-volume.ts index 3cdfdfb0602bc1b06098f40ac0216c0d2ebd9d21..06bd36e35c961360c77c20aac621f1952b3299e1 100644 --- a/src/mol-repr/structure/visual/gaussian-density-volume.ts +++ b/src/mol-repr/structure/visual/gaussian-density-volume.ts @@ -56,6 +56,7 @@ export function GaussianDensityVolumeVisual(materialId: number): ComplexVisual<G if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true; if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true; if (newProps.ignoreHydrogens !== currentProps.ignoreHydrogens) state.createGeometry = true; + if (newProps.traceOnly !== currentProps.traceOnly) state.createGeometry = true; if (newProps.includeParent !== currentProps.includeParent) state.createGeometry = true; } }, materialId); diff --git a/src/mol-repr/volume/direct-volume.ts b/src/mol-repr/volume/direct-volume.ts index cd4626c705f6f19405e6bb3449bc64549893849c..23891193d89835c84c68eaa1fc5f1ae7f6f8026a 100644 --- a/src/mol-repr/volume/direct-volume.ts +++ b/src/mol-repr/volume/direct-volume.ts @@ -213,7 +213,9 @@ export async function createDirectVolume(ctx: VisualContext, volume: Volume, the } function getLoci(volume: Volume, props: PD.Values<DirectVolumeParams>) { - return Volume.Loci(volume); + return props.renderMode.name === 'isosurface' + ? Volume.Isosurface.Loci(volume, props.renderMode.params.isoValue) + : Volume.Loci(volume); } export function getDirectVolumeLoci(pickingId: PickingId, volume: Volume, props: DirectVolumeProps, id: number) { @@ -225,9 +227,9 @@ export function getDirectVolumeLoci(pickingId: PickingId, volume: Volume, props: } export function eachDirectVolume(loci: Loci, volume: Volume, props: DirectVolumeProps, apply: (interval: Interval) => boolean) { - return props.renderMode.name === 'isosurface' - ? eachVolumeLoci(loci, volume, props.renderMode.params.isoValue, apply) - : false; + const isoValue = props.renderMode.name === 'isosurface' + ? props.renderMode.params.isoValue : undefined; + return eachVolumeLoci(loci, volume, isoValue, apply); } // diff --git a/src/mol-repr/volume/slice.ts b/src/mol-repr/volume/slice.ts index 21309f24650691dde8f275e64ad48cf69c2e4a10..49688fe58aa025588c7b8f707dce4be64a594fb8 100644 --- a/src/mol-repr/volume/slice.ts +++ b/src/mol-repr/volume/slice.ts @@ -16,13 +16,13 @@ import { RepresentationContext, RepresentationParamsGetter } from '../representa import { VisualContext } from '../visual'; import { PickingId } from '../../mol-geo/geometry/picking'; import { EmptyLoci, Loci } from '../../mol-model/loci'; -import { Interval, OrderedSet, SortedArray } from '../../mol-data/int'; +import { Interval, SortedArray } from '../../mol-data/int'; import { transformPositionArray } from '../../mol-geo/util'; -import { equalEps } from '../../mol-math/linear-algebra/3d/common'; import { RenderableState } from '../../mol-gl/renderable'; import { Color } from '../../mol-util/color'; import { ColorTheme } from '../../mol-theme/color'; import { encodeFloatRGBtoArray } from '../../mol-util/float-packing'; +import { eachVolumeLoci } from './util'; export async function createImage(ctx: VisualContext, volume: Volume, theme: Theme, props: PD.Values<SliceParams>, image?: Image) { const { dimension: { name: dim }, isoValue } = props; @@ -156,32 +156,7 @@ function getSliceLoci(pickingId: PickingId, volume: Volume, props: PD.Values<Sli } function eachSlice(loci: Loci, volume: Volume, props: PD.Values<SliceParams>, apply: (interval: Interval) => boolean) { - let changed = false; - if (Volume.isLoci(loci)) { - if (!Volume.areEquivalent(loci.volume, volume)) return false; - if (apply(Interval.ofLength(volume.grid.cells.data.length))) changed = true; - } else if (Volume.Isosurface.isLoci(loci)) { - if (!Volume.areEquivalent(loci.volume, volume)) return false; - // TODO find a cheaper way? - const { stats, cells: { data } } = volume.grid; - const eps = stats.sigma; - const v = Volume.IsoValue.toAbsolute(loci.isoValue, stats).absoluteValue; - for (let i = 0, il = data.length; i < il; ++i) { - if (equalEps(v, data[i], eps)) { - if (apply(Interval.ofSingleton(i))) changed = true; - } - } - } else if (Volume.Cell.isLoci(loci)) { - if (!Volume.areEquivalent(loci.volume, volume)) return false; - if (Interval.is(loci.indices)) { - if (apply(loci.indices)) changed = true; - } else { - OrderedSet.forEach(loci.indices, v => { - if (apply(Interval.ofSingleton(v))) changed = true; - }); - } - } - return changed; + return eachVolumeLoci(loci, volume, undefined, apply); } // diff --git a/src/mol-repr/volume/util.ts b/src/mol-repr/volume/util.ts index 91fade6a776d8c3caed46d9f789507a09c60b7b9..0c4957746e366ad187c4c0b83e922beb6d94e559 100644 --- a/src/mol-repr/volume/util.ts +++ b/src/mol-repr/volume/util.ts @@ -7,16 +7,29 @@ import { Volume } from '../../mol-model/volume'; import { Loci } from '../../mol-model/loci'; import { Interval, OrderedSet } from '../../mol-data/int'; +import { equalEps } from '../../mol-math/linear-algebra/3d/common'; -export function eachVolumeLoci(loci: Loci, volume: Volume, isoValue: Volume.IsoValue, apply: (interval: Interval) => boolean) { +export function eachVolumeLoci(loci: Loci, volume: Volume, isoValue: Volume.IsoValue | undefined, apply: (interval: Interval) => boolean) { let changed = false; if (Volume.isLoci(loci)) { if (!Volume.areEquivalent(loci.volume, volume)) return false; if (apply(Interval.ofLength(volume.grid.cells.data.length))) changed = true; } else if (Volume.Isosurface.isLoci(loci)) { if (!Volume.areEquivalent(loci.volume, volume)) return false; - if (!Volume.IsoValue.areSame(loci.isoValue, isoValue, volume.grid.stats)) return false; - if (apply(Interval.ofLength(volume.grid.cells.data.length))) changed = true; + if (isoValue) { + if (Volume.IsoValue.areSame(loci.isoValue, isoValue, volume.grid.stats)) return false; + if (apply(Interval.ofLength(volume.grid.cells.data.length))) changed = true; + } else { + // TODO find a cheaper way? + const { stats, cells: { data } } = volume.grid; + const eps = stats.sigma; + const v = Volume.IsoValue.toAbsolute(loci.isoValue, stats).absoluteValue; + for (let i = 0, il = data.length; i < il; ++i) { + if (equalEps(v, data[i], eps)) { + if (apply(Interval.ofSingleton(i))) changed = true; + } + } + } } else if (Volume.Cell.isLoci(loci)) { if (!Volume.areEquivalent(loci.volume, volume)) return false; if (Interval.is(loci.indices)) {