Skip to content
Snippets Groups Projects
Commit 612c5f78 authored by Alexander Rose's avatar Alexander Rose
Browse files

wip, refactoring

parent 05b5554e
No related branches found
No related tags found
No related merge requests found
Showing with 271 additions and 165 deletions
...@@ -8,15 +8,21 @@ import * as React from 'react' ...@@ -8,15 +8,21 @@ import * as React from 'react'
import State from '../state' import State from '../state'
export default class Viewport extends React.Component<{ state: State }, { initialized: boolean }> { 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 } state = { initialized: false }
componentDidMount() { 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() { 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> </div>
} }
} }
\ No newline at end of file
...@@ -9,7 +9,7 @@ import { BehaviorSubject } from 'rxjs'; ...@@ -9,7 +9,7 @@ import { BehaviorSubject } from 'rxjs';
// import { ValueCell } from 'mol-util/value-cell' // import { ValueCell } from 'mol-util/value-cell'
// import { Vec3, Mat4 } from 'mol-math/linear-algebra' // 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 { createColorTexture } from 'mol-gl/util';
// import Icosahedron from 'mol-geo/primitive/icosahedron' // import Icosahedron from 'mol-geo/primitive/icosahedron'
// import Box from 'mol-geo/primitive/box' // import Box from 'mol-geo/primitive/box'
...@@ -25,21 +25,21 @@ import { StructureRepresentation } from 'mol-geo/representation/structure'; ...@@ -25,21 +25,21 @@ import { StructureRepresentation } from 'mol-geo/representation/structure';
// import Cylinder from 'mol-geo/primitive/cylinder'; // import Cylinder from 'mol-geo/primitive/cylinder';
export default class State { export default class State {
renderer: Renderer viewer: Viewer
pdbId = '1crn' pdbId = '1crn'
initialized = new BehaviorSubject<boolean>(false) initialized = new BehaviorSubject<boolean>(false)
loading = new BehaviorSubject<boolean>(false) loading = new BehaviorSubject<boolean>(false)
async initRenderer (container: HTMLDivElement) { async initRenderer (canvas: HTMLCanvasElement, container: HTMLDivElement) {
this.renderer = Renderer.fromElement(container) this.viewer = Viewer.create(canvas, container)
this.initialized.next(true) this.initialized.next(true)
this.loadPdbId() this.loadPdbId()
this.renderer.frame() this.viewer.animate()
} }
async loadPdbId () { async loadPdbId () {
const { renderer, pdbId } = this const { viewer, pdbId } = this
renderer.clear() viewer.clear()
if (pdbId.length !== 4) return if (pdbId.length !== 4) return
this.loading.next(true) this.loading.next(true)
...@@ -49,11 +49,11 @@ export default class State { ...@@ -49,11 +49,11 @@ export default class State {
const structPointRepr = StructureRepresentation(Point) const structPointRepr = StructureRepresentation(Point)
await Run(structPointRepr.create(struct)) await Run(structPointRepr.create(struct))
structPointRepr.renderObjects.forEach(renderer.add) structPointRepr.renderObjects.forEach(viewer.add)
const structSpacefillRepr = StructureRepresentation(Spacefill) const structSpacefillRepr = StructureRepresentation(Spacefill)
await Run(structSpacefillRepr.create(struct)) await Run(structSpacefillRepr.create(struct))
structSpacefillRepr.renderObjects.forEach(renderer.add) structSpacefillRepr.renderObjects.forEach(viewer.add)
this.loading.next(false) this.loading.next(false)
} }
......
...@@ -8,7 +8,7 @@ import { ElementGroup, ElementSet, Structure, Unit } from 'mol-model/structure'; ...@@ -8,7 +8,7 @@ import { ElementGroup, ElementSet, Structure, Unit } from 'mol-model/structure';
import { EquivalenceClasses } from 'mol-data/util'; import { EquivalenceClasses } from 'mol-data/util';
import { OrderedSet } from 'mol-data/int' import { OrderedSet } from 'mol-data/int'
import { Task } from 'mol-task' import { Task } from 'mol-task'
import { RenderObject } from 'mol-gl/renderer'; import { RenderObject } from 'mol-gl/scene';
export interface RepresentationProps { export interface RepresentationProps {
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
import { ValueCell } from 'mol-util/value-cell' 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 { createColorTexture } from 'mol-gl/util';
import { Mat4 } from 'mol-math/linear-algebra' import { Mat4 } from 'mol-math/linear-algebra'
import { OrderedSet } from 'mol-data/int' import { OrderedSet } from 'mol-data/int'
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
import { ValueCell } from 'mol-util/value-cell' 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 { createColorTexture } from 'mol-gl/util';
import { Vec3, Mat4 } from 'mol-math/linear-algebra' import { Vec3, Mat4 } from 'mol-math/linear-algebra'
import { OrderedSet } from 'mol-data/int' import { OrderedSet } from 'mol-data/int'
......
...@@ -4,104 +4,24 @@ ...@@ -4,104 +4,24 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de> * @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 { Vec3, Mat4 } from 'mol-math/linear-algebra'
import { ValueCell } from 'mol-util'; import { Viewport } from 'mol-view/camera/util';
import TrackballControls from './controls/trackball'; import { Camera } from 'mol-view/camera/base';
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 }
}
export function createRenderObject(type: 'mesh' | 'point', data: PointRenderable.Data | MeshRenderable.Data, uniforms: { [k: string]: REGL.Uniform }) { import * as glContext from './context'
return { id: getNextId(), type, data, uniforms } import Scene, { RenderObject } from './scene';
}
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 Renderer { interface Renderer {
camera: PerspectiveCamera
controls: any // OrbitControls
add: (o: RenderObject) => void add: (o: RenderObject) => void
remove: (o: RenderObject) => void remove: (o: RenderObject) => void
clear: () => void clear: () => void
draw: () => void draw: () => void
frame: () => void
handleResize: () => void
}
function resizeCanvas (canvas: HTMLCanvasElement, element: HTMLElement) { setViewport: (viewport: Viewport) => void
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) { dispose: () => void
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)
// }
resize()
return create(canvas)
}
export function create(canvas: HTMLCanvasElement): Renderer {
const renderableList: Renderable[] = []
const objectIdRenderableMap: { [k: number]: Renderable } = {}
const extensions = [ const extensions = [
'OES_element_index_uint', 'OES_element_index_uint',
'ANGLE_instanced_arrays' 'ANGLE_instanced_arrays'
...@@ -110,17 +30,10 @@ namespace Renderer { ...@@ -110,17 +30,10 @@ namespace Renderer {
'EXT_disjoint_timer_query' 'EXT_disjoint_timer_query'
] ]
namespace Renderer {
export function create(canvas: HTMLCanvasElement, camera: Camera): Renderer {
const regl = glContext.create({ canvas, extensions, optionalExtensions, profile: true }) const regl = glContext.create({ canvas, extensions, optionalExtensions, profile: true })
const scene = Scene.create(regl)
const camera = PerspectiveCamera.create({
near: 0.01,
far: 10000,
position: Vec3.create(0, 0, 50)
})
const controls = TrackballControls.create(canvas, camera, {
})
const baseContext = regl({ const baseContext = regl({
context: { context: {
...@@ -142,68 +55,39 @@ namespace Renderer { ...@@ -142,68 +55,39 @@ namespace Renderer {
} }
}) })
const stats = Stats([])
let prevTime = regl.now()
const draw = () => { const draw = () => {
controls.update() regl.poll() // updates timers and viewport
// controls.copyInto(camera.position, camera.direction, camera.up)
camera.update() camera.update()
baseContext(state => { baseContext(state => {
regl.clear({ color: [0, 0, 0, 1] }) regl.clear({ color: [0, 0, 0, 1] })
// TODO painters sort, filter visible, filter picking, visibility culling? // TODO painters sort, filter visible, filter picking, visibility culling?
renderableList.forEach(r => { scene.forEach(r => {
r.draw() r.draw()
}) })
stats.update(state.time - prevTime)
prevTime = state.time
}) })
} }
window.addEventListener('resize', handleResize, false)
handleResize()
// TODO animate, draw, requestDraw // TODO animate, draw, requestDraw
return { return {
camera,
controls,
add: (o: RenderObject) => { add: (o: RenderObject) => {
const renderable = createRenderable(regl, o) scene.add(o)
renderableList.push(renderable)
objectIdRenderableMap[o.id] = renderable
stats.add(renderable)
draw() draw()
}, },
remove: (o: RenderObject) => { remove: (o: RenderObject) => {
if (o.id in objectIdRenderableMap) { scene.remove(o)
// TODO
// objectIdRenderableMap[o.id].destroy()
delete objectIdRenderableMap[o.id]
draw() draw()
}
}, },
clear: () => { clear: () => {
for (const id in objectIdRenderableMap) { scene.clear()
// TODO
// objectIdRenderableMap[id].destroy()
delete objectIdRenderableMap[id]
}
renderableList.length = 0
draw() draw()
}, },
draw, draw,
frame: () => { setViewport: (viewport: Viewport) => {
regl.frame((ctx) => draw()) regl({ viewport })
}, },
handleResize dispose: () => {
regl.destroy()
} }
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)
} }
} }
} }
......
/**
* 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
\ No newline at end of file
...@@ -99,6 +99,10 @@ export type PinchInput = { ...@@ -99,6 +99,10 @@ export type PinchInput = {
isStart: boolean isStart: boolean
} }
export type ResizeInput = {
}
const enum DraggingState { const enum DraggingState {
Stopped = 0, Stopped = 0,
Started = 1, Started = 1,
...@@ -120,6 +124,7 @@ interface InputObserver { ...@@ -120,6 +124,7 @@ interface InputObserver {
wheel: Subject<WheelInput>, wheel: Subject<WheelInput>,
pinch: Subject<PinchInput>, pinch: Subject<PinchInput>,
click: Subject<ClickInput>, click: Subject<ClickInput>,
resize: Subject<ResizeInput>,
dispose: () => void dispose: () => void
} }
...@@ -150,6 +155,7 @@ namespace InputObserver { ...@@ -150,6 +155,7 @@ namespace InputObserver {
const click = new Subject<ClickInput>() const click = new Subject<ClickInput>()
const wheel = new Subject<WheelInput>() const wheel = new Subject<WheelInput>()
const pinch = new Subject<PinchInput>() const pinch = new Subject<PinchInput>()
const resize = new Subject<ResizeInput>()
attach() attach()
...@@ -163,6 +169,7 @@ namespace InputObserver { ...@@ -163,6 +169,7 @@ namespace InputObserver {
wheel, wheel,
pinch, pinch,
click, click,
resize,
dispose dispose
} }
...@@ -185,6 +192,8 @@ namespace InputObserver { ...@@ -185,6 +192,8 @@ namespace InputObserver {
element.addEventListener('keyup', handleMods as EventListener) element.addEventListener('keyup', handleMods as EventListener)
element.addEventListener('keydown', handleMods as EventListener) element.addEventListener('keydown', handleMods as EventListener)
element.addEventListener('keypress', handleMods as EventListener) element.addEventListener('keypress', handleMods as EventListener)
window.addEventListener('resize', onResize, false)
} }
function dispose () { function dispose () {
...@@ -206,6 +215,8 @@ namespace InputObserver { ...@@ -206,6 +215,8 @@ namespace InputObserver {
element.removeEventListener('keyup', handleMods as EventListener) element.removeEventListener('keyup', handleMods as EventListener)
element.removeEventListener('keydown', handleMods as EventListener) element.removeEventListener('keydown', handleMods as EventListener)
element.removeEventListener('keypress', handleMods as EventListener) element.removeEventListener('keypress', handleMods as EventListener)
window.removeEventListener('resize', onResize, false)
} }
function preventDefault (ev: Event | Touch) { function preventDefault (ev: Event | Touch) {
...@@ -368,6 +379,10 @@ namespace InputObserver { ...@@ -368,6 +379,10 @@ namespace InputObserver {
} }
} }
function onResize (ev: Event) {
resize.next()
}
function insideBounds (pos: Vec2) { function insideBounds (pos: Vec2) {
if (element instanceof Window || element instanceof Document || element === document.body) { if (element instanceof Window || element instanceof Document || element === document.body) {
return true return true
......
File moved
File moved
...@@ -48,7 +48,7 @@ interface TrackballControls { ...@@ -48,7 +48,7 @@ interface TrackballControls {
} }
namespace 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 p = { ...DefaultTrackballControlsProps, ...props }
const viewport: Viewport = { x: 0, y: 0, width: 0, height: 0 } const viewport: Viewport = { x: 0, y: 0, width: 0, height: 0 }
...@@ -59,10 +59,9 @@ namespace TrackballControls { ...@@ -59,10 +59,9 @@ namespace TrackballControls {
let disposed = false let disposed = false
const input = InputObserver.create(element) const dragSub = input.drag.subscribe(onDrag)
input.drag.subscribe(onDrag) const wheelSub = input.wheel.subscribe(onWheel)
input.wheel.subscribe(onWheel) const pinchSub = input.pinch.subscribe(onPinch)
input.pinch.subscribe(onPinch)
// internals // internals
const target = Vec3.zero() const target = Vec3.zero()
...@@ -284,7 +283,10 @@ namespace TrackballControls { ...@@ -284,7 +283,10 @@ namespace TrackballControls {
function dispose() { function dispose() {
if (disposed) return if (disposed) return
disposed = true disposed = true
input.dispose()
dragSub.unsubscribe()
wheelSub.unsubscribe()
pinchSub.unsubscribe()
} }
// force an update at start // force an update at start
......
/**
* 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` })
}
\ No newline at end of file
/**
* 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
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment