/** * System takes care of updating all the entities (based on their component data) * @param apiSystem GameLib.API.System * @param graphics * @constructor */ GameLib.System.Input = function( apiSystem, graphics ) { GameLib.System.call( this, apiSystem ); this.graphics = graphics; this.graphics.isNotThreeThrow(); this.selectAll = false; this.controlLeft = false; this.sensitivityCounter = 0; this.editorControls = []; this.touchControls = []; this.keyboardControls = []; this.mouseControls = []; /** * Touch Controls * @type {null} */ this.touchStart = null; this.touchMove = null; this.touchEnd = null; this.touchCancel = null; /** * Keyboard Controls * @type {null} */ this.keyboardKeyUp = null; this.keyboardKeyDown = null; /** * Mouse Controls * @type {null} */ this.mouseDown = null; this.mouseMove = null; this.mouseWheel = null; this.mouseUp = null; /** * Editor Controls * @type {null} */ this.keyDown = null; this.keyUp = null; this.mouseDownEdit = null; this.mouseMoveEdit = null; this.mouseWheelEdit = null; this.mouseUpEdit = null; this.delayedInstanceEncounteredSubscription = null; this.instanceCreatedSubscription = null; this.removeComponentSubscription = null; this.mouse = new GameLib.Mouse( graphics ) }; GameLib.System.Input.prototype = Object.create(GameLib.System.prototype); GameLib.System.Input.prototype.constructor = GameLib.System.Input; /** * */ GameLib.System.Input.prototype.start = function() { GameLib.System.prototype.start.call(this); this.editorControls = GameLib.EntityManager.Instance.queryComponents(GameLib.D3.Controls.Editor); this.touchControls = GameLib.EntityManager.Instance.queryComponents(GameLib.D3.Controls.Touch); this.keyboardControls = GameLib.EntityManager.Instance.queryComponents(GameLib.D3.Controls.Keyboard); this.mouseControls = GameLib.EntityManager.Instance.queryComponents(GameLib.D3.Controls.Mouse); this.instanceCreatedSubscription = GameLib.Event.Subscribe( GameLib.Event.INSTANCE_CREATED, this.instanceCreated.bind(this) ); this.removeComponentSubscription = GameLib.Event.Subscribe( GameLib.Event.REMOVE_COMPONENT, this.removeComponent.bind(this) ); this.delayedInstanceEncounteredSubscription = GameLib.Event.Subscribe( GameLib.Event.DELAYED_INSTANCE_ENCOUNTERED, this.delayedInstanceEncountered.bind(this) ); /** * If we have touch controls - inject them first so we can override editor controls if necessary */ this.registerTouchControls(); this.registerKeyboardControls(); this.registerMouseControls(); this.registerEditorControls(); }; /** * */ GameLib.System.Input.prototype.stop = function() { GameLib.System.prototype.stop.call(this); this.instanceCreatedSubscription.remove(); this.removeComponentSubscription.remove(); this.delayedInstanceEncounteredSubscription.remove(); this.deRegisterEditorControls(); this.deRegisterTouchControls(); this.deRegisterKeyboardControls(); this.deRegisterMouseControls(); this.editorControls = []; this.touchControls = []; this.keyboardControls = []; this.mouseControls = []; }; /** * From now on we want to track everything about a component, only from the systems that are active * @param data */ GameLib.System.Input.prototype.instanceCreated = function(data) { if (data.component instanceof GameLib.D3.Controls.Editor) { if (this.editorControls.length > 0) { console.log('ignoring multiple editor controls') } else { this.editorControls.push(data.component); this.registerEditorControls(); } } if (data.component instanceof GameLib.D3.Controls.Touch) { if (this.touchControls.length > 0) { console.log('ignoring multiple touch controls') } else { this.touchControls.push(data.component); this.registerTouchControls(); } } if (data.component instanceof GameLib.D3.Controls.Keyboard) { if (this.keyboardControls.length > 0) { console.log('ignoring multiple keyboard controls') } else { this.keyboardControls.push(data.component); this.registerKeyboardControls(); } } if (data.component instanceof GameLib.D3.Controls.Mouse) { if (this.mouseControls.length > 0) { console.log('ignoring multiple mouse controls') } else { this.mouseControls.push(data.component); this.registerMouseControls(); } } }; /** * Removes a particle engine from this system * @param data */ GameLib.System.Input.prototype.removeComponent = function(data) { if (data.component instanceof GameLib.D3.Controls.Editor) { var index = this.editorControls.indexOf(data.component); if (index !== -1) { console.log('removing editor controls from system'); this.deRegisterEditorControls(); this.editorControls.splice(index, 1); } else { console.log('failed to find the editor controls in the system - probably it was ignored - ' + data.component.name); } } }; /** * Delayed Instance - we need to check if editControls will block the loading process (since only one will be created) * @param data */ GameLib.System.Input.prototype.delayedInstanceEncountered = function(data) { if (data.component instanceof GameLib.D3.Controls.Editor) { if (this.editorControls.length === 0) { /** * We need to register these controls so instance creation can continue */ this.editorControls.push(data.component); this.registerEditorControls(); } else { /** * There are already another editor controls, these ones will never get created, we need to * notify our linking system of this problem so loading can continue */ data.component.createInstance(); } } }; GameLib.System.Input.prototype.registerTouchControls = function() { if (this.touchControls.length !== 1) { return; } var touchControl = this.touchControls[0]; this.touchSensitivity = touchControl.sensitivity; this.touchStart = this.onTouchStart.bind(this); this.touchMove = this.onTouchMove.bind(this); this.touchEnd = this.onTouchEnd.bind(this); this.touchCancel = this.onTouchCancel.bind(this); touchControl.domElement.instance.addEventListener( 'touchstart', this.touchStart, false ); touchControl.domElement.instance.addEventListener( 'touchmove', this.touchMove, false ); touchControl.domElement.instance.addEventListener( 'touchend', this.touchEnd, false ); touchControl.domElement.instance.addEventListener( 'touchcancel', this.touchCancel, false ); }; GameLib.System.Input.prototype.registerKeyboardControls = function() { if (this.keyboardControls.length !== 1) { return; } var keyboardControl = this.keyboardControls[0]; this.keyboardKeyUp = this.onKeyboardKeyUp.bind(this); this.keyboardKeyDown = this.onKeyboardKeyDown.bind(this); keyboardControl.domElement.instance.addEventListener( 'keyup', this.keyboardKeyUp, false ); keyboardControl.domElement.instance.addEventListener( 'keydown', this.keyboardKeyDown, false ); }; GameLib.System.Input.prototype.registerMouseControls = function() { if (this.mouseControls.length !== 1) { return; } var mouseControl = this.mouseControls[0]; this.mouseDown = this.onMouseDown.bind(this); this.mouseMove = this.onMouseMove.bind(this); this.mouseWheel = this.onMouseWheel.bind(this); this.mouseUp = this.onMouseUp.bind(this); mouseControl.domElement.instance.addEventListener( 'mousedown', this.mouseDown, false ); mouseControl.domElement.instance.addEventListener( 'mousemove', this.mouseMove, false ); mouseControl.domElement.instance.addEventListener( 'wheel', this.mouseWheel, false ); mouseControl.domElement.instance.addEventListener( 'mouseup', this.mouseUp, false ); }; GameLib.System.Input.prototype.registerEditorControls = function() { if (this.editorControls.length !== 1) { return; } var editorControl = this.editorControls[0]; /** * If we already have mouse controls, we don't want to add another event listener onto the DOM */ this.mouseDownEdit = this.onMouseDownEdit.bind(this); this.mouseMoveEdit = this.onMouseMoveEdit.bind(this); editorControl.domElement.instance.addEventListener( 'mousedown', this.mouseDownEdit, false ); editorControl.domElement.instance.addEventListener( 'mousemove', this.mouseMoveEdit, false ); /** * If we already have keyboard controls, we don't want to add another event listener onto the DOM */ if (this.keyboardControls.length > 0) { /** * Do Nothing */ } else { this.keyDown = this.onKeyDown.bind(this); this.keyUp = this.onKeyUp.bind(this); editorControl.domElement.instance.addEventListener( 'keydown', this.keyDown, false ); editorControl.domElement.instance.addEventListener( 'keyup', this.keyUp, false ); } editorControl.createInstance(); this.mouseWheelEdit = this.onMouseWheelEdit.bind(this); this.mouseUpEdit = this.onMouseUpEdit.bind(this); editorControl.domElement.instance.addEventListener( 'wheel', this.mouseWheelEdit, false ); editorControl.domElement.instance.addEventListener( 'mouseup', this.mouseUpEdit, false ); }; GameLib.System.Input.prototype.deRegisterEditorControls = function() { if (this.editorControls.length !== 1) { return; } var editorControl = this.editorControls[0]; editorControl.domElement.instance.removeEventListener( 'mousedown', this.mouseDownEdit, false ); editorControl.domElement.instance.removeEventListener( 'mousemove', this.mouseMoveEdit, false ); if (this.keyboardControls.length < 1) { editorControl.domElement.instance.removeEventListener( 'keydown', this.keyDown, false ); editorControl.domElement.instance.removeEventListener( 'keyup', this.keyUp, false ); } editorControl.instance.dispose(); editorControl.domElement.instance.removeEventListener( 'wheel', this.mouseWheelEdit, false ); editorControl.domElement.instance.removeEventListener( 'mouseup', this.mouseUpEdit, false ); }; GameLib.System.Input.prototype.deRegisterTouchControls = function() { if (this.touchControls.length !== 1) { return; } var touchControl = this.touchControls[0]; touchControl.domElement.instance.removeEventListener( 'touchstart', this.touchStart, false ); touchControl.domElement.instance.removeEventListener( 'touchmove', this.touchMove, false ); touchControl.domElement.instance.removeEventListener( 'touchend', this.touchEnd, false ); touchControl.domElement.instance.removeEventListener( 'touchcancel', this.touchCancel, false ); }; GameLib.System.Input.prototype.deRegisterKeyboardControls = function() { if (this.keyboardControls.length !== 1) { return; } var keyboardControl = this.keyboardControls[0]; keyboardControl.domElement.instance.removeEventListener( 'keydown', this.keyboardKeyDown, false ); keyboardControl.domElement.instance.removeEventListener( 'keyup', this.keyboardKeyUp, false ); }; GameLib.System.Input.prototype.deRegisterMouseControls = function() { if (this.mouseControls.length !== 1) { return; } var mouseControl = this.mouseControls[0]; mouseControl.domElement.instance.removeEventListener( 'mousedown', this.mouseDown, false ); mouseControl.domElement.instance.removeEventListener( 'mousemove', this.mouseMove, false ); mouseControl.domElement.instance.removeEventListener( 'wheel', this.mouseWheel, false ); mouseControl.domElement.instance.removeEventListener( 'mouseup', this.mouseUp, false ); }; GameLib.System.Input.prototype.onKeyboardKeyUp = function(event) { GameLib.Event.Emit( GameLib.Event.KEY_DOWN, { code : event.code } ); }; GameLib.System.Input.prototype.onKeyboardKeyDown = function(event) { GameLib.Event.Emit( GameLib.Event.KEY_UP, { code : event.code } ); }; GameLib.System.Input.prototype.onTouchStart = function(event) { this.sensitivityCounter = 0; this.touches = {}; for (var t = 0; t < event.touches.length; t++) { this.touches[event.touches[t].identifier] = { left : 0, right : 0, up : 0, down : 0, pageX : event.touches[t].pageX, pageY : event.touches[t].pageY, cancelled : false, ended : false }; } this.touches.event = event; GameLib.Event.Emit( GameLib.Event.TOUCH_START, this.touches ) }; GameLib.System.Input.prototype.onTouchMove = function (event) { this.sensitivityCounter++; var id = null; for (var t = 0; t < event.changedTouches.length; t++) { id = event.changedTouches[t].identifier; var diffX = Math.abs(event.changedTouches[t].pageX - this.touches[id].pageX); var diffY = Math.abs(event.changedTouches[t].pageY - this.touches[id].pageY); var left = 0; var right = 0; var up = 0; var down = 0; if (event.changedTouches[t].pageX > this.touches[id].pageX) { right += diffX; left -= diffX; } if (event.changedTouches[t].pageX < this.touches[id].pageX) { left += diffX; right -= diffX; } if (event.changedTouches[t].pageY < this.touches[id].pageY) { up += diffY; down -= diffY; } if (event.changedTouches[t].pageY > this.touches[id].pageY) { down += diffY; up -= diffY; } this.touches[id].right += right; this.touches[id].left += left; this.touches[id].up += up; this.touches[id].down += down; this.touches[id].pageX = event.changedTouches[t].pageX; this.touches[id].pageY = event.changedTouches[t].pageY; //console.log(this.touches[id]); } this.touches.event = event; if (this.sensitivityCounter >= this.touchSensitivity) { this.sensitivityCounter = 0; GameLib.Event.Emit( GameLib.Event.TOUCH_MOVE, this.touches ); } }; GameLib.System.Input.prototype.onTouchCancel = function(event) { this.sensitivityCounter = 0; for (var t = 0; t < event.changedTouches.length; t++) { this.touches[event.changedTouches[t].identifier].cancelled = true; this.touches[event.changedTouches[t].identifier].event = event; GameLib.Event.Emit( GameLib.Event.TOUCH_CANCEL, this.touches[event.changedTouches[t].identifier] ); delete this.touches[event.changedTouches[t].identifier]; } }; GameLib.System.Input.prototype.onTouchEnd = function(event) { this.sensitivityCounter = 0; for (var t = 0; t < event.changedTouches.length; t++) { this.touches[event.changedTouches[t].identifier].ended = true; this.touches[event.changedTouches[t].identifier].event = event; GameLib.Event.Emit( GameLib.Event.TOUCH_END, this.touches[event.changedTouches[t].identifier] ); delete this.touches[event.changedTouches[t].identifier]; } }; GameLib.System.Input.prototype.onKeyDown = function(event) { console.log('input system emitted keypress ' + event.code); GameLib.Event.Emit( GameLib.Event.KEY_DOWN, { code : event.code } ); var meshes = null; if (event.code === 'Delete') { meshes = GameLib.EntityManager.Instance.queryComponents([GameLib.D3.Mesh]); var deletedMeshes = []; meshes.map( function(mesh) { if (mesh.selected) { deletedMeshes.push(mesh); mesh.removeHelper(); var scene = mesh.parentScene; scene.removeObject(mesh); scene.buildIdToObject(); } }.bind(this) ); GameLib.Event.Emit( GameLib.Event.REMOVE_MESH, { meshes : deletedMeshes } ); } if (event.code === 'ControlLeft') { this.controlLeft = true; } if (event.code === 'KeyA') { this.selectAll = !this.selectAll; meshes = GameLib.EntityManager.Instance.queryComponents([GameLib.D3.Mesh]); meshes.map(function(mesh){ if (this.selectAll) { this.selectMesh(mesh); } else { this.deSelectMesh(mesh); } }.bind(this)); GameLib.Event.Emit( GameLib.Event.BUILD_GUI, null ) } if (event.code === 'KeyP') { GameLib.Event.Emit(GameLib.Event.PAUSE); } }; GameLib.System.Input.prototype.onKeyUp = function(event) { GameLib.Event.Emit( GameLib.Event.KEY_UP, { code : event.code } ); if (event.code === 'ControlLeft') { this.controlLeft = false; } }; GameLib.System.Input.prototype.onMouseDown = function(event) { // console.log('mouse down'); GameLib.Event.Emit( GameLib.Event.MOUSE_DOWN, { event : event } ) }; GameLib.System.Input.prototype.onMouseMove = function(event) { // console.log('mouse move'); GameLib.Event.Emit( GameLib.Event.MOUSE_MOVE, { event : event } ) }; GameLib.System.Input.prototype.onMouseWheel = function(event) { // console.log('mouse wheel'); GameLib.Event.Emit( GameLib.Event.MOUSE_WHEEL, { event : event } ) }; GameLib.System.Input.prototype.onMouseUp = function(event) { // console.log('mouse up'); GameLib.Event.Emit( GameLib.Event.MOUSE_UP, { event : event } ) }; GameLib.System.Input.prototype.onMouseDownEdit = function(event) { if (event.button === 2) { this.editorControls.map( function(editorControl) { if (this.controlLeft) { return; } this.mouse.x = (event.offsetX / event.target.width ) * 2 - 1; this.mouse.y = -(event.offsetY / event.target.height) * 2 + 1; var scenes = GameLib.EntityManager.Instance.queryComponents(GameLib.D3.Scene); var intersects = scenes.reduce( function (result, scene) { editorControl.raycaster.instance.setFromCamera( this.mouse, editorControl.camera.instance ); intersects = editorControl.raycaster.getIntersectedObjects(scene.meshes); intersects.map(function (intersect) { result.push(intersect); }); return result; }.bind(this), [] ); intersects.sort( function (a, b) { if (a.distance < b.distance) { return -1; } if (a.distance > b.distance) { return 1; } return 0; } ); var meshes = intersects.map(function (intersect) { return intersect.mesh; }); var mesh = meshes[0]; if (mesh) { /** * Prevent default action (like context menu or whatever) */ event.preventDefault(); /** * Prevent other event listeners for 'mousedown' from executing their actions */ event.stopImmediatePropagation(); if (mesh.selected) { this.deSelectMesh(mesh); } else { this.selectMesh(mesh); } /** * Notify our GUI system to build a GUI */ GameLib.Event.Emit( GameLib.Event.BUILD_GUI, null ) } }.bind(this) ); } }; /** * * @param event */ GameLib.System.Input.prototype.onMouseMoveEdit = function(event) { }; /** * Update the camera position etc. after mouse up * @returns {Function} * @param event */ GameLib.System.Input.prototype.onMouseUpEdit = function(event) { this.editorControls.map( function(editorControl) { editorControl.camera.position.x = editorControl.camera.instance.position.x; editorControl.camera.position.y = editorControl.camera.instance.position.y; editorControl.camera.position.z = editorControl.camera.instance.position.z; editorControl.camera.quaternion.x = editorControl.camera.instance.quaternion.x; editorControl.camera.quaternion.y = editorControl.camera.instance.quaternion.y; editorControl.camera.quaternion.z = editorControl.camera.instance.quaternion.z; editorControl.camera.quaternion.w = editorControl.camera.instance.quaternion.w; editorControl.camera.lookAt.x = editorControl.instance.center.x; editorControl.camera.lookAt.y = editorControl.instance.center.y; editorControl.camera.lookAt.z = editorControl.instance.center.z; editorControl.camera.lookAt.instance.copy(editorControl.instance.center); } ); }; /** * Update our camera position after moving the mouse wheel * @returns {Function} * @param event */ GameLib.System.Input.prototype.onMouseWheelEdit = function(event) { this.editorControls.map( function(editorControl) { editorControl.camera.position.x = editorControl.camera.instance.position.x; editorControl.camera.position.y = editorControl.camera.instance.position.y; editorControl.camera.position.z = editorControl.camera.instance.position.z; } ); }; GameLib.System.Input.prototype.selectMesh = function(mesh) { /** * If mesh is already selected, do nothing */ if (mesh.selected === true) { return; } /** * Notify our component as being 'selected' * @type {boolean} */ mesh.selected = true; mesh.createHelper(); GameLib.Event.Emit( GameLib.Event.MESH_SELECTED, { mesh : mesh } ); }; GameLib.System.Input.prototype.deSelectMesh = function(mesh) { mesh.selected = false; mesh.removeHelper(); GameLib.Event.Emit( GameLib.Event.MESH_DESELECTED, { mesh : mesh } ); }; // // console.log('keypressed ' + event.code); // // if (event.code === 'KeyV') { // //todo - change view // } // // // if (event.code == 'KeyG') { // if (!this.meshMoveMode) { // console.log('move mode'); // this.meshMoveMode = true; // } // } // // if (event.code == 'KeyX') { // if (this.meshMoveMode) { // console.log('move along x'); // this.meshMoveXMode = true; // this.meshMoveYMode = false; // this.meshMoveZMode = false; // } // } // // if (event.code == 'KeyY') { // if (this.meshMoveMode) { // console.log('move along y'); // this.meshMoveXMode = false; // this.meshMoveYMode = true; // this.meshMoveZMode = false; // } // } // // if (event.code == 'KeyZ') { // if (this.meshMoveMode) { // console.log('move along z'); // this.meshMoveXMode = false; // this.meshMoveYMode = false; // this.meshMoveZMode = true; // } // } // // if (event.code == 'Escape') { // if (this.meshMoveMode) { // this.meshMoveMode = false; // console.log('TODO: implement restore positions'); // } // } // // if (event.code == 'Enter') { // if (this.meshMoveMode) { // this.meshMoveMode = false; // console.log('TODO: implement apply positions'); // } // } // }; // GameLib.D3.Input.Editor.prototype.onMouseDown = function(entity) { // // return function(event) { // // if (event.button === 2) { // event.cancelBubble = true; // // event.preventDefault(); // // if (event.stopPropagation) { // event.stopPropagation(); // } // // var meshes = entity.queryComponents(GameLib.D3.Mesh); // // var intersects = this.raycaster.getIntersectedObjects(meshes); // // if (intersects.length > 0) { // // console.log('object(s) instersected'); // // // var index = -1; // // // // for (var s = 0; s < this.editor.selectedObjects.length; s++) { // // if (this.editor.selectedObjects[s].object == intersects[0]) { // // index = s; // // break; // // } // // } // // // // if (index == -1) { // // /** // // * The object is not selected, select it // // */ // // this.selectObject(intersects[0]); // // // // } else { // // /** // // * De-select the objec // // */ // // var delta = Date.now() - this.editor.selectedObjects[index].lastUpdate; // // if (delta > this.selectDelayMs) { // // this.unselectObject(intersects[0]); // // } // // } // // // // if (this.editor.onSelectionChanged) { // // this.editor.onSelectionChanged(this.editor); // // } // } // // return false; // } // } // }; // /** // * Mouse click events // * @param event // * @returns {boolean} // */ // GameLib.D3.Input.Editor.prototype.onMouseDown = function(event) { // // if (event.button === 2) { // // // // // // } // // if (event.button == 0) { // if (this.meshMoveMode) { // this.meshMoveMode = false; // this.meshMoveXMode = false; // this.meshMoveYMode = false; // this.meshMoveZMode = false; // } // } // }; // /** // * Mouse move events // * @param event // */ // GameLib.D3.Input.Editor.prototype.onMouseMove = function(event) { // // // var clientX = event.clientX - this.widthOffset; // // this.mouse.x = ((clientX / (window.innerWidth - this.widthOffset))) * 2 - 1; // // this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; // // this.mouse.x = event.clientX; // this.mouse.y = event.clientY; // // console.log("mouse (" + this.mouse.x + ", " + this.mouse.y + ")"); // // this.raycaster.instance.setFromCamera( // this.mouse, // this.camera.instance // ); // // if (this.meshMoveMode) { // // var units = event.movementY; // // if (this.meshMoveXMode) { // this.moveSelectedObjects('x', units); // } // // if (this.meshMoveYMode) { // this.moveSelectedObjects('y', units); // } // // if (this.meshMoveZMode) { // this.moveSelectedObjects('z', units); // } // } // }; // /** // * Moves selected objects along an axis // * @param alongAxis // * @param units // */ // GameLib.D3.Input.Editor.prototype.moveSelectedObjects = function(alongAxis, units) { // // for (var s = 0; s < this.editor.selectedObjects.length; s++) { // // var object = this.editor.selectedObjects[s].object; // // if (object.position) { // if (alongAxis == 'x') { // object.position.x += units; // } // if (alongAxis == 'y') { // object.position.y += units; // } // if (alongAxis == 'z') { // object.position.z += units; // } // // if (object.updateInstance) { // object.updateInstance(); // } // } // } // };