/** * Input parent class * @param graphics GameLib.D3.Graphics * @param apiInputEditor GameLib.D3.API.Input.Editor * @param dom GameLib.DOM * @constructor */ GameLib.D3.Input.Editor = function ( graphics, apiInputEditor, dom ) { this.graphics = graphics; this.graphics.isNotThreeThrow(); if (GameLib.Utils.UndefinedOrNull(apiInputEditor)) { apiInputEditor = {}; } GameLib.D3.API.Input.Editor.call( this, apiInputEditor.id, apiInputEditor.name, apiInputEditor.domElement, apiInputEditor.domContainer, apiInputEditor.editor, apiInputEditor.camera, apiInputEditor.widthOffset, apiInputEditor.heightOffset, apiInputEditor.containerWidthOffset, apiInputEditor.containerHeightOffset, apiInputEditor.selectDelayMs, apiInputEditor.parentEntity ); if (GameLib.Utils.UndefinedOrNull(dom)) { console.warn('Cannot create window resize event without handle to the DOM'); dom = null; // throw new Error('Cannot create Input without an handle to the DOM'); } this.dom = dom; if (this.domElement instanceof GameLib.API.DomElement) { this.domElement = new GameLib.DomElement( this.graphics, this.domElement ) } if (this.domContainer instanceof GameLib.API.DomElement) { this.domContainer = new GameLib.DomElement( this.graphics, this.domContainer ) } this.meshMoveMode = false; this.meshMoveXMode = false; this.meshMoveYMode = false; this.meshMoveZMode = false; /** * We need new function pointers with scope bound to this so we can remove the * window event handlers when we need to * @type {function()} */ this.resize = this.onWindowResize.bind(this); this.mouseMove = this.onMouseMove.bind(this); this.mouseDown = this.onMouseDown.bind(this); this.keyPress = this.onKeyPress.bind(this); this.contextMenu = this.onContextMenu.bind(this); this.raycaster = new GameLib.D3.Raycaster( this.graphics ); this.mouse = new GameLib.Mouse( this.graphics ); this.buildIdToObject(); this.instance = this.createInstance(); }; GameLib.D3.Input.Editor.prototype = Object.create(GameLib.D3.API.Input.Editor.prototype); GameLib.D3.Input.Editor.prototype.constructor = GameLib.D3.Input.Editor; GameLib.D3.Input.Editor.prototype.createInstance = function(update) { var instance = null; if (update) { instance = this.instance; instance.camera = this.camera.instance; return instance; } else { instance = new THREE.EditorControls( this.camera.instance, this.domElement.instance ) } this.domElement.instance.addEventListener( 'mousemove', this.mouseMove, false ); this.domElement.instance.addEventListener( 'contextmenu', this.contextMenu, false ); this.domElement.instance.addEventListener( 'mousedown', this.mouseDown, false ); this.domElement.instance.addEventListener( 'keydown', this.keyPress, false ); //TODO : window resize // this.dom.window.addEventListener( // 'resize', // this.resize, // false // ); return instance; }; GameLib.D3.Input.Editor.prototype.updateInstance = function() { this.instance = this.createInstance(true); }; /** * GameLib.D3.Input.Editor to GameLib.D3.API.Input.Editor * @returns {GameLib.D3.API.Input.Editor} */ GameLib.D3.Input.Editor.prototype.toApiComponent = function() { var apiInputEditor = new GameLib.D3.API.Input.Editor( this.id, this.name, this.domElementId, this.domContainerId, GameLib.Utils.IdOrNull(this.editor), GameLib.Utils.IdOrNull(this.camera), this.widthOffset, this.heightOffset, this.containerWidthOffset, this.containerHeightOffset, this.selectDelayMs, GameLib.Utils.IdOrNull(this.parentEntity) ); return apiInputEditor; }; GameLib.D3.Input.Editor.FromObjectComponent = function(graphics, objectComponent) { var apiInputEditor = GameLib.D3.API.Input.Editor.FromObjectComponent(objectComponent); return new GameLib.D3.Input.Editor( graphics, apiInputEditor ); }; GameLib.D3.Input.Editor.prototype.onWindowResize = function() { this.domContainer.instance.style.height = (this.window.innerHeight - this.containerHeightOffset) + 'px'; this.domContainer.instance.style.width = (this.window.innerWidth - this.containerWidthOffset) + 'px'; var width = this.window.innerWidth - this.widthOffset; var height = this.window.innerHeight - this.heightOffset; //TODO: map the relative viewport sizes and offsets with the size differences this.editor.viewports.map( function(viewport) { viewport.width = width; viewport.height = height; viewport.updateInstance(); } ); this.editor.game.viewports.map( function(viewport) { viewport.width = width; viewport.height = height; viewport.updateInstance(); } ); // // this.scene.cameras[this.scene.activeCameraIndex].aspect = () / window.innerHeight; // this.scene.cameras[this.scene.activeCameraIndex].updateInstance(); // // this.scene.renderers[this.scene.activeRendererIndex].width = window.innerWidth - 400; // this.scene.renderers[this.scene.activeRendererIndex].height = window.innerHeight; // this.scene.renderers[this.scene.activeRendererIndex].updateInstance(); }; /** * Keypress events * @param event */ GameLib.D3.Input.Editor.prototype.onKeyPress = function(event) { if (event.code == "KeyQ") { this.editor.allSelected = !this.editor.allSelected; this.editor.selectedObjects = []; if (this.editor.allSelected) { for (var property in this.editor.idToObject) { if (this.editor.idToObject.hasOwnProperty(property)) { this.editor.selectedObjects.push( new GameLib.D3.SelectedObject( this.graphics, this.editor.idToObject(property) ) ) } } } if (this.editor.onSelectionChanged) { this.editor.onSelectionChanged(this.editor); } } 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'); } } }; /** * Mouse click events * @param event * @returns {boolean} */ GameLib.D3.Input.Editor.prototype.onMouseDown = function(event) { if (event.button == 2) { event.cancelBubble = true; event.preventDefault(); if (event.stopPropagation) { event.stopPropagation(); } var meshes = []; for (var property in this.editor.idToObject) { if (this.editor.idToObject.hasOwnProperty(property)) { if (this.editor.idToObject[property] instanceof GameLib.D3.Mesh) { meshes.push(this.editor.idToObject[property]); } } } var intersects = this.raycaster.getIntersectedObjects(meshes); if (intersects.length > 0) { 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; } 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.raycaster.instance.setFromCamera( this.mouse, this.cameras[this.scene.activeCameraIndex].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(); } } } }; /** * Prevent Context Menu creation * @param event * @returns {boolean} */ GameLib.D3.Input.Editor.prototype.onContextMenu = function(event){ if (event.stopPropagation) { event.stopPropagation(); } if (event.preventDefault) { event.preventDefault(); } event.cancelBubble = true; return false; }; GameLib.D3.Input.Editor.prototype.update = function(deltaTime) { return; };