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

wip, regl camera

parent d129f139
No related branches found
No related tags found
No related merge requests found
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
/*
* This code has been modified from https://github.com/regl-project/regl-camera,
* copyright (c) 2016 Mikola Lysenko. MIT License
*/
const isBrowser = typeof window !== 'undefined'
import REGL = require('regl');
import mouseChange, { MouseModifiers } from 'mol-util/mouse-change'
import mouseWheel from 'mol-util/mouse-wheel'
import { Mat4, Vec3 } from 'mol-math/linear-algebra/3d'
import { clamp, damp } from 'mol-math/interpolate'
export interface CameraUniforms {
projection: Mat4,
}
export interface CameraState {
center: Vec3,
theta: number,
phi: number,
distance: number,
eye: Vec3,
up: Vec3,
fovy: number,
near: number,
far: number,
noScroll: boolean,
flipY: boolean,
dtheta: number,
dphi: number,
rotationSpeed: number,
zoomSpeed: number,
renderOnDirty: boolean,
damping: number,
minDistance: number,
maxDistance: number,
}
export interface Camera {
update: (props: any, block: any) => void,
setState: (newState: CameraState) => void,
isDirty: () => boolean
}
export namespace Camera {
export function create (regl: REGL.Regl, element: HTMLElement, props: Partial<CameraState> = {}): Camera {
const state: CameraState = {
center: props.center || Vec3.zero(),
theta: props.theta || 0,
phi: props.phi || 0,
distance: Math.log(props.distance || 10.0),
eye: Vec3.zero(),
up: props.up || Vec3.create(0, 1, 0),
fovy: props.fovy || Math.PI / 4.0,
near: typeof props.near !== 'undefined' ? props.near : 0.01,
far: typeof props.far !== 'undefined' ? props.far : 1000.0,
noScroll: typeof props.noScroll !== 'undefined' ? props.noScroll : false,
flipY: !!props.flipY,
dtheta: 0,
dphi: 0,
rotationSpeed: typeof props.rotationSpeed !== 'undefined' ? props.rotationSpeed : 1,
zoomSpeed: typeof props.zoomSpeed !== 'undefined' ? props.zoomSpeed : 1,
renderOnDirty: typeof props.renderOnDirty !== undefined ? !!props.renderOnDirty : false,
damping: typeof props.damping !== 'undefined' ? props.damping : 0.9,
minDistance: Math.log(typeof props.minDistance !== 'undefined' ? props.minDistance : 0.1),
maxDistance: Math.log(typeof props.maxDistance !== 'undefined' ? props.maxDistance : 1000)
}
const view = Mat4.identity()
const projection = Mat4.identity()
const right = Vec3.create(1, 0, 0)
const front = Vec3.create(0, 0, 1)
let dirty = false
let ddistance = 0
let prevX = 0
let prevY = 0
if (isBrowser) {
const source = element || regl._gl.canvas
const getWidth = function () {
return element ? element.offsetWidth : window.innerWidth
}
const getHeight = function () {
return element ? element.offsetHeight : window.innerHeight
}
mouseChange(source, function (buttons: number, x: number, y: number, mods: MouseModifiers) {
if (buttons & 1) {
const dx = (x - prevX) / getWidth()
const dy = (y - prevY) / getHeight()
state.dtheta += state.rotationSpeed * 4.0 * dx
state.dphi += state.rotationSpeed * 4.0 * dy
dirty = true;
}
prevX = x
prevY = y
})
mouseWheel(source, function (dx: number, dy: number) {
ddistance += dy / getHeight() * state.zoomSpeed
dirty = true;
}, state.noScroll)
}
function dampAndMarkDirty (x: number) {
const xd = damp(x, state.damping)
if (Math.abs(xd) < 0.1) return 0
dirty = true;
return xd
}
function setState (newState: Partial<CameraState> = {}) {
Object.assign(state, newState)
const { center, eye, up, dtheta, dphi } = state
state.theta += dtheta
state.phi = clamp(state.phi + dphi, -Math.PI / 2.0, Math.PI / 2.0)
state.distance = clamp(state.distance + ddistance, state.minDistance, state.maxDistance)
state.dtheta = dampAndMarkDirty(dtheta)
state.dphi = dampAndMarkDirty(dphi)
ddistance = dampAndMarkDirty(ddistance)
const theta = state.theta
const phi = state.phi
const r = Math.exp(state.distance)
const vf = r * Math.sin(theta) * Math.cos(phi)
const vr = r * Math.cos(theta) * Math.cos(phi)
const vu = r * Math.sin(phi)
for (let i = 0; i < 3; ++i) {
eye[i] = center[i] + vf * front[i] + vr * right[i] + vu * up[i]
}
Mat4.lookAt(view, eye, center, up)
}
const injectContext = regl({
context: {
view: () => view,
dirty: () => dirty,
projection: (context: REGL.DefaultContext) => {
Mat4.perspective(
projection,
state.fovy,
context.viewportWidth / context.viewportHeight,
state.near,
state.far
)
if (state.flipY) { projection[5] *= -1 }
return projection
}
},
uniforms: { // TODO
view: regl.context('view' as any),
projection: regl.context('projection' as any)
}
})
function update (props: any, block: any) {
setState()
injectContext(props, block)
if (dirty) {
console.log(view)
}
dirty = false
}
return {
update,
setState,
isDirty: () => dirty
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment