Alexander Rose 6 роки тому
батько
коміт
bc86142fcb

+ 17 - 14
src/mol-app/ui/visualization/image-canvas.tsx

@@ -22,39 +22,41 @@ export class ImageCanvas extends React.Component<{ imageData: ImageData, aspectR
     private canvas: HTMLCanvasElement | null = null;
     private ctx: CanvasRenderingContext2D | null = null;
 
-    componentWillMount() {
+    updateStateFromProps() {
         this.setState({
             imageData: this.props.imageData,
             ...getExtend(this.props.aspectRatio, this.props.maxWidth, this.props.maxHeight)
         })
     }
 
-    componentDidMount() {
+    updateImage() {
         if (this.canvas) {
             this.canvas.width = this.state.imageData.width
             this.canvas.height = this.state.imageData.height
-            this.ctx = this.canvas.getContext('2d')
         }
         if (this.ctx) {
             this.ctx.putImageData(this.state.imageData, 0, 0)
         }
     }
 
+    componentWillMount() {
+        this.updateStateFromProps()
+    }
+
+    componentDidMount() {
+        if (this.canvas && !this.ctx) {
+            this.ctx = this.canvas.getContext('2d')
+            if (this.ctx) this.ctx.imageSmoothingEnabled = false
+        }
+        this.updateImage()
+    }
+
     componentWillReceiveProps() {
-        this.setState({
-            imageData: this.props.imageData,
-            ...getExtend(this.props.aspectRatio, this.props.maxWidth, this.props.maxHeight)
-        })
+        this.updateStateFromProps()
     }
 
     componentDidUpdate() {
-        if (this.canvas) {
-            this.canvas.width = this.state.imageData.width
-            this.canvas.height = this.state.imageData.height
-        }
-        if (this.ctx) {
-            this.ctx.putImageData(this.state.imageData, 0, 0)
-        }
+        this.updateImage()
     }
 
     render() {
@@ -74,6 +76,7 @@ export class ImageCanvas extends React.Component<{ imageData: ImageData, aspectR
                 style={{
                     width: this.state.width,
                     height: this.state.height,
+                    imageRendering: 'pixelated'
                 }}
             />
         </div>;

+ 5 - 2
src/mol-app/ui/visualization/viewport.tsx

@@ -114,8 +114,11 @@ export class Viewport extends View<ViewportController, {}, { noWebGl?: boolean,
             })
         })
 
-        viewer.didDraw.subscribe(() => this.setState({ imageData: viewer.getImageData() }))
-        viewer.didDraw.subscribe(() => this.setState({ imageData: viewer.getImageData() }))
+        viewer.didDraw.subscribe(() => {
+            // this.setState({ imageData: viewer.getImageData() })
+            viewer.pick()
+            this.setState({ imageData: viewer.getPickImageData() })
+        })
 
         if (this.container) {
             this.setState({ aspectRatio: this.container.clientWidth / this.container.clientHeight })

+ 0 - 1
src/mol-geo/representation/structure/point.ts

@@ -98,7 +98,6 @@ export default function Point(): UnitsRepresentation<PointProps> {
                     ...size,
 
                     uAlpha: ValueCell.create(defaults(props.alpha, 1.0)),
-                    uObjectId: ValueCell.create(0),
                     uInstanceCount: ValueCell.create(instanceCount),
                     uElementCount: ValueCell.create(group.elements.length),
 

+ 0 - 1
src/mol-geo/representation/structure/spacefill.ts

@@ -114,7 +114,6 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
                     ...color,
 
                     uAlpha: ValueCell.create(defaults(props.alpha, 1.0)),
-                    uObjectId: ValueCell.create(0),
                     uInstanceCount: ValueCell.create(instanceCount),
                     uElementCount: ValueCell.create(group.elements.length),
 

+ 0 - 1
src/mol-geo/representation/volume/surface.ts

@@ -73,7 +73,6 @@ export default function Surface(): VolumeElementRepresentation<SurfaceProps> {
                     ...color,
 
                     uAlpha: ValueCell.create(defaults(props.alpha, 1.0)),
-                    uObjectId: ValueCell.create(0),
                     uInstanceCount: ValueCell.create(instanceCount),
                     uElementCount: ValueCell.create(mesh.triangleCount),
 

+ 5 - 4
src/mol-gl/_spec/renderer.spec.ts

@@ -18,6 +18,7 @@ import { createContext } from '../webgl/context';
 import { RenderableState } from '../renderable';
 import { createPointRenderObject } from '../render-object';
 import { PointValues } from '../renderable/point';
+import Scene from '../scene';
 
 // function writeImage(gl: WebGLRenderingContext, width: number, height: number) {
 //     const pixels = new Uint8Array(width * height * 4)
@@ -61,7 +62,6 @@ function createPoints() {
         ...size,
 
         uAlpha: ValueCell.create(1.0),
-        uObjectId: ValueCell.create(0),
         uInstanceCount: ValueCell.create(1),
         uElementCount: ValueCell.create(3),
 
@@ -101,18 +101,19 @@ describe('renderer', () => {
     it('points', () => {
         const [ width, height ] = [ 32, 32 ]
         const gl = createGl(width, height, { preserveDrawingBuffer: true })
-        const { ctx, renderer } = createRenderer(gl)
+        const { ctx } = createRenderer(gl)
+        const scene = Scene.create(ctx)
 
         const points = createPoints()
 
-        renderer.add(points)
+        scene.add(points)
         expect(ctx.bufferCount).toBe(6);
         expect(ctx.textureCount).toBe(1);
         expect(ctx.vaoCount).toBe(1);
         expect(ctx.programCache.count).toBe(1);
         expect(ctx.shaderCache.count).toBe(2);
 
-        renderer.remove(points)
+        scene.remove(points)
         expect(ctx.bufferCount).toBe(0);
         expect(ctx.textureCount).toBe(0);
         expect(ctx.vaoCount).toBe(0);

+ 2 - 2
src/mol-gl/render-object.ts

@@ -27,7 +27,7 @@ export function createPointRenderObject(values: PointValues, state: RenderableSt
 
 export function createRenderable(ctx: Context, o: RenderObject) {
     switch (o.type) {
-        case 'mesh': return MeshRenderable(ctx, o.values, o.state)
-        case 'point': return PointRenderable(ctx, o.values, o.state)
+        case 'mesh': return MeshRenderable(ctx, o.id, o.values, o.state)
+        case 'point': return PointRenderable(ctx, o.id, o.values, o.state)
     }
 }

+ 3 - 1
src/mol-gl/renderable.ts

@@ -14,10 +14,12 @@ export type RenderableState = {
 
 export interface Renderable<T extends RenderableValues> {
     draw: () => void
+    pick: () => void
     values: T
     state: RenderableState
     name: string
-    program: Program
+    drawProgram: Program
+    pickProgram: Program
     update: () => void
     dispose: () => void
 }

+ 13 - 5
src/mol-gl/renderable/mesh.ts

@@ -7,8 +7,9 @@
 import { Renderable, RenderableState } from '../renderable'
 import { Context } from '../webgl/context';
 import { createRenderItem } from '../webgl/render-item';
-import { GlobalUniformSchema, BaseSchema, AttributeSpec, ElementsSpec, DefineSpec, Values } from '../renderable/schema';
+import { GlobalUniformSchema, BaseSchema, AttributeSpec, ElementsSpec, DefineSpec, Values, InternalSchema } from '../renderable/schema';
 import { MeshShaderCode } from '../shader-code';
+import { ValueCell } from 'mol-util';
 
 export const MeshSchema = {
     ...BaseSchema,
@@ -21,19 +22,26 @@ export const MeshSchema = {
 export type MeshSchema = typeof MeshSchema
 export type MeshValues = Values<MeshSchema>
 
-export function MeshRenderable(ctx: Context, values: MeshValues, state: RenderableState): Renderable<MeshValues> {
-    const schema = { ...GlobalUniformSchema, ...MeshSchema }
+export function MeshRenderable(ctx: Context, id: number, values: MeshValues, state: RenderableState): Renderable<MeshValues> {
+    const schema = { ...GlobalUniformSchema, ...InternalSchema, ...MeshSchema }
+    const internalValues = {
+        uObjectId: ValueCell.create(id)
+    }
     const schaderCode = MeshShaderCode
-    const renderItem = createRenderItem(ctx, 'triangles', schaderCode, schema, values)
+    const renderItem = createRenderItem(ctx, 'triangles', schaderCode, schema, { ...values, ...internalValues })
 
     return {
         draw: () => {
             renderItem.draw()
         },
+        pick: () => {
+            renderItem.pick()
+        },
         get values () { return values },
         get state () { return state },
         name: 'mesh',
-        get program () { return renderItem.program },
+        get drawProgram () { return renderItem.drawProgram },
+        get pickProgram () { return renderItem.pickProgram },
         update: () => {
             renderItem.update()
         },

+ 13 - 5
src/mol-gl/renderable/point.ts

@@ -7,8 +7,9 @@
 import { Renderable, RenderableState } from '../renderable'
 import { Context } from '../webgl/context';
 import { createRenderItem } from '../webgl/render-item';
-import { GlobalUniformSchema, BaseSchema, AttributeSpec, UniformSpec, DefineSpec, Values } from '../renderable/schema';
+import { GlobalUniformSchema, BaseSchema, AttributeSpec, UniformSpec, DefineSpec, Values, InternalSchema } from '../renderable/schema';
 import { PointShaderCode } from '../shader-code';
+import { ValueCell } from 'mol-util';
 
 export const PointSchema = {
     ...BaseSchema,
@@ -20,19 +21,26 @@ export const PointSchema = {
 export type PointSchema = typeof PointSchema
 export type PointValues = Values<PointSchema>
 
-export function PointRenderable(ctx: Context, values: PointValues, state: RenderableState): Renderable<PointValues> {
-    const schema = { ...GlobalUniformSchema, ...PointSchema }
+export function PointRenderable(ctx: Context, id: number, values: PointValues, state: RenderableState): Renderable<PointValues> {
+    const schema = { ...GlobalUniformSchema, ...InternalSchema, ...PointSchema }
+    const internalValues = {
+        uObjectId: ValueCell.create(id)
+    }
     const schaderCode = PointShaderCode
-    const renderItem = createRenderItem(ctx, 'points', schaderCode, schema, values)
+    const renderItem = createRenderItem(ctx, 'points', schaderCode, schema, { ...values, ...internalValues })
 
     return {
         draw: () => {
             renderItem.draw()
         },
+        pick: () => {
+            renderItem.pick()
+        },
         get values () { return values },
         get state () { return state },
         name: 'point',
-        get program () { return renderItem.program },
+        get drawProgram () { return renderItem.drawProgram },
+        get pickProgram () { return renderItem.pickProgram },
         update: () => {
             renderItem.update()
         },

+ 4 - 1
src/mol-gl/renderable/schema.ts

@@ -130,6 +130,10 @@ export const GlobalUniformSchema = {
 export type GlobalUniformSchema = typeof GlobalUniformSchema
 export type GlobalUniformValues = { [k in keyof GlobalUniformSchema]: ValueCell<any> }
 
+export const InternalSchema = {
+    uObjectId: UniformSpec('i'),
+}
+
 export const BaseSchema = {
     aInstanceId: AttributeSpec('float32', 1, 1),
     aPosition: AttributeSpec('float32', 3, 0),
@@ -138,7 +142,6 @@ export const BaseSchema = {
     aColor: AttributeSpec('float32', 3, 0),
 
     uAlpha: UniformSpec('f'),
-    uObjectId: UniformSpec('i'),
     uInstanceCount: UniformSpec('i'),
     uElementCount: UniformSpec('i'),
     uColorTexSize: UniformSpec('v2'),

+ 18 - 41
src/mol-gl/renderer.ts

@@ -15,11 +15,8 @@ import { Renderable } from './renderable';
 import { Color } from 'mol-util/color';
 import { ValueCell } from 'mol-util';
 import { RenderableValues, GlobalUniformValues } from './renderable/schema';
-import { RenderObject } from './render-object';
-import { BehaviorSubject } from 'rxjs';
 
 export interface RendererStats {
-    renderableCount: number
     programCount: number
     shaderCount: number
     bufferCount: number
@@ -28,18 +25,12 @@ export interface RendererStats {
 }
 
 interface Renderer {
-    add: (o: RenderObject) => void
-    remove: (o: RenderObject) => void
-    update: () => void
-    clear: () => void
-    draw: () => void
+    render: (scene: Scene, pick: boolean) => void
 
     setViewport: (viewport: Viewport) => void
     setClearColor: (color: Color) => void
     getImageData: () => ImageData
 
-    didDraw: BehaviorSubject<number>
-
     stats: RendererStats
     dispose: () => void
 }
@@ -58,10 +49,6 @@ namespace Renderer {
     export function create(ctx: Context, camera: Camera, props: RendererProps = {}): Renderer {
         const { gl } = ctx
         let { clearColor, viewport: _viewport } = { ...DefaultRendererProps, ...props }
-        const scene = Scene.create(ctx)
-
-        const startTime = performance.now()
-        const didDraw = new BehaviorSubject(0)
 
         const model = Mat4.identity()
         const viewport = Viewport.clone(_viewport)
@@ -90,12 +77,13 @@ namespace Renderer {
         }
 
         let currentProgramId = -1
-        const drawObject = (r: Renderable<RenderableValues>) => {
+        const renderObject = (r: Renderable<RenderableValues>, pick: boolean) => {
+            const program = pick ? r.pickProgram : r.drawProgram
             if (r.state.visible) {
-                if (currentProgramId !== r.program.id) {
-                    r.program.use()
-                    r.program.setUniforms(globalUniforms)
-                    currentProgramId = r.program.id
+                if (currentProgramId !== program.id) {
+                    program.use()
+                    program.setUniforms(globalUniforms)
+                    currentProgramId = program.id
                 }
                 if (r.values.dDoubleSided.ref.value) {
                     gl.disable(gl.CULL_FACE)
@@ -113,11 +101,15 @@ namespace Renderer {
 
                 gl.depthMask(r.state.depthMask)
 
-                r.draw()
+                if (pick) {
+                    r.pick()
+                } else {
+                    r.draw()
+                }
             }
         }
 
-        const draw = () => {
+        const render = (scene: Scene, pick: boolean) => {
             ValueCell.update(globalUniforms.uView, camera.view)
             ValueCell.update(globalUniforms.uProjection, camera.projection)
 
@@ -128,29 +120,17 @@ namespace Renderer {
 
             gl.disable(gl.BLEND)
             gl.enable(gl.DEPTH_TEST)
-            scene.eachOpaque(drawObject)
+            scene.eachOpaque((r) => renderObject(r, pick))
 
             gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
             gl.enable(gl.BLEND)
-            scene.eachTransparent(drawObject)
+            scene.eachTransparent((r) => renderObject(r, pick))
 
-            didDraw.next(performance.now() - startTime)
+            gl.finish()
         }
 
         return {
-            add: (o: RenderObject) => {
-                scene.add(o)
-            },
-            remove: (o: RenderObject) => {
-                scene.remove(o)
-            },
-            update: () => {
-                scene.forEach((r, o) => r.update())
-            },
-            clear: () => {
-                scene.clear()
-            },
-            draw,
+            render,
 
             setClearColor,
             setViewport: (newViewport: Viewport) => {
@@ -166,11 +146,8 @@ namespace Renderer {
                 return createImageData(buffer, width, height)
             },
 
-            didDraw,
-
             get stats(): RendererStats {
                 return {
-                    renderableCount: scene.count,
                     programCount: ctx.programCache.count,
                     shaderCount: ctx.shaderCache.count,
                     bufferCount: ctx.bufferCount,
@@ -179,7 +156,7 @@ namespace Renderer {
                 }
             },
             dispose: () => {
-                scene.clear()
+                // TODO
             }
         }
     }

+ 4 - 0
src/mol-gl/scene.ts

@@ -13,6 +13,7 @@ import { RenderObject, createRenderable } from './render-object';
 interface Scene {
     add: (o: RenderObject) => void
     remove: (o: RenderObject) => void
+    update: () => void
     clear: () => void
     forEach: (callbackFn: (value: Renderable<any>, key: RenderObject) => void) => void
     eachOpaque: (callbackFn: (value: Renderable<any>, key: RenderObject) => void) => void
@@ -39,6 +40,9 @@ namespace Scene {
                     renderableMap.delete(o)
                 }
             },
+            update: () => {
+                renderableMap.forEach((r, o) => r.update())
+            },
             clear: () => {
                 renderableMap.forEach(renderable => renderable.dispose())
                 renderableMap.clear()

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

@@ -1,5 +1,5 @@
 #if defined(dColorType_uniform)
-    vec3 material = uColor;
-#elif defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_element) || defined(dColorType_elementInstance)
-    vec3 material = vColor;
+    vec4 material.rgb = uColor;
+#elif defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_element) || defined(dColorType_elementInstance) || defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_elementPicking)
+    vec4 material = vColor;
 #endif

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

@@ -1,9 +1,15 @@
 #if defined(dColorType_attribute)
-    vColor = aColor;
+    vColor.xyz = aColor;
 #elif defined(dColorType_instance)
-    vColor = read_vec3(tColor, aInstanceId, uColorTexSize);
+    vColor.xyz = read_vec3(tColor, aInstanceId, uColorTexSize);
 #elif defined(dColorType_element)
-    vColor = read_vec3(tColor, aElementId, uColorTexSize);
+    vColor.xyz = read_vec3(tColor, aElementId, uColorTexSize);
 #elif defined(dColorType_elementInstance)
-    vColor = read_vec3(tColor, aInstanceId * float(uElementCount) + aElementId, uColorTexSize);
+    vColor.xyz = read_vec3(tColor, aInstanceId * float(uElementCount) + aElementId, uColorTexSize);
+#elif defined(dColorType_objectPicking)
+    vColor = encodeIdRGBA(float(uObjectId));
+#elif defined(dColorType_instancePicking)
+    vColor = encodeIdRGBA(aInstanceId);
+#elif defined(dColorType_elementPicking)
+    vColor = encodeIdRGBA(aElementId);
 #endif

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

@@ -1,5 +1,5 @@
 #if defined(dColorType_uniform)
     uniform vec3 uColor;
-#elif defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_element) || defined(dColorType_elementInstance)
-    varying vec3 vColor;
+#elif defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_element) || defined(dColorType_elementInstance) || defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_elementPicking)
+    varying vec4 vColor;
 #endif

+ 5 - 2
src/mol-gl/shader/chunks/color-vert-params.glsl

@@ -1,12 +1,15 @@
 #if defined(dColorType_uniform)
     uniform vec3 uColor;
 #elif defined(dColorType_attribute)
-    varying vec3 vColor;
+    varying vec4 vColor;
     attribute vec3 aColor;
 #elif defined(dColorType_instance) || defined(dColorType_element) || defined(dColorType_elementInstance)
-    varying vec3 vColor;
+    varying vec4 vColor;
     uniform vec2 uColorTexSize;
     uniform sampler2D tColor;
+#elif defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_elementPicking)
+    varying vec4 vColor;
+    #pragma glslify: encodeIdRGBA = require(../utils/encode-id-rgba.glsl)
 #endif
 
 #pragma glslify: read_vec3 = require(../utils/read-from-texture.glsl)

+ 3 - 0
src/mol-gl/shader/chunks/common-frag-params.glsl

@@ -0,0 +1,3 @@
+uniform int uObjectId;
+uniform int uInstanceCount;
+uniform int uElementCount;

+ 5 - 0
src/mol-gl/shader/chunks/common-vert-params.glsl

@@ -0,0 +1,5 @@
+uniform mat4 uProjection, uModel, uView;
+
+uniform int uObjectId;
+uniform int uInstanceCount;
+uniform int uElementCount;

+ 37 - 29
src/mol-gl/shader/mesh.frag

@@ -9,6 +9,10 @@
 #endif
 
 precision highp float;
+precision highp int;
+
+#pragma glslify: import('./chunks/common-frag-params.glsl')
+#pragma glslify: import('./chunks/color-frag-params.glsl')
 
 // uniform vec3 uLightPosition;
 uniform vec3 uLightColor;
@@ -21,8 +25,6 @@ uniform float uAlpha;
 #endif
 varying vec3 vViewPosition;
 
-#pragma glslify: import('./chunks/color-frag-params.glsl')
-
 #pragma glslify: attenuation = require(./utils/attenuation.glsl)
 #pragma glslify: calculateSpecular = require(./utils/phong-specular.glsl)
 #pragma glslify: calculateDiffuse = require(./utils/oren-nayar-diffuse.glsl)
@@ -36,37 +38,43 @@ void main() {
     // material color
     #pragma glslify: import('./chunks/color-assign-material.glsl')
 
-    // determine surface to light direction
-    // vec4 viewLightPosition = view * vec4(lightPosition, 1.0);
-    // vec3 lightVector = viewLightPosition.xyz - vViewPosition;
-    vec3 lightVector = vViewPosition;
+    #if defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_elementPicking)
+        // gl_FragColor = vec4(material.r, material.g, material.a, 1.0);
+        gl_FragColor = material;
+        gl_FragColor.a = 1.0;
+    #else
+        // determine surface to light direction
+        // vec4 viewLightPosition = view * vec4(lightPosition, 1.0);
+        // vec3 lightVector = viewLightPosition.xyz - vViewPosition;
+        vec3 lightVector = vViewPosition;
 
-    vec3 L = normalize(lightVector); // light direction
-    vec3 V = normalize(vViewPosition); // eye direction
+        vec3 L = normalize(lightVector); // light direction
+        vec3 V = normalize(vViewPosition); // eye direction
 
-    // surface normal
-    #ifdef dFlatShaded
-        vec3 fdx = dFdx(vViewPosition);
-        vec3 fdy = dFdy(vViewPosition);
-        vec3 N = -normalize(cross(fdx, fdy));
-    #else
-        vec3 N = -normalize(vNormal);
-        #ifdef dDoubleSided
-            N = N * (float(gl_FrontFacing) * 2.0 - 1.0);
+        // surface normal
+        #ifdef dFlatShaded
+            vec3 fdx = dFdx(vViewPosition);
+            vec3 fdy = dFdy(vViewPosition);
+            vec3 N = -normalize(cross(fdx, fdy));
+        #else
+            vec3 N = -normalize(vNormal);
+            #ifdef dDoubleSided
+                N = N * (float(gl_FrontFacing) * 2.0 - 1.0);
+            #endif
         #endif
-    #endif
 
-    // compute our diffuse & specular terms
-    float specular = calculateSpecular(L, V, N, shininess) * specularScale;
-    vec3 diffuse = uLightColor * calculateDiffuse(L, V, N, roughness, albedo);
-    vec3 ambient = uLightAmbient;
+        // compute our diffuse & specular terms
+        float specular = calculateSpecular(L, V, N, shininess) * specularScale;
+        vec3 diffuse = uLightColor * calculateDiffuse(L, V, N, roughness, albedo);
+        vec3 ambient = uLightAmbient;
 
-    // add the lighting
-    vec3 finalColor = material * (diffuse + ambient) + specular;
+        // add the lighting
+        vec3 finalColor = material.rgb * (diffuse + ambient) + specular;
 
-    // gl_FragColor.rgb = N;
-    // gl_FragColor.a = 1.0;
-    // gl_FragColor.rgb = vec3(1.0, 0.0, 0.0);
-    gl_FragColor.rgb = finalColor;
-    gl_FragColor.a = uAlpha;
+        // gl_FragColor.rgb = N;
+        // gl_FragColor.a = 1.0;
+        // gl_FragColor.rgb = vec3(1.0, 0.0, 0.0);
+        gl_FragColor.rgb = finalColor;
+        gl_FragColor.a = uAlpha;
+    #endif
 }

+ 2 - 6
src/mol-gl/shader/mesh.vert

@@ -5,13 +5,9 @@
  */
 
 precision highp float;
+precision highp int;
 
-uniform mat4 uProjection, uModel, uView;
-
-uniform int uObjectId;
-uniform int uInstanceCount;
-uniform int uElementCount;
-
+#pragma glslify: import('./chunks/common-vert-params.glsl')
 #pragma glslify: import('./chunks/color-vert-params.glsl')
 
 attribute vec3 aPosition;

+ 4 - 2
src/mol-gl/shader/point.frag

@@ -5,11 +5,13 @@
  */
 
 precision highp float;
+precision highp int;
 
-uniform float uAlpha;
-
+#pragma glslify: import('./chunks/common-frag-params.glsl')
 #pragma glslify: import('./chunks/color-frag-params.glsl')
 
+uniform float uAlpha;
+
 void main(){
     #pragma glslify: import('./chunks/color-assign-material.glsl')
     gl_FragColor = vec4(material, uAlpha);

+ 3 - 7
src/mol-gl/shader/point.vert

@@ -5,18 +5,14 @@
  */
 
 precision highp float;
+precision highp int;
 
-uniform mat4 uProjection, uModel, uView;
-
-uniform int uObjectId;
-uniform int uInstanceCount;
-uniform int uElementCount;
+#pragma glslify: import('./chunks/common-vert-params.glsl')
+#pragma glslify: import('./chunks/color-vert-params.glsl')
 
 uniform float uPixelRatio;
 uniform float uViewportHeight;
 
-#pragma glslify: import('./chunks/color-vert-params.glsl')
-
 #if defined(dSizeType_uniform)
     uniform float uSize;
 #elif defined(dSizeType_attribute)

+ 1 - 1
src/mol-gl/shader/utils/decode-float-rgba.glsl

@@ -1,5 +1,5 @@
 float decodeFloatRGBA(vec4 rgba) {
-  return dot(rgba, vec4(1.0, 1/255.0, 1/65025.0, 1/16581375.0));
+    return dot(rgba, vec4(1.0, 1/255.0, 1/65025.0, 1/16581375.0));
 }
 
 #pragma glslify: export(decodeFloatRGBA)

+ 4 - 4
src/mol-gl/shader/utils/encode-float-rgba.glsl

@@ -1,8 +1,8 @@
 vec4 encodeFloatRGBA(float v) {
-  vec4 enc = vec4(1.0, 255.0, 65025.0, 16581375.0) * v;
-  enc = frac(enc);
-  enc -= enc.yzww * float4(1.0/255.0,1.0/255.0,1.0/255.0,0.0);
-  return enc;
+    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;
 }
 
 #pragma glslify: export(encodeFloatRGBA)

+ 9 - 0
src/mol-gl/shader/utils/encode-id-rgba.glsl

@@ -0,0 +1,9 @@
+#pragma glslify: encodeFloatRGBA = require(../utils/encode-float-rgba.glsl)
+
+#define MAX_ID 16777216.0
+
+vec4 encodeIdRGBA( const in float v ) {
+	return encodeFloatRGBA(1.0 - ((v + 1.0) / MAX_ID));
+}
+
+#pragma glslify: export(encodeIdRGBA)

+ 30 - 10
src/mol-gl/webgl/render-item.ts

@@ -12,6 +12,7 @@ import { Program } from './program';
 import { RenderableSchema, RenderableValues, AttributeSpec, getValueVersions, splitValues } from '../renderable/schema';
 import { idFactory } from 'mol-util/id-factory';
 import { deleteVertexArray, createVertexArray } from './vertex-array';
+import { ValueCell } from 'mol-util';
 
 const getNextRenderItemId = idFactory()
 
@@ -32,11 +33,12 @@ export function getDrawMode(ctx: Context, drawMode: DrawMode) {
 
 export interface RenderItem {
     readonly id: number
-    readonly programId: number
-    readonly program: Program
+    readonly drawProgram: Program
+    readonly pickProgram: Program
 
     update: () => void
     draw: () => void
+    pick: () => void
     destroy: () => void
 }
 
@@ -53,6 +55,10 @@ export function createRenderItem(ctx: Context, drawMode: DrawMode, shaderCode: S
         shaderCode: addShaderDefines(defineValues, shaderCode),
         schema
     })
+    let pickProgram = programCache.get(ctx, {
+        shaderCode: addShaderDefines({ ...defineValues, dColorType: ValueCell.create('elementPicking') }, shaderCode),
+        schema
+    })
 
     const textures = createTextures(ctx, schema, textureValues)
     const attributeBuffers = createAttributeBuffers(ctx, schema, attributeValues)
@@ -63,14 +69,15 @@ export function createRenderItem(ctx: Context, drawMode: DrawMode, shaderCode: S
         elementsBuffer = createElementsBuffer(ctx, elements.ref.value)
     }
 
-    let vertexArray: WebGLVertexArrayObjectOES | undefined = createVertexArray(ctx, drawProgram.value, attributeBuffers, elementsBuffer)
+    let drawVertexArray: WebGLVertexArrayObjectOES | undefined = createVertexArray(ctx, drawProgram.value, attributeBuffers, elementsBuffer)
+    let pickVertexArray: WebGLVertexArrayObjectOES | undefined = createVertexArray(ctx, pickProgram.value, attributeBuffers, elementsBuffer)
 
     let drawCount = values.drawCount.ref.value
     let instanceCount = values.instanceCount.ref.value
 
     let destroyed = false
 
-    function render(program: Program) {
+    function render(program: Program, vertexArray: WebGLVertexArrayObjectOES | undefined) {
         program.setUniforms(uniformValues)
         if (oesVertexArrayObject && vertexArray) {
             oesVertexArrayObject.bindVertexArrayOES(vertexArray)
@@ -88,11 +95,14 @@ export function createRenderItem(ctx: Context, drawMode: DrawMode, shaderCode: S
 
     return {
         id,
-        get programId () { return drawProgram.value.id },
-        get program () { return drawProgram.value },
+        get drawProgram () { return drawProgram.value },
+        get pickProgram () { return pickProgram.value },
 
         draw: () => {
-            render(drawProgram.value)
+            render(drawProgram.value, drawVertexArray)
+        },
+        pick: () => {
+            render(pickProgram.value, pickVertexArray)
         },
         update: () => {
             let defineChange = false
@@ -112,6 +122,12 @@ export function createRenderItem(ctx: Context, drawMode: DrawMode, shaderCode: S
                     shaderCode: addShaderDefines(defineValues, shaderCode),
                     schema
                 })
+
+                pickProgram.free()
+                pickProgram = programCache.get(ctx, {
+                    shaderCode: addShaderDefines({ ...defineValues, dColorType: ValueCell.create('elementPicking') }, shaderCode),
+                    schema
+                })
             }
 
             if (values.drawCount.ref.version !== versions.drawCount) {
@@ -160,8 +176,10 @@ export function createRenderItem(ctx: Context, drawMode: DrawMode, shaderCode: S
 
             if (defineChange || bufferChange) {
                 console.log('program/defines or buffers changed, rebuild vao')
-                deleteVertexArray(ctx, vertexArray)
-                vertexArray = createVertexArray(ctx, drawProgram.value, attributeBuffers, elementsBuffer)
+                deleteVertexArray(ctx, drawVertexArray)
+                drawVertexArray = createVertexArray(ctx, drawProgram.value, attributeBuffers, elementsBuffer)
+                deleteVertexArray(ctx, pickVertexArray)
+                pickVertexArray = createVertexArray(ctx, drawProgram.value, attributeBuffers, elementsBuffer)
             }
 
             Object.keys(textureValues).forEach(k => {
@@ -176,10 +194,12 @@ export function createRenderItem(ctx: Context, drawMode: DrawMode, shaderCode: S
         destroy: () => {
             if (!destroyed) {
                 drawProgram.free()
+                pickProgram.free()
                 Object.keys(textures).forEach(k => textures[k].destroy())
                 Object.keys(attributeBuffers).forEach(k => attributeBuffers[k].destroy())
                 if (elementsBuffer) elementsBuffer.destroy()
-                deleteVertexArray(ctx, vertexArray)
+                deleteVertexArray(ctx, drawVertexArray)
+                deleteVertexArray(ctx, pickVertexArray)
                 destroyed = true
             }
         }

+ 16 - 7
src/mol-gl/webgl/render-target.ts

@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Context } from './context'
+import { Context, createImageData } from './context'
 import { idFactory } from 'mol-util/id-factory';
 import { createTexture } from './texture';
 import { createFramebuffer } from './framebuffer';
@@ -17,15 +17,21 @@ export interface RenderTarget {
 
     bind: () => void
     setSize: (width: number, height: number) => void
-    readPixels: (x: number, y: number, width: number, height: number, buffer: Uint8Array) => void
+    getImageData: () => ImageData
     destroy: () => void
 }
 
 export function createRenderTarget (ctx: Context, _width: number, _height: number): RenderTarget {
     const { gl } = ctx
 
+    const image = {
+        array: new Uint8Array(_width * _height * 4),
+        width: _width,
+        height: _height
+    }
+
     const targetTexture = createTexture(ctx, 'rgba', 'ubyte')
-    targetTexture.setSize(_width, _height)
+    targetTexture.load(image)
 
     const framebuffer = createFramebuffer(ctx)
     framebuffer.bind()
@@ -51,12 +57,15 @@ export function createRenderTarget (ctx: Context, _width: number, _height: numbe
         setSize: (width: number, height: number) => {
             _width = width
             _height = height
-            targetTexture.setSize(_width, _height)
+            image.array = new Uint8Array(_width * _height * 4)
+            image.width = _width
+            image.height = _height
+            targetTexture.load(image)
         },
-        readPixels: (x: number, y: number, width: number, height: number, buffer: Uint8Array) => {
+        getImageData: () => {
             framebuffer.bind()
-            ctx.readPixels(x, y, width, height, buffer)
-            ctx.unbindFramebuffer()
+            ctx.readPixels(0, 0, _width, _height, image.array)
+            return createImageData(image.array, _width, _height)
         },
         destroy: () => {
             if (destroyed) return

+ 0 - 6
src/mol-gl/webgl/texture.ts

@@ -43,7 +43,6 @@ export interface Texture {
     load: (image: TextureImage) => void
     bind: (id: TextureId) => void
     unbind: (id: TextureId) => void
-    setSize: (width: number, height: number) => void
     destroy: () => void
 }
 
@@ -103,11 +102,6 @@ export function createTexture(ctx: Context, _format: TextureFormat, _type: Textu
             gl.activeTexture(gl.TEXTURE0 + id)
             gl.bindTexture(gl.TEXTURE_2D, null)
         },
-        setSize: (width: number, height: number) => {
-            gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, format, type, null)
-            _width = width
-            _height = height
-        },
         destroy: () => {
             if (destroyed) return
             gl.deleteTexture(texture)

+ 44 - 11
src/mol-view/viewer.ts

@@ -18,6 +18,8 @@ import { PerspectiveCamera } from './camera/perspective'
 import { resizeCanvas } from './util';
 import { createContext } from 'mol-gl/webgl/context';
 import { Representation } from 'mol-geo/representation';
+import { createRenderTarget } from 'mol-gl/webgl/render-target';
+import Scene from 'mol-gl/scene';
 
 interface Viewer {
     center: (p: Vec3) => void
@@ -33,6 +35,8 @@ interface Viewer {
     draw: (force?: boolean) => void
     requestDraw: () => void
     animate: () => void
+    pick: () => void
+
     reprCount: BehaviorSubject<number>
     didDraw: BehaviorSubject<number>
 
@@ -40,6 +44,7 @@ interface Viewer {
     resetCamera: () => void
     downloadScreenshot: () => void
     getImageData: () => ImageData
+    getPickImageData: () => ImageData
 
     input: InputObserver
     stats: RendererStats
@@ -62,6 +67,9 @@ namespace Viewer {
         const reprMap = new Map<Representation<any>, Set<RenderObject>>()
         const reprCount = new BehaviorSubject(0)
 
+        const startTime = performance.now()
+        const didDraw = new BehaviorSubject(0)
+
         const input = InputObserver.create(canvas)
         input.resize.subscribe(handleResize)
 
@@ -86,17 +94,33 @@ namespace Viewer {
         }
         const ctx = createContext(gl)
 
+        const scene = Scene.create(ctx)
         const renderer = Renderer.create(ctx, camera)
 
+        const rtScale = 1 / 4
+        const renderTarget = createRenderTarget(ctx, Math.round(canvas.width * rtScale), Math.round(canvas.height * rtScale))
+
         let drawPending = false
         const prevProjectionView = Mat4.zero()
 
-        function draw (force?: boolean) {
+        function render(pick: boolean, force?: boolean) {
+            let didRender = false
             controls.update()
             camera.update()
             if (force || !Mat4.areEqual(camera.projectionView, prevProjectionView, EPSILON.Value)) {
                 Mat4.copy(prevProjectionView, camera.projectionView)
-                renderer.draw()
+                renderer.render(scene, pick)
+                didRender = true
+            }
+            return didRender
+        }
+
+        function draw(force?: boolean) {
+            ctx.unbindFramebuffer()
+            const viewport = { x: 0, y: 0, width: canvas.width, height: canvas.height }
+            renderer.setViewport(viewport)
+            if (render(false, force)) {
+                didDraw.next(performance.now() - startTime)
             }
             drawPending = false
         }
@@ -108,7 +132,7 @@ namespace Viewer {
         }
 
         function animate () {
-            draw()
+            draw(false)
             window.requestAnimationFrame(() => animate())
         }
 
@@ -133,11 +157,11 @@ namespace Viewer {
                 const newRO = new Set<RenderObject>()
                 repr.renderObjects.forEach(o => newRO.add(o))
                 if (oldRO) {
-                    SetUtils.difference(newRO, oldRO).forEach(o => renderer.add(o))
-                    SetUtils.difference(oldRO, newRO).forEach(o => renderer.remove(o))
-                    renderer.update()
+                    SetUtils.difference(newRO, oldRO).forEach(o => scene.add(o))
+                    SetUtils.difference(oldRO, newRO).forEach(o => scene.remove(o))
+                    scene.update()
                 } else {
-                    repr.renderObjects.forEach(o => renderer.add(o))
+                    repr.renderObjects.forEach(o => scene.add(o))
                 }
                 reprMap.set(repr, newRO)
                 reprCount.next(reprMap.size)
@@ -145,20 +169,24 @@ namespace Viewer {
             remove: (repr: Representation<any>) => {
                 const renderObjectSet = reprMap.get(repr)
                 if (renderObjectSet) {
-                    renderObjectSet.forEach(o => renderer.remove(o))
+                    renderObjectSet.forEach(o => scene.remove(o))
                     reprMap.delete(repr)
                     reprCount.next(reprMap.size)
                 }
             },
-            update: () => renderer.update(),
+            update: () => scene.update(),
             clear: () => {
                 reprMap.clear()
-                renderer.clear()
+                scene.clear()
             },
 
             draw,
             requestDraw,
             animate,
+            pick: () => {
+                renderTarget.bind()
+                render(true, true)
+            },
 
             handleResize,
             resetCamera: () => {
@@ -170,8 +198,11 @@ namespace Viewer {
             getImageData: () => {
                 return renderer.getImageData()
             },
+            getPickImageData: () => {
+                return renderTarget.getImageData()
+            },
             reprCount,
-            didDraw: renderer.didDraw,
+            didDraw,
 
             get input() {
                 return input
@@ -180,6 +211,7 @@ namespace Viewer {
                 return renderer.stats
             },
             dispose: () => {
+                scene.clear()
                 input.dispose()
                 controls.dispose()
                 renderer.dispose()
@@ -192,6 +224,7 @@ namespace Viewer {
             renderer.setViewport(viewport)
             Viewport.copy(camera.viewport, viewport)
             Viewport.copy(controls.viewport, viewport)
+            renderTarget.setSize(Math.round(canvas.width * rtScale), Math.round(canvas.height * rtScale))
         }
     }
 }