UNPKG

marching

Version:

Marching.js is a JavaScript library that compiles GLSL ray marchers.

239 lines (205 loc) 7.29 kB
const vec3 = require('gl-vec3') const mat4 = require('gl-mat4') // camera adapted from https://github.com/shama/first-person-camera function FirstPersonCamera(opts) { if (!(this instanceof FirstPersonCamera)) return new FirstPersonCamera(opts) opts = opts || {} this.position = opts.position || vec3.create() this.rotation = opts.rotation || vec3.create() this.positionSpeed = opts.positionSpeed || -.5 this.rotationSpeed = opts.rotationSpeed || .01 } module.exports = FirstPersonCamera FirstPersonCamera.prototype.view = function(out) { if (!out) out = mat4.create() // altered x/y ordering from original mat4.rotateY(out, out, this.rotation[1]) mat4.rotateX(out, out, this.rotation[0]) mat4.rotateZ(out, out, this.rotation[2] - Math.PI) mat4.translate(out, out, [-this.position[0], -this.position[1], -this.position[2]]) return out } FirstPersonCamera.prototype.control = function(dt, move, mouse, prevMouse) { var speed = (this.positionSpeed / 1000) * dt var dir = [0,0,0] if (move[0]) dir[2] -= speed * (Marching.keys.Alt ? 4 : 1 ) else if (move[1]) dir[2] += speed * (Marching.keys.Alt ? 4 : 1 ) if (move[2]) dir[0] += speed * (Marching.keys.Alt ? 4 : 1 ) else if (move[3]) dir[0] -= speed * (Marching.keys.Alt ? 4 : 1 ) if (move[4]) dir[1] -= speed * (Marching.keys.Alt ? 4 : 1 ) else if (move[5]) dir[1] += speed * (Marching.keys.Alt ? 4 : 1 ) this.move(dir) // just use arrow keys instead of mouse // this.pointer(mouse, prevMouse) } FirstPersonCamera.prototype.move = function(dir) { if (dir[0] !== 0 || dir[1] !== 0 || dir[2] !== 0) { var cam = mat4.create() mat4.rotateY(cam, cam, this.rotation[1]) mat4.rotateX(cam, cam, this.rotation[0]) vec3.transformMat4(dir, dir, cam) vec3.add(this.position, this.position, dir) this.parent.pos.dirty = true } } //FirstPersonCamera.prototype.pointer = function(da, db) { // var dt = [da[0] - db[0], da[1]- db[1]] // var rot = this.rotation // rot[1] -= dt[0] * this.rotationSpeed // if (rot[1] < 0) rot[1] += Math.PI * 2 // if (rot[1] >= Math.PI * 2) rot[1] -= Math.PI * 2 // rot[0] -= dt[1] * this.rotationSpeed // if (rot[0] < -Math.PI * .5) rot[0] = -Math.PI*0.5 // if (rot[0] > Math.PI * .5) rot[0] = Math.PI*0.5 //} const Camera = { init( gl, program, handler ) { const camera = FirstPersonCamera({ fov: 190, near:.01, far:10, direction:[0,0,1], viewport:[1,1,1,-1] }) camera.rotation = [0,Math.PI,Math.PI] Camera.__camera = camera camera.parent = this const camera_pos = gl.getUniformLocation( program, 'camera_pos' ) const camera_normal = gl.getUniformLocation( program, 'camera_normal' ) const camera_rot = gl.getUniformLocation( program, 'camera_rot' ) const ucamera = gl.getUniformLocation( program, 'camera' ) this.pos = { dirty:false } this.dir = { dirty:true } this.__rot = { dirty:true, value:0 } Object.defineProperty( this, 'rotation', { configurable:true, get() { return this.__rot.value }, set(v) { this.__rot.value = v this.__rot.dirty = true } }) let px = 0, py =0, pz = 5, nx = 0, ny = 0, nz = 0 Object.defineProperties( this.pos, { x: { get() { return px }, set(v) { px = camera.position[0] = v;this.dirty = true; } }, y: { get() { return py }, set(v) { py = camera.position[1] = v; this.dirty = true; } }, z: { get() { return pz }, set(v) { pz = camera.position[2] = v; this.dirty = true; } }, }) Object.defineProperties( this.dir, { x: { get() { return nx }, set(v) { nx = camera.rotation[0] = v; this.dirty = true; } }, y: { get() { return ny }, set(v) { ny = camera.rotation[1] = v; this.dirty = true; } }, z: { get() { return nz }, set(v) { nz = camera.rotation[2] = v; this.dirty = true; } }, }) let init = false gl.uniform3f( camera_normal, this.dir.x, this.dir.y, this.dir.z ) camera.position = [this.pos.x, this.pos.y, this.pos.z ] //camera.update() gl.uniform3f( camera_pos, this.pos.x, this.pos.y, this.pos.z ) gl.uniformMatrix4fv( ucamera, false, camera.view() ) gl.uniform1f( camera_rot, this.rot ) Camera.move = (x,y,z) => { // XXX does this need to update property values? camera.move([x,y,z]) Camera.update() } Camera.moveTo = (x,y,z) => { Camera.pos.x = x Camera.pos.y = y Camera.pos.z = z } Camera.update = ()=> { const pos = camera.position gl.uniform3f( camera_pos, pos[0], pos[1], pos[2] ) gl.uniformMatrix4fv( ucamera, false, camera.view() ) } // determine an offset from the current camera position based // on the current camera rotation e.g. to always position a light // behind the camera. Camera.offset = (amt=[0,0,3]) => { const cam = mat4.create() mat4.rotateY(cam, cam, camera.rotation[1]) mat4.rotateX(cam, cam, camera.rotation[0]) vec3.transformMat4(amt, amt, cam) return amt } let prvx = 0, prvy = 0, x = 0, y = 0 Camera.__mousemovefnc = e => { prvx = x prvy = y x = e.pageX y = e.pageY } let prevTime = 0 let k = Marching.keys Camera.__framefnc = t => { if( k.ArrowLeft ) camera.rotation[1] += camera.rotationSpeed if( k.ArrowRight ) camera.rotation[1] -= camera.rotationSpeed if( k.ArrowUp && !k.Shift ) camera.rotation[0] -= camera.rotationSpeed if( k.ArrowDown && !k.Shift) camera.rotation[0] += camera.rotationSpeed if( Marching.cameraEnabled ) { camera.control( t*1000 - prevTime, [k.w,k.s,k.d,k.a,k.ArrowUp && k.Shift, k.ArrowDown && k.Shift], [x,y], [prvx,prvy] ) Camera.update() prvx = x prvy = y prevTime = t*1000 } } Camera.__mousemove = null Camera.on = ()=> { if( Camera.__mousemove === null ) { window.addEventListener( 'mousemove', Camera.__mousemovefnc ) Camera.__mousemove = true } if( Marching.callbacks.indexOf( Camera.__framefnc ) === -1 ) { Marching.callbacks.push( Camera.__framefnc ) } } handler( ()=> { if( this.pos.dirty === true ) { //camera.position = [this.pos.x, this.pos.y, this.pos.z ] //camera.position = [this.pos.x, this.pos.y, this.pos.z ] //camera.update() const pos = camera.position gl.uniform3f( camera_pos, pos[0], pos[1], pos[2] ) gl.uniformMatrix4fv( ucamera, false, camera.view() ) this.pos.dirty = false } // XXX this is broken and needs to be fixed if( this.dir.dirty === true ) { gl.uniform3f( camera_normal, this.dir.x, this.dir.y, this.dir.z ) gl.uniformMatrix4fv( ucamera, false, camera.view() ) this.dir.dirty = false } if( this.__rot.dirty === true ) { gl.uniform1f( camera_rot, this.__rot.value ) this.__rot.dirty = false } if( typeof this.onmove === 'function' ) { this.onmove( this ) } }) } } module.exports = Camera