Bladeren bron

wip, per-object transparency

Alexander Rose 6 jaren geleden
bovenliggende
commit
652bf7b437

+ 3 - 0
src/mol-geo/geometry/direct-volume/direct-volume.ts

@@ -25,6 +25,7 @@ import { ColorListOptions, ColorListName } from 'mol-util/color/scale';
 import { Color } from 'mol-util/color';
 import { BaseGeometry } from '../base';
 import { createEmptyOverpaint } from '../overpaint-data';
+import { createEmptyTransparency } from '../transparency-data';
 
 const VolumeBox = Box()
 const RenderModeOptions = [['isosurface', 'Isosurface'], ['volume', 'Volume']] as [string, string][]
@@ -103,6 +104,7 @@ export namespace DirectVolume {
         const color = createColors(locationIt, theme.color)
         const marker = createMarkers(instanceCount * groupCount)
         const overpaint = createEmptyOverpaint()
+        const transparency = createEmptyTransparency()
 
         const counts = { drawCount: VolumeBox.indices.length, groupCount, instanceCount }
 
@@ -117,6 +119,7 @@ export namespace DirectVolume {
             ...color,
             ...marker,
             ...overpaint,
+            ...transparency,
             ...transform,
             ...BaseGeometry.createValues(props, counts),
 

+ 3 - 0
src/mol-geo/geometry/lines/lines.ts

@@ -23,6 +23,7 @@ import { Theme } from 'mol-theme/theme';
 import { Color } from 'mol-util/color';
 import { BaseGeometry } from '../base';
 import { createEmptyOverpaint } from '../overpaint-data';
+import { createEmptyTransparency } from '../transparency-data';
 
 /** Wide line */
 export interface Lines {
@@ -119,6 +120,7 @@ export namespace Lines {
         const size = createSizes(locationIt, theme.size)
         const marker = createMarkers(instanceCount * groupCount)
         const overpaint = createEmptyOverpaint()
+        const transparency = createEmptyTransparency()
 
         const counts = { drawCount: lines.lineCount * 2 * 3, groupCount, instanceCount }
 
@@ -137,6 +139,7 @@ export namespace Lines {
             ...size,
             ...marker,
             ...overpaint,
+            ...transparency,
             ...transform,
 
             ...BaseGeometry.createValues(props, counts),

+ 3 - 0
src/mol-geo/geometry/mesh/mesh.ts

@@ -22,6 +22,7 @@ import { MeshValues } from 'mol-gl/renderable/mesh';
 import { Color } from 'mol-util/color';
 import { BaseGeometry } from '../base';
 import { createEmptyOverpaint } from '../overpaint-data';
+import { createEmptyTransparency } from '../transparency-data';
 
 export interface Mesh {
     readonly kind: 'mesh',
@@ -383,6 +384,7 @@ export namespace Mesh {
         const color = createColors(locationIt, theme.color)
         const marker = createMarkers(instanceCount * groupCount)
         const overpaint = createEmptyOverpaint()
+        const transparency = createEmptyTransparency()
 
         const counts = { drawCount: mesh.triangleCount * 3, groupCount, instanceCount }
 
@@ -401,6 +403,7 @@ export namespace Mesh {
             ...color,
             ...marker,
             ...overpaint,
+            ...transparency,
             ...transform,
 
             ...BaseGeometry.createValues(props, counts),

+ 3 - 0
src/mol-geo/geometry/points/points.ts

@@ -22,6 +22,7 @@ import { RenderableState } from 'mol-gl/renderable';
 import { Color } from 'mol-util/color';
 import { BaseGeometry } from '../base';
 import { createEmptyOverpaint } from '../overpaint-data';
+import { createEmptyTransparency } from '../transparency-data';
 
 /** Point cloud */
 export interface Points {
@@ -84,6 +85,7 @@ export namespace Points {
         const size = createSizes(locationIt, theme.size)
         const marker = createMarkers(instanceCount * groupCount)
         const overpaint = createEmptyOverpaint()
+        const transparency = createEmptyTransparency()
 
         const counts = { drawCount: points.pointCount, groupCount, instanceCount }
 
@@ -101,6 +103,7 @@ export namespace Points {
             ...size,
             ...marker,
             ...overpaint,
+            ...transparency,
             ...transform,
 
             ...BaseGeometry.createValues(props, counts),

+ 3 - 0
src/mol-geo/geometry/spheres/spheres.ts

@@ -19,6 +19,7 @@ import { createSizes, getMaxSize } from '../size-data';
 import { Color } from 'mol-util/color';
 import { BaseGeometry } from '../base';
 import { createEmptyOverpaint } from '../overpaint-data';
+import { createEmptyTransparency } from '../transparency-data';
 
 /** Spheres */
 export interface Spheres {
@@ -81,6 +82,7 @@ export namespace Spheres {
         const size = createSizes(locationIt, theme.size)
         const marker = createMarkers(instanceCount * groupCount)
         const overpaint = createEmptyOverpaint()
+        const transparency = createEmptyTransparency()
 
         const counts = { drawCount: spheres.sphereCount * 2 * 3, groupCount, instanceCount }
 
@@ -101,6 +103,7 @@ export namespace Spheres {
             ...size,
             ...marker,
             ...overpaint,
+            ...transparency,
             ...transform,
 
             padding: ValueCell.create(padding),

+ 3 - 0
src/mol-geo/geometry/text/text.ts

@@ -25,6 +25,7 @@ import { clamp } from 'mol-math/interpolate';
 import { createRenderObject as _createRenderObject } from 'mol-gl/render-object';
 import { BaseGeometry } from '../base';
 import { createEmptyOverpaint } from '../overpaint-data';
+import { createEmptyTransparency } from '../transparency-data';
 
 type TextAttachment = (
     'bottom-left' | 'bottom-center' | 'bottom-right' |
@@ -124,6 +125,7 @@ export namespace Text {
         const size = createSizes(locationIt, theme.size)
         const marker = createMarkers(instanceCount * groupCount)
         const overpaint = createEmptyOverpaint()
+        const transparency = createEmptyTransparency()
 
         const counts = { drawCount: text.charCount * 2 * 3, groupCount, instanceCount }
 
@@ -145,6 +147,7 @@ export namespace Text {
             ...size,
             ...marker,
             ...overpaint,
+            ...transparency,
             ...transform,
 
             aTexCoord: text.tcoordBuffer,

+ 57 - 0
src/mol-geo/geometry/transparency-data.ts

@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2019 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 { TextureImage, createTextureImage } from 'mol-gl/renderable/util';
+
+export type TransparencyData = {
+    tTransparency: ValueCell<TextureImage<Uint8Array>>
+    uTransparencyTexDim: ValueCell<Vec2>
+    dTransparency: ValueCell<boolean>,
+}
+
+export function applyTransparencyValue(array: Uint8Array, start: number, end: number, value: number) {
+    for (let i = start; i < end; ++i) {
+        array[i] = value * 255
+    }
+    return true
+}
+
+export function clearTransparency(array: Uint8Array, start: number, end: number) {
+    array.fill(0, start, end)
+}
+
+export function createTransparency(count: number, transparencyData?: TransparencyData): TransparencyData {
+    const transparency = createTextureImage(Math.max(1, count), 1, transparencyData && transparencyData.tTransparency.ref.value.array)
+    if (transparencyData) {
+        ValueCell.update(transparencyData.tTransparency, transparency)
+        ValueCell.update(transparencyData.uTransparencyTexDim, Vec2.create(transparency.width, transparency.height))
+        ValueCell.update(transparencyData.dTransparency, count > 0)
+        return transparencyData
+    } else {
+        return {
+            tTransparency: ValueCell.create(transparency),
+            uTransparencyTexDim: ValueCell.create(Vec2.create(transparency.width, transparency.height)),
+            dTransparency: ValueCell.create(count > 0),
+        }
+    }
+}
+
+const emptyTransparencyTexture = { array: new Uint8Array(1), width: 1, height: 1 }
+export function createEmptyTransparency(transparencyData?: TransparencyData): TransparencyData {
+    if (transparencyData) {
+        ValueCell.update(transparencyData.tTransparency, emptyTransparencyTexture)
+        ValueCell.update(transparencyData.uTransparencyTexDim, Vec2.create(1, 1))
+        return transparencyData
+    } else {
+        return {
+            tTransparency: ValueCell.create(emptyTransparencyTexture),
+            uTransparencyTexDim: ValueCell.create(Vec2.create(1, 1)),
+            dTransparency: ValueCell.create(false),
+        }
+    }
+}

+ 3 - 0
src/mol-gl/_spec/renderer.spec.ts

@@ -23,6 +23,7 @@ import { fillSerial } from 'mol-util/array';
 import { Color } from 'mol-util/color';
 import { Sphere3D } from 'mol-math/geometry';
 import { createEmptyOverpaint } from 'mol-geo/geometry/overpaint-data';
+import { createEmptyTransparency } from 'mol-geo/geometry/transparency-data';
 
 // function writeImage(gl: WebGLRenderingContext, width: number, height: number) {
 //     const pixels = new Uint8Array(width * height * 4)
@@ -54,6 +55,7 @@ function createPoints() {
     const size = createValueSize(1)
     const marker = createEmptyMarkers()
     const overpaint = createEmptyOverpaint()
+    const transparency = createEmptyTransparency()
 
     const aTransform = ValueCell.create(new Float32Array(16))
     const m4 = Mat4.identity()
@@ -73,6 +75,7 @@ function createPoints() {
         ...marker,
         ...size,
         ...overpaint,
+        ...transparency,
 
         uAlpha: ValueCell.create(1.0),
         uHighlightColor: ValueCell.create(Vec3.create(1.0, 0.4, 0.6)),

+ 4 - 0
src/mol-gl/renderable/direct-volume.ts

@@ -24,6 +24,10 @@ export const DirectVolumeSchema = {
     tOverpaint: TextureSpec('image-uint8', 'rgba', 'ubyte', 'nearest'),
     dOverpaint: DefineSpec('boolean'),
 
+    uTransparencyTexDim: UniformSpec('v2'),
+    tTransparency: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
+    dTransparency: DefineSpec('boolean'),
+
     uInstanceCount: UniformSpec('i'),
     uGroupCount: UniformSpec('i'),
 

+ 9 - 0
src/mol-gl/renderable/schema.ts

@@ -207,10 +207,19 @@ export const OverpaintSchema = {
 export type OverpaintSchema = typeof OverpaintSchema
 export type OverpaintValues = Values<OverpaintSchema>
 
+export const TransparencySchema = {
+    uTransparencyTexDim: UniformSpec('v2'),
+    tTransparency: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
+    dTransparency: DefineSpec('boolean'),
+}
+export type TransparencySchema = typeof TransparencySchema
+export type TransparencyValues = Values<TransparencySchema>
+
 export const BaseSchema = {
     ...ColorSchema,
     ...MarkerSchema,
     ...OverpaintSchema,
+    ...TransparencySchema,
 
     aInstance: AttributeSpec('float32', 1, 1),
     aGroup: AttributeSpec('float32', 1, 0),

+ 1 - 1
src/mol-gl/shader/chunks/apply-marker-color.glsl

@@ -1,5 +1,5 @@
 // only mark elements with an alpha above the picking threshold
-if (uAlpha >= uPickingAlphaThreshold) {
+if (gl_FragColor.a >= uPickingAlphaThreshold) {
     float marker = floor(vMarker * 255.0 + 0.5); // rounding required to work on some cards on win
     if (marker > 0.1) {
         if (intMod(marker, 2.0) > 0.1) {

+ 4 - 0
src/mol-gl/shader/chunks/assign-color-varying.glsl

@@ -16,4 +16,8 @@
 
 #ifdef dOverpaint
     vOverpaint = readFromTexture(tOverpaint, aInstance * float(uGroupCount) + aGroup, uOverpaintTexDim);
+#endif
+
+#ifdef dTransparency
+    vTransparency = readFromTexture(tTransparency, aInstance * float(uGroupCount) + aGroup, uTransparencyTexDim);
 #endif

+ 5 - 0
src/mol-gl/shader/chunks/assign-material-color.glsl

@@ -9,4 +9,9 @@
 // mix material with overpaint
 #if defined(dOverpaint) && (defined(dColorType_uniform) || defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_group) || defined(dColorType_groupInstance))
     material.rgb = mix(material.rgb, vOverpaint.rgb, vOverpaint.a);
+#endif
+
+// apply transparency
+#if defined(dTransparency) && (defined(dColorType_uniform) || defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_group) || defined(dColorType_groupInstance))
+    material.a *= 1 - vTransparency;
 #endif

+ 4 - 0
src/mol-gl/shader/chunks/color-frag-params.glsl

@@ -12,4 +12,8 @@
 
 #ifdef dOverpaint
     varying vec4 vOverpaint;
+#endif
+
+#ifdef dTransparency
+    varying float vTransparency;
 #endif

+ 6 - 0
src/mol-gl/shader/chunks/color-vert-params.glsl

@@ -20,4 +20,10 @@
     varying vec4 vOverpaint;
     uniform vec2 uOverpaintTexDim;
     uniform sampler2D tOverpaint;
+#endif
+
+#ifdef dTransparency
+    varying float vTransparency;
+    uniform vec2 uTransparencyTexDim;
+    uniform sampler2D tTransparency;
 #endif

+ 8 - 1
src/mol-repr/representation.ts

@@ -20,6 +20,7 @@ import { Mat4 } from 'mol-math/linear-algebra';
 import { BaseGeometry } from 'mol-geo/geometry/base';
 import { Visual } from './visual';
 import { Overpaint } from 'mol-theme/overpaint';
+import { Transparency } from 'mol-theme/transparency';
 
 // export interface RepresentationProps {
 //     visuals?: string[]
@@ -146,19 +147,22 @@ namespace Representation {
         pickable: boolean
         /** Overpaint applied to the representation's renderobjects */
         overpaint: Overpaint
+        /** Per group transparency applied to the representation's renderobjects */
+        transparency: Transparency
         /** Controls if the representation's renderobjects are synced automatically with GPU or not */
         syncManually: boolean
         /** A transformation applied to the representation's renderobjects */
         transform: Mat4
     }
     export function createState(): State {
-        return { visible: false, alphaFactor: 0, pickable: false, syncManually: false, transform: Mat4.identity(), overpaint: Overpaint.Empty }
+        return { visible: false, alphaFactor: 0, pickable: false, syncManually: false, transform: Mat4.identity(), overpaint: Overpaint.Empty, transparency: Transparency.Empty }
     }
     export function updateState(state: State, update: Partial<State>) {
         if (update.visible !== undefined) state.visible = update.visible
         if (update.alphaFactor !== undefined) state.alphaFactor = update.alphaFactor
         if (update.pickable !== undefined) state.pickable = update.pickable
         if (update.overpaint !== undefined) state.overpaint = update.overpaint
+        if (update.transparency !== undefined) state.transparency = update.transparency
         if (update.syncManually !== undefined) state.syncManually = update.syncManually
         if (update.transform !== undefined) Mat4.copy(state.transform, update.transform)
     }
@@ -322,6 +326,9 @@ namespace Representation {
                 if (state.overpaint !== undefined) {
                     // TODO
                 }
+                if (state.transparency !== undefined) {
+                    // TODO
+                }
                 if (state.transform !== undefined) Visual.setTransform(renderObject, state.transform)
 
                 Representation.updateState(currentState, state)

+ 3 - 0
src/mol-repr/shape/representation.ts

@@ -186,6 +186,9 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa
                 if (state.overpaint !== undefined) {
                     Visual.setOverpaint(_renderObject, state.overpaint, lociApply, true)
                 }
+                if (state.transparency !== undefined) {
+                    Visual.setTransparency(_renderObject, state.transparency, lociApply, true)
+                }
                 if (state.transform !== undefined) Visual.setTransform(_renderObject, state.transform)
             }
 

+ 4 - 0
src/mol-repr/structure/complex-visual.ts

@@ -29,6 +29,7 @@ import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume';
 import { Mat4 } from 'mol-math/linear-algebra';
 import { createIdentityTransform } from 'mol-geo/geometry/transform-data';
 import { Overpaint } from 'mol-theme/overpaint';
+import { Transparency } from 'mol-theme/transparency';
 
 export interface  ComplexVisual<P extends StructureParams> extends Visual<Structure, P> { }
 
@@ -199,6 +200,9 @@ export function ComplexVisual<G extends Geometry, P extends ComplexParams & Geom
         setOverpaint(overpaint: Overpaint, clear = false) {
             return Visual.setOverpaint(renderObject, overpaint, lociApply, true)
         },
+        setTransparency(transparency: Transparency) {
+            return Visual.setTransparency(renderObject, transparency, lociApply, true)
+        },
         destroy() {
             // TODO
             renderObject = undefined

+ 4 - 0
src/mol-repr/structure/units-visual.ts

@@ -32,6 +32,7 @@ import { Mat4 } from 'mol-math/linear-algebra';
 import { Spheres } from 'mol-geo/geometry/spheres/spheres';
 import { createUnitsTransform, includesUnitKind } from './visual/util/common';
 import { Overpaint } from 'mol-theme/overpaint';
+import { Transparency } from 'mol-theme/transparency';
 
 export type StructureGroup = { structure: Structure, group: Unit.SymmetryGroup }
 
@@ -245,6 +246,9 @@ export function UnitsVisual<G extends Geometry, P extends UnitsParams & Geometry
         setOverpaint(overpaint: Overpaint) {
             return Visual.setOverpaint(renderObject, overpaint, lociApply, true)
         },
+        setTransparency(transparency: Transparency) {
+            return Visual.setTransparency(renderObject, transparency, lociApply, true)
+        },
         destroy() {
             // TODO
             renderObject = undefined

+ 27 - 0
src/mol-repr/visual.ts

@@ -19,6 +19,8 @@ import { ValueCell } from 'mol-util';
 import { Overpaint } from 'mol-theme/overpaint';
 import { createOverpaint, clearOverpaint, applyOverpaintColor } from 'mol-geo/geometry/overpaint-data';
 import { Interval } from 'mol-data/int';
+import { Transparency } from 'mol-theme/transparency';
+import { createTransparency, clearTransparency, applyTransparencyValue } from 'mol-geo/geometry/transparency-data';
 
 export interface VisualContext {
     readonly runtime: RuntimeContext
@@ -39,6 +41,7 @@ interface Visual<D, P extends PD.Params> {
     setPickable: (pickable: boolean) => void
     setTransform: (matrix?: Mat4, instanceMatrices?: Float32Array | null) => void
     setOverpaint: (overpaint: Overpaint) => void
+    setTransparency: (transparency: Transparency) => void
     destroy: () => void
 }
 namespace Visual {
@@ -96,6 +99,30 @@ namespace Visual {
         ValueCell.update(tOverpaint, tOverpaint.ref.value)
     }
 
+    export function setTransparency(renderObject: GraphicsRenderObject | undefined, transparency: Transparency, lociApply: LociApply, clear: boolean) {
+        if (!renderObject) return
+
+        const { tTransparency, uGroupCount, instanceCount } = renderObject.values
+        const count = uGroupCount.ref.value * instanceCount.ref.value
+
+        // ensure texture has right size
+        createTransparency(transparency.layers.length ? count : 0, renderObject.values)
+
+        // clear if requested
+        if (clear) clearTransparency(tTransparency.ref.value.array, 0, count)
+
+        for (let i = 0, il = transparency.layers.length; i < il; ++i) {
+            const { loci, value } = transparency.layers[i]
+            const apply = (interval: Interval) => {
+                const start = Interval.start(interval)
+                const end = Interval.end(interval)
+                return applyTransparencyValue(tTransparency.ref.value.array, start, end, value)
+            }
+            lociApply(loci, apply)
+        }
+        ValueCell.update(tTransparency, tTransparency.ref.value)
+    }
+
     export function setTransform(renderObject: GraphicsRenderObject | undefined, transform?: Mat4, instanceTransforms?: Float32Array | null) {
         if (!renderObject || (!transform && !instanceTransforms)) return
 

+ 4 - 0
src/mol-repr/volume/representation.ts

@@ -27,6 +27,7 @@ import { ColorTheme } from 'mol-theme/color';
 import { createColors } from 'mol-geo/geometry/color-data';
 import { createSizes } from 'mol-geo/geometry/size-data';
 import { Overpaint } from 'mol-theme/overpaint';
+import { Transparency } from 'mol-theme/transparency';
 
 export interface VolumeVisual<P extends VolumeParams> extends Visual<VolumeData, P> { }
 
@@ -186,6 +187,9 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet
         setOverpaint(overpaint: Overpaint) {
             return Visual.setOverpaint(renderObject, overpaint, lociApply, true)
         },
+        setTransparency(transparency: Transparency) {
+            return Visual.setTransparency(renderObject, transparency, lociApply, true)
+        },
         destroy() {
             // TODO
             renderObject = undefined

+ 26 - 0
src/mol-theme/transparency.ts

@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Loci } from 'mol-model/loci';
+
+export { Transparency }
+
+type Transparency = { layers: ReadonlyArray<Transparency.Layer> }
+
+namespace Transparency {
+    export type Layer = { readonly loci: Loci, readonly value: number }
+    export const Empty: Transparency = { layers: [] }
+
+    export function areEqual(tA: Transparency, tB: Transparency) {
+        if (tA.layers.length === 0 && tB.layers.length === 0) return true
+        if (tA.layers.length !== tB.layers.length) return false
+        for (let i = 0, il = tA.layers.length; i < il; ++i) {
+            if (tA.layers[i].value !== tB.layers[i].value) return false
+            if (!Loci.areEqual(tA.layers[i].loci, tB.layers[i].loci)) return false
+        }
+        return true
+    }
+}