Bladeren bron

wip, refactoring

Alexander Rose 7 jaren geleden
bovenliggende
commit
612c5f78af

+ 9 - 3
src/apps/render-test/components/viewport.tsx

@@ -8,15 +8,21 @@ import * as React from 'react'
 import State from '../state'
 
 export default class Viewport extends React.Component<{ state: State }, { initialized: boolean }> {
-    private canvasContainer: HTMLDivElement | null = null;
+    private container: HTMLDivElement | null = null;
+    private canvas: HTMLCanvasElement | null = null;
     state = { initialized: false }
 
     componentDidMount() {
-        if (this.canvasContainer) this.props.state.initRenderer(this.canvasContainer).then(() => this.setState({ initialized: true }))
+        if (this.container && this.canvas) {
+            this.props.state.initRenderer(this.canvas, this.container).then(() => {
+                this.setState({ initialized: true })
+            })
+        }
     }
 
     render() {
-        return <div ref={elm => this.canvasContainer = elm} style={{ height: '100%' }}>
+        return <div ref={elm => this.container = elm} style={{ height: '100%' }}>
+            <canvas ref={elm => this.canvas = elm}></canvas>
         </div>
     }
 }

+ 9 - 9
src/apps/render-test/state.ts

@@ -9,7 +9,7 @@ import { BehaviorSubject } from 'rxjs';
 // import { ValueCell } from 'mol-util/value-cell'
 
 // import { Vec3, Mat4 } from 'mol-math/linear-algebra'
-import Renderer from 'mol-gl/renderer'
+import Viewer from 'mol-view/viewer'
 // import { createColorTexture } from 'mol-gl/util';
 // import Icosahedron from 'mol-geo/primitive/icosahedron'
 // import Box from 'mol-geo/primitive/box'
@@ -25,21 +25,21 @@ import { StructureRepresentation } from 'mol-geo/representation/structure';
 // import Cylinder from 'mol-geo/primitive/cylinder';
 
 export default class State {
-    renderer: Renderer
+    viewer: Viewer
     pdbId = '1crn'
     initialized = new BehaviorSubject<boolean>(false)
     loading = new BehaviorSubject<boolean>(false)
 
-    async initRenderer (container: HTMLDivElement) {
-        this.renderer = Renderer.fromElement(container)
+    async initRenderer (canvas: HTMLCanvasElement, container: HTMLDivElement) {
+        this.viewer = Viewer.create(canvas, container)
         this.initialized.next(true)
         this.loadPdbId()
-        this.renderer.frame()
+        this.viewer.animate()
     }
 
     async loadPdbId () {
-        const { renderer, pdbId } = this
-        renderer.clear()
+        const { viewer, pdbId } = this
+        viewer.clear()
 
         if (pdbId.length !== 4) return
         this.loading.next(true)
@@ -49,11 +49,11 @@ export default class State {
 
         const structPointRepr = StructureRepresentation(Point)
         await Run(structPointRepr.create(struct))
-        structPointRepr.renderObjects.forEach(renderer.add)
+        structPointRepr.renderObjects.forEach(viewer.add)
 
         const structSpacefillRepr = StructureRepresentation(Spacefill)
         await Run(structSpacefillRepr.create(struct))
-        structSpacefillRepr.renderObjects.forEach(renderer.add)
+        structSpacefillRepr.renderObjects.forEach(viewer.add)
 
         this.loading.next(false)
     }

+ 1 - 1
src/mol-geo/representation/structure/index.ts

@@ -8,7 +8,7 @@ import { ElementGroup, ElementSet, Structure, Unit } from 'mol-model/structure';
 import { EquivalenceClasses } from 'mol-data/util';
 import { OrderedSet } from 'mol-data/int'
 import { Task } from 'mol-task'
-import { RenderObject } from 'mol-gl/renderer';
+import { RenderObject } from 'mol-gl/scene';
 
 export interface RepresentationProps {
 

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

@@ -6,7 +6,7 @@
 
 import { ValueCell } from 'mol-util/value-cell'
 
-import { createRenderObject, RenderObject } from 'mol-gl/renderer'
+import { createRenderObject, RenderObject } from 'mol-gl/scene'
 import { createColorTexture } from 'mol-gl/util';
 import { Mat4 } from 'mol-math/linear-algebra'
 import { OrderedSet } from 'mol-data/int'

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

@@ -6,7 +6,7 @@
 
 import { ValueCell } from 'mol-util/value-cell'
 
-import { createRenderObject, RenderObject } from 'mol-gl/renderer'
+import { createRenderObject, RenderObject } from 'mol-gl/scene'
 import { createColorTexture } from 'mol-gl/util';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra'
 import { OrderedSet } from 'mol-data/int'

+ 28 - 144
src/mol-gl/renderer.ts

@@ -4,123 +4,36 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import REGL = require('regl');
-import * as glContext from './context'
-import { PerspectiveCamera } from './camera/perspective'
-import { PointRenderable, MeshRenderable, Renderable } from './renderable'
-import Stats from './stats'
-
 import { Vec3, Mat4 } from 'mol-math/linear-algebra'
-import { ValueCell } from 'mol-util';
-import TrackballControls from './controls/trackball';
-import { Viewport } from './camera/util';
-
-let _renderObjectId = 0;
-function getNextId() {
-    return _renderObjectId++ % 0x7FFFFFFF;
-}
-
-
-
-export interface RenderUpdateInfo {
-
-}
-
-export type RenderData = { [k: string]: ValueCell<Helpers.TypedArray> }
-
-export interface RenderObject {
-    id: number
-    type: 'mesh' | 'point'
-    data: PointRenderable.Data | MeshRenderable.Data
-    uniforms: { [k: string]: REGL.Uniform }
-}
+import { Viewport } from 'mol-view/camera/util';
+import { Camera } from 'mol-view/camera/base';
 
-export function createRenderObject(type: 'mesh' | 'point', data: PointRenderable.Data | MeshRenderable.Data, uniforms: { [k: string]: REGL.Uniform }) {
-    return { id: getNextId(), type, data, uniforms }
-}
-
-export function createRenderable(regl: REGL.Regl, o: RenderObject) {
-    switch (o.type) {
-        case 'mesh': return MeshRenderable.create(regl, o.data as MeshRenderable.Data, o.uniforms || {})
-        case 'point': return PointRenderable.create(regl, o.data as PointRenderable.Data)
-    }
-}
+import * as glContext from './context'
+import Scene, { RenderObject } from './scene';
 
 interface Renderer {
-    camera: PerspectiveCamera
-    controls: any // OrbitControls
-
     add: (o: RenderObject) => void
     remove: (o: RenderObject) => void
     clear: () => void
     draw: () => void
-    frame: () => void
-    handleResize: () => void
-}
-
-function resizeCanvas (canvas: HTMLCanvasElement, element: HTMLElement) {
-    let w = window.innerWidth
-    let h = window.innerHeight
-    if (element !== document.body) {
-        let bounds = element.getBoundingClientRect()
-        w = bounds.right - bounds.left
-        h = bounds.bottom - bounds.top
-    }
-    canvas.width = window.devicePixelRatio * w
-    canvas.height = window.devicePixelRatio * h
-    Object.assign(canvas.style, { width: `${w}px`, height: `${h}px` })
-}
-
-namespace Renderer {
-    export function fromElement(element: HTMLElement, contexAttributes?: WebGLContextAttributes) {
-        const canvas = document.createElement('canvas')
-        Object.assign(canvas.style, { border: 0, margin: 0, padding: 0, top: 0, left: 0 })
-        element.appendChild(canvas)
-
-        if (element === document.body) {
-            canvas.style.position = 'absolute'
-            Object.assign(element.style, { margin: 0, padding: 0 })
-        }
-
-        function resize () {
-            resizeCanvas(canvas, element)
-        }
-
-        window.addEventListener('resize', resize, false)
 
-        // function onDestroy () {
-        //     window.removeEventListener('resize', resize)
-        //     element.removeChild(canvas)
-        // }
+    setViewport: (viewport: Viewport) => void
 
-        resize()
-
-        return create(canvas)
-    }
-
-    export function create(canvas: HTMLCanvasElement): Renderer {
-        const renderableList: Renderable[] = []
-        const objectIdRenderableMap: { [k: number]: Renderable } = {}
+    dispose: () => void
+}
 
-        const extensions = [
-            'OES_element_index_uint',
-            'ANGLE_instanced_arrays'
-        ]
-        const optionalExtensions = [
-            'EXT_disjoint_timer_query'
-        ]
+const extensions = [
+    'OES_element_index_uint',
+    'ANGLE_instanced_arrays'
+]
+const optionalExtensions = [
+    'EXT_disjoint_timer_query'
+]
 
+namespace Renderer {
+    export function create(canvas: HTMLCanvasElement, camera: Camera): Renderer {
         const regl = glContext.create({ canvas, extensions, optionalExtensions, profile: true })
-
-        const camera = PerspectiveCamera.create({
-            near: 0.01,
-            far: 10000,
-            position: Vec3.create(0, 0, 50)
-        })
-
-        const controls = TrackballControls.create(canvas, camera, {
-
-        })
+        const scene = Scene.create(regl)
 
         const baseContext = regl({
             context: {
@@ -142,68 +55,39 @@ namespace Renderer {
             }
         })
 
-        const stats = Stats([])
-        let prevTime = regl.now()
-
         const draw = () => {
-            controls.update()
-            // controls.copyInto(camera.position, camera.direction, camera.up)
+            regl.poll() // updates timers and viewport
             camera.update()
             baseContext(state => {
                 regl.clear({ color: [0, 0, 0, 1] })
                 // TODO painters sort, filter visible, filter picking, visibility culling?
-                renderableList.forEach(r => {
+                scene.forEach(r => {
                     r.draw()
                 })
-                stats.update(state.time - prevTime)
-                prevTime = state.time
             })
         }
 
-        window.addEventListener('resize', handleResize, false)
-        handleResize()
-
         // TODO animate, draw, requestDraw
         return {
-            camera,
-            controls,
-
             add: (o: RenderObject) => {
-                const renderable = createRenderable(regl, o)
-                renderableList.push(renderable)
-                objectIdRenderableMap[o.id] = renderable
-                stats.add(renderable)
+                scene.add(o)
                 draw()
             },
             remove: (o: RenderObject) => {
-                if (o.id in objectIdRenderableMap) {
-                    // TODO
-                    // objectIdRenderableMap[o.id].destroy()
-                    delete objectIdRenderableMap[o.id]
-                    draw()
-                }
+                scene.remove(o)
+                draw()
             },
             clear: () => {
-                for (const id in objectIdRenderableMap) {
-                    // TODO
-                    // objectIdRenderableMap[id].destroy()
-                    delete objectIdRenderableMap[id]
-                }
-                renderableList.length = 0
+                scene.clear()
                 draw()
             },
             draw,
-            frame: () => {
-                regl.frame((ctx) => draw())
+            setViewport: (viewport: Viewport) => {
+                regl({ viewport })
             },
-            handleResize
-        }
-
-        function handleResize() {
-            const viewport = { x: 0, y: 0, width: canvas.width, height: canvas.height }
-            regl({ viewport })
-            Viewport.copy(camera.viewport, viewport)
-            Viewport.copy(controls.viewport, viewport)
+            dispose: () => {
+                regl.destroy()
+            }
         }
     }
 }

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

@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import REGL = require('regl');
+import { PointRenderable, MeshRenderable, Renderable } from './renderable'
+
+import { ValueCell } from 'mol-util';
+
+let _renderObjectId = 0;
+function getNextId() {
+    return _renderObjectId++ % 0x7FFFFFFF;
+}
+
+export type RenderData = { [k: string]: ValueCell<Helpers.TypedArray> }
+
+export interface RenderObject {
+    id: number
+    type: 'mesh' | 'point'
+    data: PointRenderable.Data | MeshRenderable.Data
+    uniforms: { [k: string]: REGL.Uniform }
+}
+
+export function createRenderObject(type: 'mesh' | 'point', data: PointRenderable.Data | MeshRenderable.Data, uniforms: { [k: string]: REGL.Uniform }) {
+    return { id: getNextId(), type, data, uniforms }
+}
+
+export function createRenderable(regl: REGL.Regl, o: RenderObject) {
+    switch (o.type) {
+        case 'mesh': return MeshRenderable.create(regl, o.data as MeshRenderable.Data, o.uniforms || {})
+        case 'point': return PointRenderable.create(regl, o.data as PointRenderable.Data)
+    }
+}
+
+interface Scene {
+    add: (o: RenderObject) => void
+    remove: (o: RenderObject) => void
+    clear: () => void
+    forEach: (callbackFn: (value: Renderable) => void) => void
+}
+
+namespace Scene {
+    export function create(regl: REGL.Regl): Scene {
+        const renderableList: Renderable[] = []
+        const objectIdRenderableMap: { [k: number]: Renderable } = {}
+
+        return {
+            add: (o: RenderObject) => {
+                const renderable = createRenderable(regl, o)
+                renderableList.push(renderable)
+                objectIdRenderableMap[o.id] = renderable
+            },
+            remove: (o: RenderObject) => {
+                if (o.id in objectIdRenderableMap) {
+                    // TODO
+                    // objectIdRenderableMap[o.id].destroy()
+                    delete objectIdRenderableMap[o.id]
+                }
+            },
+            clear: () => {
+                for (const id in objectIdRenderableMap) {
+                    // TODO
+                    // objectIdRenderableMap[id].destroy()
+                    delete objectIdRenderableMap[id]
+                }
+                renderableList.length = 0
+            },
+            forEach: (callbackFn: (value: Renderable) => void) => {
+                renderableList.forEach(callbackFn)
+            }
+        }
+    }
+}
+
+export default Scene

+ 15 - 0
src/mol-util/input/input-observer.ts

@@ -99,6 +99,10 @@ export type PinchInput = {
     isStart: boolean
 }
 
+export type ResizeInput = {
+
+}
+
 const enum DraggingState {
     Stopped = 0,
     Started = 1,
@@ -120,6 +124,7 @@ interface InputObserver {
     wheel: Subject<WheelInput>,
     pinch: Subject<PinchInput>,
     click: Subject<ClickInput>,
+    resize: Subject<ResizeInput>,
 
     dispose: () => void
 }
@@ -150,6 +155,7 @@ namespace InputObserver {
         const click = new Subject<ClickInput>()
         const wheel = new Subject<WheelInput>()
         const pinch = new Subject<PinchInput>()
+        const resize = new Subject<ResizeInput>()
 
         attach()
 
@@ -163,6 +169,7 @@ namespace InputObserver {
             wheel,
             pinch,
             click,
+            resize,
 
             dispose
         }
@@ -185,6 +192,8 @@ namespace InputObserver {
             element.addEventListener('keyup', handleMods as EventListener)
             element.addEventListener('keydown', handleMods as EventListener)
             element.addEventListener('keypress', handleMods as EventListener)
+
+            window.addEventListener('resize', onResize, false)
         }
 
         function dispose () {
@@ -206,6 +215,8 @@ namespace InputObserver {
             element.removeEventListener('keyup', handleMods as EventListener)
             element.removeEventListener('keydown', handleMods as EventListener)
             element.removeEventListener('keypress', handleMods as EventListener)
+
+            window.removeEventListener('resize', onResize, false)
         }
 
         function preventDefault (ev: Event | Touch) {
@@ -368,6 +379,10 @@ namespace InputObserver {
             }
         }
 
+        function onResize (ev: Event) {
+            resize.next()
+        }
+
         function insideBounds (pos: Vec2) {
             if (element instanceof Window || element instanceof Document || element === document.body) {
                 return true

+ 0 - 0
src/mol-gl/camera/base.ts → src/mol-view/camera/base.ts


+ 0 - 0
src/mol-gl/camera/orthographic.ts → src/mol-view/camera/orthographic.ts


+ 0 - 0
src/mol-gl/camera/perspective.ts → src/mol-view/camera/perspective.ts


+ 0 - 0
src/mol-gl/camera/util.ts → src/mol-view/camera/util.ts


+ 8 - 6
src/mol-gl/controls/trackball.ts → src/mol-view/controls/trackball.ts

@@ -48,7 +48,7 @@ interface TrackballControls {
 }
 
 namespace TrackballControls {
-    export function create (element: Element, object: Object, props: TrackballControlsProps = {}): TrackballControls {
+    export function create (input: InputObserver, object: Object, props: TrackballControlsProps = {}): TrackballControls {
         const p = { ...DefaultTrackballControlsProps, ...props }
 
         const viewport: Viewport = { x: 0, y: 0, width: 0, height: 0 }
@@ -59,10 +59,9 @@ namespace TrackballControls {
 
         let disposed = false
 
-        const input = InputObserver.create(element)
-        input.drag.subscribe(onDrag)
-        input.wheel.subscribe(onWheel)
-        input.pinch.subscribe(onPinch)
+        const dragSub = input.drag.subscribe(onDrag)
+        const wheelSub = input.wheel.subscribe(onWheel)
+        const pinchSub = input.pinch.subscribe(onPinch)
 
         // internals
         const target = Vec3.zero()
@@ -284,7 +283,10 @@ namespace TrackballControls {
         function dispose() {
             if (disposed) return
             disposed = true
-            input.dispose()
+
+            dragSub.unsubscribe()
+            wheelSub.unsubscribe()
+            pinchSub.unsubscribe()
         }
 
         // force an update at start

+ 18 - 0
src/mol-view/util.ts

@@ -0,0 +1,18 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+export function resizeCanvas (canvas: HTMLCanvasElement, container: Element) {
+    let w = window.innerWidth
+    let h = window.innerHeight
+    if (container !== document.body) {
+        let bounds = container.getBoundingClientRect()
+        w = bounds.right - bounds.left
+        h = bounds.bottom - bounds.top
+    }
+    canvas.width = window.devicePixelRatio * w
+    canvas.height = window.devicePixelRatio * h
+    Object.assign(canvas.style, { width: `${w}px`, height: `${h}px` })
+}

+ 104 - 0
src/mol-view/viewer.ts

@@ -0,0 +1,104 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Vec3 } from 'mol-math/linear-algebra'
+import InputObserver from 'mol-util/input/input-observer'
+import Renderer from 'mol-gl/renderer'
+import { RenderObject } from 'mol-gl/scene'
+
+import TrackballControls from './controls/trackball'
+import { Viewport } from './camera/util'
+import { PerspectiveCamera } from './camera/perspective'
+import { resizeCanvas } from './util';
+
+interface Viewer {
+    add: (o: RenderObject) => void
+    remove: (o: RenderObject) => void
+    clear: () => void
+    draw: () => void
+
+    requestDraw: () => void
+    animate: () => void
+
+    handleResize: () => void
+
+    dispose: () => void
+}
+
+namespace Viewer {
+    export function create(canvas: HTMLCanvasElement, container: Element): Viewer {
+        const input = InputObserver.create(canvas)
+        input.resize.subscribe(handleResize)
+
+        const camera = PerspectiveCamera.create({
+            near: 0.01,
+            far: 10000,
+            position: Vec3.create(0, 0, 50)
+        })
+
+        const controls = TrackballControls.create(input, camera, {
+
+        })
+
+        const renderer = Renderer.create(canvas, camera)
+
+        let drawPending = false
+
+        function draw () {
+
+            controls.update()
+            camera.update()
+            renderer.draw()
+        }
+
+        function requestDraw () {
+            if (drawPending) return
+            drawPending = true
+            window.requestAnimationFrame(draw)
+        }
+
+        function animate () {
+            draw()
+            window.requestAnimationFrame(animate)
+        }
+
+        handleResize()
+
+        return {
+            add: (o: RenderObject) => {
+                renderer.add(o)
+            },
+            remove: (o: RenderObject) => {
+                renderer.remove(o)
+            },
+            clear: () => {
+                renderer.clear()
+            },
+
+            draw,
+            requestDraw,
+            animate,
+
+            handleResize,
+
+            dispose: () => {
+                input.dispose()
+                controls.dispose()
+                renderer.dispose()
+            }
+        }
+
+        function handleResize() {
+            resizeCanvas(canvas, container)
+            const viewport = { x: 0, y: 0, width: canvas.width, height: canvas.height }
+            renderer.setViewport(viewport)
+            Viewport.copy(camera.viewport, viewport)
+            Viewport.copy(controls.viewport, viewport)
+        }
+    }
+}
+
+export default Viewer