summaryrefslogtreecommitdiff
path: root/assets/javascripts/mx/extensions
diff options
context:
space:
mode:
authorJulie Lala <jules@okfoc.us>2014-04-17 10:17:09 -0400
committerJulie Lala <jules@okfoc.us>2014-04-17 10:17:09 -0400
commit6005be1d04dd02000889d8be2faaf62969c4421f (patch)
tree12c2151b606188bd02b656689928035c0561bf36 /assets/javascripts/mx/extensions
stowing UI stuff in empty branchui-mocks
Diffstat (limited to 'assets/javascripts/mx/extensions')
-rw-r--r--assets/javascripts/mx/extensions/mx.movements.js160
-rw-r--r--assets/javascripts/mx/extensions/mx.rotationControl.js266
-rw-r--r--assets/javascripts/mx/extensions/mx.scene.js161
3 files changed, 587 insertions, 0 deletions
diff --git a/assets/javascripts/mx/extensions/mx.movements.js b/assets/javascripts/mx/extensions/mx.movements.js
new file mode 100644
index 0000000..691ada7
--- /dev/null
+++ b/assets/javascripts/mx/extensions/mx.movements.js
@@ -0,0 +1,160 @@
+
+
+MX.Movements = function (cam, viewHeight, minimap) {
+
+ var moveForward,
+ moveLeft,
+ moveBackward,
+ moveRight,
+ turnLeft,
+ turnRight,
+ jumping = false
+
+ var v = 25,
+ vr = Math.PI * 0.015
+ jumpV = 30,
+ vx = vy = vz = 0
+
+ return {
+
+ init: function () {
+
+ document.addEventListener('keydown', function (e) {
+ $(".edit-image.menu,.edit-video.menu").hide()
+
+ switch ( e.keyCode ) {
+ case 38: // up
+ case 87: // w
+ moveForward = true
+ break
+
+ case 37: // left
+ case 65: // a
+ turnLeft = true
+ break
+
+ case 40: // down
+ case 83: // s
+ moveBackward = true
+ break
+
+ case 39: // right
+ case 68: // d
+ turnRight = true
+ break
+
+ case 32: // space
+ if (!jumping) vy += jumpV
+ jumping = true
+ break
+ }
+ })
+
+ document.addEventListener('keyup', function (e) {
+ $(".edit-image.menu,.edit-video.menu").hide()
+
+ switch ( e.keyCode ) {
+ case 38: // up
+ case 87: // w
+ moveForward = false
+ break
+
+ case 37: // left
+ case 65: // a
+ turnLeft = false
+ break
+
+ case 40: // down
+ case 83: // s
+ moveBackward = false
+ break
+
+ case 39: // right
+ case 68: // d
+ turnRight = false
+ break
+ }
+ })
+
+ var mouseX, mouseY, dx, dy, rotX, rotY, dragging = false
+ document.addEventListener('mousedown', function (e) {
+ $(".edit-image.menu,.edit-video.menu").hide()
+
+ mouseX = e.pageX
+ mouseY = e.pageY
+ rotX = cam.rotationX
+ rotY = cam.rotationY
+ dragging = true
+ })
+ document.addEventListener('mousemove', function (e) {
+ if (! dragging || app.dragging) return
+ var dx = (e.pageX - mouseX) / window.innerWidth * Math.PI/3
+ var dy = (e.pageY - mouseY) / window.innerHeight * Math.PI/3
+ cam.rotationY = rotY + dx
+ cam.rotationX = rotX - dy
+ minimap.update()
+ })
+ document.addEventListener('mouseup', function (e) {
+ app.dragging = dragging = false
+ })
+
+ window.addEventListener('blur', function(e){
+ $(".edit-image.menu,.edit-video.menu").hide()
+ moveForward = moveLeft= moveBackward = moveRight = turnLeft = turnRight = jumping = dragging = false
+ })
+
+ },
+
+ update: function () {
+
+ var ry = cam.rotationY
+
+ if (moveForward || moveBackward || moveRight || moveLeft || turnLeft || turnRight) {
+
+ vx = vz = 0
+
+ if (moveForward) {
+ vx += v * Math.cos(ry + Math.PI / 2)
+ vz += v * Math.sin(ry + Math.PI / 2)
+ }
+ if (moveBackward) {
+ vx -= v * Math.cos(ry + Math.PI / 2)
+ vz -= v * Math.sin(ry + Math.PI / 2)
+ }
+ if (moveLeft) {
+ vx -= v * Math.cos(ry)
+ vz -= v * Math.sin(ry)
+ }
+ if (moveRight) {
+ vx += v * Math.cos(ry)
+ vz += v * Math.sin(ry)
+ }
+
+ if (turnLeft) {
+ cam.rotationY += vr
+ }
+ if (turnRight) {
+ cam.rotationY -= vr
+ }
+
+ cam.x += vx
+ cam.z += vz
+
+ minimap.update()
+
+ }
+
+ vy -= 1
+
+ // update cam
+ cam.y += vy
+
+ if (cam.y <= viewHeight) {
+ cam.y = viewHeight
+ vy = 0
+ jumping = false
+ }
+
+ }
+ }
+}
diff --git a/assets/javascripts/mx/extensions/mx.rotationControl.js b/assets/javascripts/mx/extensions/mx.rotationControl.js
new file mode 100644
index 0000000..3bdc043
--- /dev/null
+++ b/assets/javascripts/mx/extensions/mx.rotationControl.js
@@ -0,0 +1,266 @@
+// Usage:
+//
+// var control = new MX.RotationControl()
+// control.init( object{MX.Object3D} [, listener{HTMLElement}] )
+//
+// In animation loop:
+//
+// control.update()
+//
+// The above code will register handler functions on `listener`
+// and will be updating `object`s rotationX and rotationY
+// If no `listener` is provided, will default to `object`s el.
+
+MX.RotationControl = function () {
+
+ var object,
+ locked = false
+
+ var down = false,
+ active = false,
+ lastX,
+ lastY
+
+ var pointerLockPrefix =
+ 'pointerLockElement' in document ? '' :
+ 'mozPointerLockElement' in document ? 'moz' :
+ 'webkitPointerLockElement' in document ? 'webkit' : null,
+ hasPointerLock = !(pointerLockPrefix === null)
+ pointerLockEnabled = false
+
+ var pub = {
+
+ sensitivity : .5,
+ ease : 10,
+ drag : true,
+
+ inverseX : false,
+ inverseY : false,
+
+ disableX : false,
+ disableY : false,
+
+ rotationX : 0,
+ rotationY : 0,
+
+ upperBoundX : undefined,
+ lowerBoundX : undefined,
+
+ upperBoundY : undefined,
+ lowerBoundY : undefined,
+
+ usePreset: function (name) {
+ var ops = presets[name]
+ if (ops) {
+ if (currentPreset && presets[currentPreset].teardown) {
+ presets[currentPreset].teardown()
+ }
+ for (var op in ops) {
+ if (op !== 'setup' && op !== 'teardown') {
+ pub[op] = ops[op]
+ }
+ }
+ if (op.setup) ops.setup()
+ }
+ }
+ }
+
+ var currentPreset
+ var presets = {
+ firstPerson: {
+ drag: false,
+ ease: 2,
+ sensitivity: .18,
+ inverseX: true,
+ inverseY: true,
+ upperBoundX: MX.rotationUnit === 'deg' ? 90 : Math.PI / 2,
+ lowerBoundX: MX.rotationUnit === 'deg' ? -90 : -Math.PI / 2
+ },
+ skybox: {
+ sensitivity: .18,
+ inverseX: true,
+ inverseY: true,
+ upperBoundX: MX.rotationUnit === 'deg' ? 90 : Math.PI / 2,
+ lowerBoundX: MX.rotationUnit === 'deg' ? -90 : -Math.PI / 2
+ }
+ }
+
+ function init (obj, lis) {
+ if (active) return
+
+ object = obj
+ pub.rotationX = object.rotationX
+ pub.rotationY = object.rotationY
+
+ if (lis instanceof HTMLElement) {
+ listener = lis
+ } else if (lis instanceof MX.Object3D) {
+ listener = lis.el
+ } else {
+ listener = window.document
+ }
+
+ listener.addEventListener('mousedown', onDown)
+ listener.addEventListener('mousemove', onMove)
+ listener.addEventListener('mouseup', onUp)
+ listener.addEventListener('touchstart', onDown)
+ listener.addEventListener('touchmove', onMove)
+ listener.addEventListener('touchend', onUp)
+
+ active = true
+ }
+
+ function changeObject (obj) {
+ object = obj
+ pub.rotationX = object.rotationX
+ pub.rotationY = object.rotationY
+ }
+
+ function changeListener (lis) {
+ remove()
+ active = false
+ init(object, lis)
+ if (pointerLockEnabled) {
+ initPointerLock()
+ }
+ }
+
+ function remove () {
+ if (!active) return
+ listener.removeEventListener('mousedown', onDown)
+ listener.removeEventListener('mousemove', onMove)
+ listener.removeEventListener('mouseup', onUp)
+ listener.removeEventListener('touchstart', onDown)
+ listener.removeEventListener('touchmove', onMove)
+ listener.removeEventListener('touchend', onUp)
+
+ if (hasPointerLock) {
+ document.removeEventListener(pointerLockPrefix + 'pointerlockchange', onPointerLockChange)
+ document.removeEventListener('mousemove', onPointerLockMove)
+ document.body[pointerLockPrefix + (pointerLockPrefix ? 'E' : 'e') + 'xitPointerLock']()
+ }
+ active = false
+ }
+
+ function onDown (e) {
+ e = normalizeEvent(e)
+ if (!e) return
+ down = true
+ lastX = e.pageX
+ lastY = e.pageY
+ }
+
+ function onMove (e) {
+ if (app.dragging) return;
+ if (e.type = 'touchmove') {
+ e.preventDefault()
+ }
+ if (pub.drag && !down) return
+ e = normalizeEvent(e)
+ if (!e) return
+ lastX = lastX || e.pageX
+ lastY = lastY || e.pageY
+ var dx = e.pageX - lastX,
+ dy = e.pageY - lastY
+ lastX = e.pageX
+ lastY = e.pageY
+ updateTarget(dx, dy)
+ }
+
+ function onUp () {
+ app.dragging = down = false
+ }
+
+ function initPointerLock () {
+
+ if (pointerLockEnabled) return
+
+ document.addEventListener(pointerLockPrefix + 'pointerlockchange', onPointerLockChange)
+ document.addEventListener('mousemove', onPointerLockMove)
+
+ document.body[pointerLockPrefix + (pointerLockPrefix ? 'R' : 'r') + 'equestPointerLock']()
+ }
+
+ function onPointerLockChange () {
+ var el = document.body
+ if (document[pointerLockPrefix + (pointerLockPrefix ? 'P' : 'p') + 'ointerLockElement'] === el) {
+ pointerLockEnabled = true
+ } else {
+ pointerLockEnabled = false
+ }
+ }
+
+ function onPointerLockMove (e) {
+ if (!pointerLockEnabled) return
+ var dx = e[pointerLockPrefix + (pointerLockPrefix ? 'M' : 'm') + 'ovementX'],
+ dy = e[pointerLockPrefix + (pointerLockPrefix ? 'M' : 'm') + 'ovementY']
+ updateTarget(dx, dy)
+ }
+
+ function normalizeEvent (e) {
+ if (e.touches) {
+ return e.touches.length > 1 ? false : e.touches[0]
+ } else {
+ return e
+ }
+ }
+
+ function updateTarget (dx, dy) {
+ if (pub.inverseX) dx = -dx
+ if (pub.inverseY) dy = -dy
+ if (MX.rotationUnit !== 'deg') {
+ dx = MX.toRad(dx)
+ dy = MX.toRad(dy)
+ }
+
+ if (!pub.disableX) {
+ pub.rotationX -= dy * pub.sensitivity
+ if (pub.upperBoundX) pub.rotationX = Math.min(pub.rotationX, pub.upperBoundX)
+ if (pub.lowerBoundX) pub.rotationX = Math.max(pub.rotationX, pub.lowerBoundX)
+ }
+
+ if (!pub.disableY) {
+ pub.rotationY += dx * pub.sensitivity
+ if (pub.upperBoundY) pub.rotationY = Math.min(pub.rotationY, pub.upperBoundY)
+ if (pub.lowerBoundY) pub.rotationY = Math.max(pub.rotationY, pub.lowerBoundY)
+ }
+ }
+
+ function update () {
+ if (!object || locked) return
+ var dx = pub.rotationX - object.rotationX,
+ dy = pub.rotationY - object.rotationY
+ if (Math.abs(dx) < 0.0001) {
+ object.rotationX = pub.rotationX
+ } else {
+ object.rotationX += dx / pub.ease
+ }
+ if (Math.abs(dy) < 0.0001) {
+ object.rotationY = pub.rotationY
+ } else {
+ object.rotationY += dy / pub.ease
+ }
+ }
+
+ function lock () {
+ locked = true
+ }
+
+ function unlock () {
+ pub.rotationX = object.rotationX
+ pub.rotationY = object.rotationY
+ locked = false
+ }
+
+ pub.init = init
+ pub.remove = remove
+ pub.update = update
+ pub.lock = lock
+ pub.unlock = unlock
+ pub.initPointerLock = initPointerLock
+ pub.changeObject = changeObject
+ pub.changeListener = changeListener
+
+ return pub
+
+} \ No newline at end of file
diff --git a/assets/javascripts/mx/extensions/mx.scene.js b/assets/javascripts/mx/extensions/mx.scene.js
new file mode 100644
index 0000000..c1501f5
--- /dev/null
+++ b/assets/javascripts/mx/extensions/mx.scene.js
@@ -0,0 +1,161 @@
+// NOTE
+//
+// This is not a fully functional 3d scene as you might expect.
+// The camera can only do pitch (rotationX) and yaw (rotationY), but no roll (rotationZ)
+// because I haven't implemented alternative euler orders or quaternions.
+//
+// For serious 3D scenes with more functionalities you should use
+// THREE.js with CSS3D Renderer.
+
+MX.Scene = (function () {
+
+ var add = MX.Object3D.prototype.add,
+ remove = MX.Object3D.prototype.remove
+
+ function Scene () {
+
+ this.el = document.createElement('div')
+ this.el.classList.add('mx-scene')
+
+ var s = this.el.style
+
+ s[MX.transformProp] = 'preserve-3d'
+
+ s.webkitPerspectiveOrigin = '50% 50%'
+ s.mozPerspectiveOrigin = '50% 50%'
+ s.perspectiveOrigin = '50% 50%'
+
+ s.webkitUserSelect = 'none'
+ s.mozUserSelect = 'none'
+ s.userSelect = 'none'
+
+ s.overflow = 'hidden'
+
+ this.inner = new MX.Object3D().addTo(this.el)
+ this.inner.el.style.width = '0'
+ this.inner.el.style.height = '0'
+
+ var self = this
+ var width, height, perspective
+
+ Object.defineProperty(this, 'width', {
+ get: function () {
+ return width
+ },
+ set: function (val) {
+ width = val
+ self.el.style.width = val + 'px'
+ }
+ })
+
+ Object.defineProperty(this, 'height', {
+ get: function () {
+ return height
+ },
+ set: function (val) {
+ height = val
+ self.el.style.height = val + 'px'
+ }
+ })
+
+ Object.defineProperty(this, 'perspective', {
+ get: function () {
+ return perspective
+ },
+ set: function (val) {
+ perspective = val
+ self.el.style[MX.perspectiveProp] = val + 'px'
+ self.inner.z = -val - self.camera.z
+ self.inner.rotationOrigin.z = -val
+ }
+ })
+
+ var cam = this.camera = new MX.Object3D()
+ cam.el = null
+
+ // cam's lookAt is a bit different
+ // ignoring rotationZ
+ cam.getLookAtEuler = getCameraEuler.bind(cam)
+
+ this.inner.rotationOrigin = { x:0, y:0, z:0 }
+
+ this.perspective = 0
+ }
+
+ Scene.prototype = {
+
+ constructor: Scene,
+
+ add: function () {
+ add.apply(this.inner, arguments)
+ return this
+ },
+
+ remove: function () {
+ remove.apply(this.inner, arguments)
+ return this
+ },
+
+ addTo: function (target) {
+ if (typeof target === 'string') {
+ target = document.querySelector(target)
+ }
+ if (target instanceof HTMLElement && target.appendChild) {
+ target.appendChild(this.el)
+ } else {
+ console.warn('You can only add a Scene to an HTML element.')
+ }
+ return this
+ },
+
+ update: function () {
+ // update inner based on camera
+
+ var i = this.inner,
+ c = this.camera
+
+ c.update()
+
+ i.z = -this.perspective - c.z
+ i.x = -c.x
+ i.y = -c.y
+
+ i.rotationX = -c.rotationX
+ i.rotationY = -c.rotationY
+ //i.rotationZ = -c.rotationZ
+
+ i.update()
+ return this
+ },
+
+ sizeToScreen: function(){
+ scene.width = window.innerWidth
+ scene.height = window.innerHeight
+ if (is_mobile) {
+ scene.perspective = min(window.innerWidth, window.innerHeight) - 80
+ }
+ else {
+ scene.perspective = min(window.innerWidth, window.innerHeight)
+ }
+ },
+
+ }
+
+ function getCameraEuler (target) {
+ var dx = target.x - this.x,
+ dy = target.y - this.y,
+ dz = target.z - this.z
+ r = {}
+ r.y = Math.atan2(-dx, dz)
+ r.x = Math.atan2(-dy, Math.sqrt(dx*dx + dz*dz))
+ r.z = 0
+ if (MX.rotationUnit === 'deg') {
+ r.x = MX.toDeg(r.x)
+ r.y = MX.toDeg(r.y)
+ }
+ return r
+ }
+
+ return Scene
+
+})() \ No newline at end of file