Browse Source

basic picking works

Alexander Rose 6 years ago
parent
commit
71590f3a23

+ 19 - 2
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',

+ 9 - 5
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)

+ 2 - 4
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)

+ 39 - 19
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() {