diff --git a/src/mol-app/ui/visualization/viewport.tsx b/src/mol-app/ui/visualization/viewport.tsx index 5382d269c62574397706c50c616139431d06f5c3..aeb157e3b81385e26d857bf870aa59da77c956fe 100644 --- a/src/mol-app/ui/visualization/viewport.tsx +++ b/src/mol-app/ui/visualization/viewport.tsx @@ -99,13 +99,14 @@ type ViewportState = { showLogo: boolean, aspectRatio: number, images: { [k: string]: ImageData } + info: string } -export class Viewport extends View<ViewportController, ViewportState, { noWebGl?: boolean, showLogo?: boolean, aspectRatio: number }> { +export class Viewport extends View<ViewportController, ViewportState, { noWebGl?: boolean, showLogo?: boolean, aspectRatio: number, info: string }> { private container: HTMLDivElement | null = null; private canvas: HTMLCanvasElement | null = null; private defaultBg = { r: 1, g: 1, b: 1 } - state: ViewportState = { noWebGl: false, showLogo: true, images: {}, aspectRatio: 1 }; + state: ViewportState = { noWebGl: false, showLogo: true, images: {}, aspectRatio: 1, info: '' }; componentDidMount() { if (!this.canvas || !this.container || !this.controller.context.initStage(this.canvas, this.container)) { @@ -121,6 +122,10 @@ export class Viewport extends View<ViewportController, ViewportState, { noWebGl? }) }) + viewer.identified.subscribe(info => { + this.setState({ info }) + }) + viewer.didDraw.subscribe(() => { // this.setState({ imageData: viewer.getImageData() }) viewer.pick() @@ -163,6 +168,18 @@ export class Viewport extends View<ViewportController, ViewportState, { noWebGl? </div> {this.state.showLogo ? <Logo /> : void 0} <ViewportControls controller={this.controller} /> + <div + style={{ + position: 'absolute', + top: 10, + left: 10, + padding: 10, + color: 'lightgrey', + background: 'rgba(0, 0, 0, 0.2)' + }} + > + {this.state.info} + </div> <div style={{ position: 'absolute', diff --git a/src/mol-gl/shader/utils/encode-float-rgba.glsl b/src/mol-gl/shader/utils/encode-float-rgba.glsl index ec9648d07cd697b2b5d41245f4ea772a3a7a40cf..fe67ca0d81e099c2bc1e6119238d364e724cc161 100644 --- a/src/mol-gl/shader/utils/encode-float-rgba.glsl +++ b/src/mol-gl/shader/utils/encode-float-rgba.glsl @@ -1,8 +1,12 @@ -vec4 encodeFloatRGBA(float v) { - vec4 enc = vec4(1.0, 255.0, 65025.0, 16581375.0) * v; - enc = fract(enc); - enc -= enc.yzww * vec4(1.0/255.0, 1.0/255.0, 1.0/255.0, 0.0); - return enc; +vec4 encodeFloatRGBA(in float value) { + value = clamp(value, 0., 16777216.); + vec3 c = vec3(0.); + c.b = mod(value, 256.); + value = floor(value/256.); + c.g = mod(value, 256.); + value = floor(value/256.); + c.r = mod(value, 256.); + return vec4(c/255., 1.); } #pragma glslify: export(encodeFloatRGBA) \ No newline at end of file diff --git a/src/mol-gl/shader/utils/encode-id-rgba.glsl b/src/mol-gl/shader/utils/encode-id-rgba.glsl index 49ccf35e5acf6e728c3c945cba584686b81ec148..9a47039656c6e0e0e8e0d08b8f411d3f6f3b89eb 100644 --- a/src/mol-gl/shader/utils/encode-id-rgba.glsl +++ b/src/mol-gl/shader/utils/encode-id-rgba.glsl @@ -1,9 +1,7 @@ #pragma glslify: encodeFloatRGBA = require(../utils/encode-float-rgba.glsl) -#define MAX_ID 16777216.0 - -vec4 encodeIdRGBA( const in float v ) { - return encodeFloatRGBA(((v + 1.0) / MAX_ID)); +vec4 encodeIdRGBA(const in float v) { + return encodeFloatRGBA(v + 1.0); } #pragma glslify: export(encodeIdRGBA) \ No newline at end of file diff --git a/src/mol-view/viewer.ts b/src/mol-view/viewer.ts index 3818ceec806c0d1602c0b563d3e28cb4e886c257..9c15ddc61bffa34364f9c913f6f8d6429cb78449 100644 --- a/src/mol-view/viewer.ts +++ b/src/mol-view/viewer.ts @@ -6,7 +6,7 @@ import { BehaviorSubject } from 'rxjs'; -import { Vec3, Mat4, EPSILON, Vec4 } from 'mol-math/linear-algebra' +import { Vec3, Mat4, EPSILON } from 'mol-math/linear-algebra' import InputObserver from 'mol-util/input/input-observer' import * as SetUtils from 'mol-util/set' import Renderer, { RendererStats } from 'mol-gl/renderer' @@ -22,6 +22,23 @@ import { createRenderTarget } from 'mol-gl/webgl/render-target'; import Scene from 'mol-gl/scene'; import { RenderVariant } from 'mol-gl/webgl/render-item'; +function decodeFloatRGBA(r: number, g: number, b: number) { + r = Math.floor(r) + g = Math.floor(g) + b = Math.floor(b) + return r * 256 * 256 + g * 256 + b +} + +function decodeIdRGBA(r: number, g: number, b: number) { + return decodeFloatRGBA(r, g, b) - 1 +} + +interface PickingId { + objectId: number + instanceId: number + elementId: number +} + interface Viewer { center: (p: Vec3) => void @@ -40,6 +57,7 @@ interface Viewer { identify: (x: number, y: number) => void reprCount: BehaviorSubject<number> + identified: BehaviorSubject<string> didDraw: BehaviorSubject<number> handleResize: () => void @@ -67,13 +85,17 @@ namespace Viewer { export function create(canvas: HTMLCanvasElement, container: Element): Viewer { const reprMap = new Map<Representation<any>, Set<RenderObject>>() const reprCount = new BehaviorSubject(0) + const identified = new BehaviorSubject('') const startTime = performance.now() const didDraw = new BehaviorSubject(0) const input = InputObserver.create(canvas) input.resize.subscribe(handleResize) - input.move.subscribe(({x, y}) => identify(x, y)) + input.move.subscribe(({x, y}) => { + const p = identify(x, y) + identified.next(`Object: ${p.objectId}, Instance: ${p.instanceId}, Element: ${p.elementId}`) + }) const camera = PerspectiveCamera.create({ near: 0.1, @@ -99,7 +121,7 @@ namespace Viewer { const scene = Scene.create(ctx) const renderer = Renderer.create(ctx, camera) - const pickScale = 1 // 1 / 4 + const pickScale = 1 / 4 const pickWidth = Math.round(canvas.width * pickScale) const pickHeight = Math.round(canvas.height * pickScale) const objectPickTarget = createRenderTarget(ctx, pickWidth, pickHeight) @@ -142,29 +164,26 @@ namespace Viewer { window.requestAnimationFrame(() => animate()) } - const decodeFactors = Vec4.create(1, 1/255, 1/65025, 1/16581375) - function decodeFloatRGBA(rgba: Vec4) { - return Vec4.dot(rgba, decodeFactors); - } - - function identify (x: number, y: number) { + function identify (x: number, y: number): PickingId { + const buffer = new Uint8Array(4) y = canvas.height - y // flip y + const xp = Math.round(x * pickScale) const yp = Math.round(y * pickScale) - console.log('position', x, y, xp, yp) - const buffer = new Uint8Array(4) + objectPickTarget.bind() + ctx.readPixels(xp, yp, 1, 1, buffer) + const objectId = decodeIdRGBA(buffer[0], buffer[1], buffer[2]) + + instancePickTarget.bind() + ctx.readPixels(xp, yp, 1, 1, buffer) + const instanceId = decodeIdRGBA(buffer[0], buffer[1], buffer[2]) + elementPickTarget.bind() ctx.readPixels(xp, yp, 1, 1, buffer) - console.log('identify', buffer[0], buffer[1], buffer[2], buffer[3]) - const v = Vec4.create(buffer[0], buffer[1], buffer[2], buffer[3]) - const d = decodeFloatRGBA(v) - console.log(d) - console.log(d * 16777216) + const elementId = decodeIdRGBA(buffer[0], buffer[1], buffer[2]) - ctx.unbindFramebuffer() - ctx.readPixels(x, y, 1, 1, buffer) - console.log('color', buffer[0], buffer[1], buffer[2], buffer[3]) + return { objectId, instanceId, elementId } } handleResize() @@ -242,6 +261,7 @@ namespace Viewer { } }, reprCount, + identified, didDraw, get input() {