From 3d050a49f682681e3400bcc377cd501482125213 Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Fri, 14 Sep 2018 18:27:55 -0700 Subject: [PATCH] wip, point rendering --- .../component/structure-representation.tsx | 114 +++++++++++------- src/mol-geo/geometry/point/point.ts | 6 + .../representation/structure/units-visual.ts | 7 +- src/mol-gl/_spec/renderer.spec.ts | 2 + src/mol-gl/renderable.ts | 2 + src/mol-gl/renderable/mesh.ts | 4 +- src/mol-gl/renderable/point.ts | 14 ++- src/mol-gl/scene.ts | 4 +- src/mol-gl/shader/point.frag | 24 +++- 9 files changed, 120 insertions(+), 57 deletions(-) diff --git a/src/apps/canvas/component/structure-representation.tsx b/src/apps/canvas/component/structure-representation.tsx index a22db7be5..13cc8cac4 100644 --- a/src/apps/canvas/component/structure-representation.tsx +++ b/src/apps/canvas/component/structure-representation.tsx @@ -11,6 +11,7 @@ import { ColorThemeProps, ColorThemeName, ColorThemeNames, ColorTheme } from 'mo import { Color } from 'mol-util/color'; import { Progress } from 'mol-task'; import { VisualQuality, VisualQualityNames } from 'mol-geo/geometry/geometry'; +import { SizeThemeProps } from 'mol-view/theme/size'; export interface StructureRepresentationComponentProps { viewer: Viewer @@ -23,43 +24,43 @@ export interface StructureRepresentationComponentState { alpha: number quality: VisualQuality colorTheme: ColorThemeProps + sizeTheme: SizeThemeProps + depthMask: boolean flatShaded?: boolean resolutionFactor?: number radiusOffset?: number smoothness?: number + pointSizeAttenuation?: boolean + pointFilledCircle?: boolean + pointEdgeBleach?: number } export class StructureRepresentationComponent extends React.Component<StructureRepresentationComponentProps, StructureRepresentationComponentState> { - state = { - label: this.props.representation.label, - visible: this.props.representation.props.visible, - alpha: this.props.representation.props.alpha, - quality: this.props.representation.props.quality, - colorTheme: this.props.representation.props.colorTheme, + state = this.stateFromRepresentation(this.props.representation) - flatShaded: (this.props.representation.props as any).flatShaded, - resolutionFactor: (this.props.representation.props as any).resolutionFactor, - radiusOffset: (this.props.representation.props as any).radiusOffset, - smoothness: (this.props.representation.props as any).smoothness, - } - - componentWillMount() { - const repr = this.props.representation - - this.setState({ - ...this.state, + private stateFromRepresentation(repr: StructureRepresentation<StructureProps>) { + return { label: repr.label, visible: repr.props.visible, alpha: repr.props.alpha, quality: repr.props.quality, colorTheme: repr.props.colorTheme, + sizeTheme: repr.props.sizeTheme, + depthMask: repr.props.depthMask, flatShaded: (repr.props as any).flatShaded, resolutionFactor: (repr.props as any).resolutionFactor, radiusOffset: (repr.props as any).radiusOffset, smoothness: (repr.props as any).smoothness, - }) + pointSizeAttenuation: (repr.props as any).pointSizeAttenuation, + pointFilledCircle: (repr.props as any).pointFilledCircle, + pointEdgeBleach: (repr.props as any).pointEdgeBleach, + } + } + + componentWillMount() { + this.setState(this.stateFromRepresentation(this.props.representation)) } async update(state: Partial<StructureRepresentationComponentState>) { @@ -70,11 +71,16 @@ export class StructureRepresentationComponent extends React.Component<StructureR if (state.quality !== undefined) props.quality = state.quality if (state.alpha !== undefined) props.alpha = state.alpha if (state.colorTheme !== undefined) props.colorTheme = state.colorTheme + if (state.sizeTheme !== undefined) props.sizeTheme = state.sizeTheme + if (state.depthMask !== undefined) props.depthMask = state.depthMask if (state.flatShaded !== undefined) (props as any).flatShaded = state.flatShaded if (state.resolutionFactor !== undefined) (props as any).resolutionFactor = state.resolutionFactor if (state.radiusOffset !== undefined) (props as any).radiusOffset = state.radiusOffset if (state.smoothness !== undefined) (props as any).smoothness = state.smoothness + if (state.pointSizeAttenuation !== undefined) (props as any).pointSizeAttenuation = state.pointSizeAttenuation + if (state.pointFilledCircle !== undefined) (props as any).pointFilledCircle = state.pointFilledCircle + if (state.pointEdgeBleach !== undefined) (props as any).pointEdgeBleach = state.pointEdgeBleach await repr.createOrUpdate(props).run( progress => console.log(Progress.format(progress)) @@ -83,39 +89,13 @@ export class StructureRepresentationComponent extends React.Component<StructureR this.props.viewer.draw(true) console.log(this.props.viewer.stats) - console.log( - 'drawCount', - repr.renderObjects[0].values.drawCount.ref.version, - repr.renderObjects[0].values.drawCount.ref.value, - 'dColorType', - repr.renderObjects[0].values.dColorType.ref.version, - repr.renderObjects[0].values.dColorType.ref.value - ) - - const newState = { - ...this.state, - visible: repr.props.visible, - quality: repr.props.quality, - alpha: repr.props.alpha, - colorTheme: repr.props.colorTheme, - - flatShaded: (repr.props as any).flatShaded, - resolutionFactor: (repr.props as any).resolutionFactor, - radiusOffset: (repr.props as any).radiusOffset, - isoValue: (repr.props as any).isoValue, - } - this.setState(newState) + this.setState(this.stateFromRepresentation(repr)) } render() { - const { label, visible, quality, alpha, colorTheme } = this.state - + const { label, visible, quality, alpha, colorTheme, depthMask } = this.state const ct = ColorTheme(colorTheme) - if (ct.legend && ct.legend.kind === 'scale-legend') { - // console.log(`linear-gradient(to right, ${ct.legend.colors.map(c => Color.toStyle(c)).join(', ')})`) - } - return <div> <div> <h4>{label}</h4> @@ -127,6 +107,12 @@ export class StructureRepresentationComponent extends React.Component<StructureR {visible ? 'Hide' : 'Show'} </button> </div> + <div> + <span>Depth Mask </span> + <button onClick={(e) => this.update({ depthMask: !depthMask }) }> + {depthMask ? 'Deactivate' : 'Activate'} + </button> + </div> { this.state.flatShaded !== undefined ? <div> <span>Flat Shaded </span> <button onClick={(e) => this.update({ flatShaded: !this.state.flatShaded }) }> @@ -183,6 +169,42 @@ export class StructureRepresentationComponent extends React.Component<StructureR > </input> </div> : '' } + { this.state.pointSizeAttenuation !== undefined ? <div> + <span>Size Attenuation </span> + <button onClick={(e) => this.update({ pointSizeAttenuation: !this.state.pointSizeAttenuation }) }> + {this.state.pointSizeAttenuation ? 'Deactivate' : 'Activate'} + </button> + </div> : '' } + { this.state.pointFilledCircle !== undefined ? <div> + <span>Filled Circle </span> + <button onClick={(e) => this.update({ pointFilledCircle: !this.state.pointFilledCircle }) }> + {this.state.pointFilledCircle ? 'Deactivate' : 'Activate'} + </button> + </div> : '' } + { this.state.pointEdgeBleach !== undefined ? <div> + <span>Edge Bleach </span> + <input type='range' + defaultValue={this.state.pointEdgeBleach.toString()} + min='0' + max='1' + step='0.05' + onInput={(e) => this.update({ pointEdgeBleach: parseFloat(e.currentTarget.value) })} + > + </input> + </div> : '' } + { this.state.sizeTheme !== undefined && this.state.sizeTheme.name === 'uniform' ? <div> + <span>Uniform Size </span> + <input type='range' + defaultValue={this.state.sizeTheme.value!.toString()} + min='0' + max='10' + step='0.1' + onInput={(e) => this.update({ + sizeTheme: { name: 'uniform', value: parseFloat(e.currentTarget.value) } + })} + > + </input> + </div> : '' } <div> <span>Color Theme </span> <select value={colorTheme.name} onChange={(e) => this.update({ colorTheme: { name: e.target.value as ColorThemeName } }) }> diff --git a/src/mol-geo/geometry/point/point.ts b/src/mol-geo/geometry/point/point.ts index a08da3658..2ab8080d2 100644 --- a/src/mol-geo/geometry/point/point.ts +++ b/src/mol-geo/geometry/point/point.ts @@ -55,6 +55,8 @@ export namespace Point { export const DefaultProps = { ...Geometry.DefaultProps, pointSizeAttenuation: false, + pointFilledCircle: false, + pointEdgeBleach: 0.2, sizeTheme: { name: 'uniform', value: 1 } as SizeThemeProps, } export type Props = typeof DefaultProps @@ -77,11 +79,15 @@ export namespace Point { ...Geometry.createValues(props, counts), dPointSizeAttenuation: ValueCell.create(props.pointSizeAttenuation), + dPointFilledCircle: ValueCell.create(props.pointFilledCircle), + uPointEdgeBleach: ValueCell.create(props.pointEdgeBleach), } } export function updateValues(values: PointValues, props: Props) { Geometry.updateValues(values, props) ValueCell.updateIfChanged(values.dPointSizeAttenuation, props.pointSizeAttenuation) + ValueCell.updateIfChanged(values.dPointFilledCircle, props.pointFilledCircle) + ValueCell.updateIfChanged(values.uPointEdgeBleach, props.pointEdgeBleach) } } \ No newline at end of file diff --git a/src/mol-geo/representation/structure/units-visual.ts b/src/mol-geo/representation/structure/units-visual.ts index 9bf2dc6ba..069085d36 100644 --- a/src/mol-geo/representation/structure/units-visual.ts +++ b/src/mol-geo/representation/structure/units-visual.ts @@ -20,6 +20,7 @@ import { Interval } from 'mol-data/int'; import { Point } from '../../geometry/point/point'; import { updateRenderableState } from '../../geometry/geometry'; import { createColors } from '../../geometry/color-data'; +import { createSizes } from '../../geometry/size-data'; export type StructureGroup = { structure: Structure, group: Unit.SymmetryGroup } @@ -245,7 +246,7 @@ export function UnitsPointVisual<P extends UnitsPointProps>(builder: UnitsPointV if (currentGroup.units.length !== locationIt.instanceCount) updateState.updateTransform = true - if (!deepEqual(newProps.sizeTheme, currentProps.sizeTheme)) updateState.createGeometry = true + if (!deepEqual(newProps.sizeTheme, currentProps.sizeTheme)) updateState.updateSize = true if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) updateState.updateColor = true if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true @@ -267,6 +268,10 @@ export function UnitsPointVisual<P extends UnitsPointProps>(builder: UnitsPointV updateState.updateColor = true } + if (updateState.updateSize) { + await createSizes(ctx, locationIt, newProps.sizeTheme, renderObject.values) + } + if (updateState.updateColor) { await createColors(ctx, locationIt, newProps.colorTheme, renderObject.values) } diff --git a/src/mol-gl/_spec/renderer.spec.ts b/src/mol-gl/_spec/renderer.spec.ts index c5df6f5f3..ae5c9805a 100644 --- a/src/mol-gl/_spec/renderer.spec.ts +++ b/src/mol-gl/_spec/renderer.spec.ts @@ -73,6 +73,8 @@ function createPoints() { instanceCount: ValueCell.create(1), dPointSizeAttenuation: ValueCell.create(true), + dPointFilledCircle: ValueCell.create(false), + uPointEdgeBleach: ValueCell.create(0.5), dUseFog: ValueCell.create(true), } const state: RenderableState = { diff --git a/src/mol-gl/renderable.ts b/src/mol-gl/renderable.ts index 09fb3fefe..ad35465e1 100644 --- a/src/mol-gl/renderable.ts +++ b/src/mol-gl/renderable.ts @@ -19,6 +19,7 @@ export interface Renderable<T extends RenderableValues & BaseValues> { readonly values: T readonly state: RenderableState readonly boundingSphere: Sphere3D + readonly opaque: boolean render: (variant: RenderVariant) => void getProgram: (variant: RenderVariant) => Program @@ -37,6 +38,7 @@ export function createRenderable<T extends Values<RenderableSchema> & BaseValues boundingSphere = calculateBoundingSphereFromValues(values) return boundingSphere }, + get opaque () { return values.uAlpha.ref.value === 1 }, render: (variant: RenderVariant) => renderItem.render(variant), getProgram: (variant: RenderVariant) => renderItem.getProgram(variant), diff --git a/src/mol-gl/renderable/mesh.ts b/src/mol-gl/renderable/mesh.ts index e18b88dab..91d41fa0b 100644 --- a/src/mol-gl/renderable/mesh.ts +++ b/src/mol-gl/renderable/mesh.ts @@ -27,8 +27,8 @@ export function MeshRenderable(ctx: Context, id: number, values: MeshValues, sta const internalValues = { uObjectId: ValueCell.create(id) } - const schaderCode = MeshShaderCode - const renderItem = createRenderItem(ctx, 'triangles', schaderCode, schema, { ...values, ...internalValues }) + const shaderCode = MeshShaderCode + const renderItem = createRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }) return createRenderable(renderItem, values, state) } \ No newline at end of file diff --git a/src/mol-gl/renderable/point.ts b/src/mol-gl/renderable/point.ts index f22941388..46e9275ce 100644 --- a/src/mol-gl/renderable/point.ts +++ b/src/mol-gl/renderable/point.ts @@ -19,6 +19,8 @@ export const PointSchema = { tSize: TextureSpec('alpha', 'ubyte'), dSizeType: DefineSpec('string', ['uniform', 'attribute']), dPointSizeAttenuation: DefineSpec('boolean'), + dPointFilledCircle: DefineSpec('boolean'), + uPointEdgeBleach: UniformSpec('f'), } export type PointSchema = typeof PointSchema export type PointValues = Values<PointSchema> @@ -28,8 +30,14 @@ export function PointRenderable(ctx: Context, id: number, values: PointValues, s const internalValues = { uObjectId: ValueCell.create(id) } - const schaderCode = PointShaderCode - const renderItem = createRenderItem(ctx, 'points', schaderCode, schema, { ...values, ...internalValues }) + const shaderCode = PointShaderCode + const renderItem = createRenderItem(ctx, 'points', shaderCode, schema, { ...values, ...internalValues }) + const renderable = createRenderable(renderItem, values, state); - return createRenderable(renderItem, values, state) + const isOpaque = Object.getOwnPropertyDescriptor(renderable, 'opaque')!.get as () => boolean + Object.defineProperty(renderable, 'opaque', { + get: () => isOpaque() && !values.dPointFilledCircle.ref.value && values.uPointEdgeBleach.ref.value === 0 + }); + + return renderable } \ No newline at end of file diff --git a/src/mol-gl/scene.ts b/src/mol-gl/scene.ts index f80d1a98a..98bde44e7 100644 --- a/src/mol-gl/scene.ts +++ b/src/mol-gl/scene.ts @@ -90,12 +90,12 @@ namespace Scene { }, eachOpaque: (callbackFn: (value: Renderable<any>, key: RenderObject) => void) => { renderableMap.forEach((r, o) => { - if (o.values.uAlpha.ref.value === 1) callbackFn(r, o) + if (r.opaque) callbackFn(r, o) }) }, eachTransparent: (callbackFn: (value: Renderable<any>, key: RenderObject) => void) => { renderableMap.forEach((r, o) => { - if (o.values.uAlpha.ref.value < 1) callbackFn(r, o) + if (!r.opaque) callbackFn(r, o) }) }, get count() { diff --git a/src/mol-gl/shader/point.frag b/src/mol-gl/shader/point.frag index 861bfef3f..c23b0e311 100644 --- a/src/mol-gl/shader/point.frag +++ b/src/mol-gl/shader/point.frag @@ -10,11 +10,29 @@ precision highp int; #pragma glslify: import('./chunks/common-frag-params.glsl') #pragma glslify: import('./chunks/color-frag-params.glsl') +#ifdef dPointFilledCircle + uniform float uPointEdgeBleach; +#endif + +const vec2 center = vec2(0.5); +const float radius = 0.5; + void main(){ #pragma glslify: import('./chunks/assign-material-color.glsl') - gl_FragColor = material; + #if defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking) + gl_FragColor = material; + #else + gl_FragColor = material; + + #ifdef dPointFilledCircle + float dist = distance(gl_PointCoord, center); + float alpha = 1.0 - smoothstep(radius - uPointEdgeBleach * 2.0, radius, dist); + gl_FragColor.a *= alpha; + if (gl_FragColor.a < 0.1) discard; + #endif - #pragma glslify: import('./chunks/apply-marker-color.glsl') - #pragma glslify: import('./chunks/apply-fog.glsl') + #pragma glslify: import('./chunks/apply-marker-color.glsl') + #pragma glslify: import('./chunks/apply-fog.glsl') + #endif } \ No newline at end of file -- GitLab