From 40b39b13a32ff6be8e856a61647fc6358e656d55 Mon Sep 17 00:00:00 2001 From: Farhad Ghayour Date: Wed, 3 Jun 2015 22:55:06 -0700 Subject: [PATCH 01/18] Feat: Add UI events to Mesh --- webgl-renderables/Mesh.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/webgl-renderables/Mesh.js b/webgl-renderables/Mesh.js index ee7b4b25..2336da64 100644 --- a/webgl-renderables/Mesh.js +++ b/webgl-renderables/Mesh.js @@ -583,7 +583,7 @@ Mesh.prototype.onOpacityChange = function onOpacityChange (opacity) { }; /** - * Adds functionality for UI events (TODO) + * Adds functionality for UI events * * @method * @@ -592,7 +592,14 @@ Mesh.prototype.onOpacityChange = function onOpacityChange (opacity) { * @return {undefined} undefined */ Mesh.prototype.onAddUIEvent = function onAddUIEvent (UIEvent) { - //TODO + if (this._UIEvents.indexOf(UIEvent) === -1) { + this._subscribe(UIEvent); + this._UIEvents.push(UIEvent); + } + else if (this._inDraw) { + this._subscribe(UIEvent); + } + return this; }; /** From a790d8a1eea8e25cdd38ea0138c8f0f07775b70e Mon Sep 17 00:00:00 2001 From: Farhad Ghayour Date: Wed, 3 Jun 2015 22:55:31 -0700 Subject: [PATCH 02/18] Feat: Add event subscribing functionality to Mesh --- webgl-renderables/Mesh.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/webgl-renderables/Mesh.js b/webgl-renderables/Mesh.js index 2336da64..291ec175 100644 --- a/webgl-renderables/Mesh.js +++ b/webgl-renderables/Mesh.js @@ -602,6 +602,26 @@ Mesh.prototype.onAddUIEvent = function onAddUIEvent (UIEvent) { return this; }; +/** + * Appends an `ADD_EVENT_LISTENER` command to the command queue. + * + * @method + * @private + * + * @param {String} UIEvent Event type (e.g. `click`) + * + * @return {undefined} undefined + */ +Mesh.prototype._subscribe = function _subscribe(UIEvent) { + if (this._initialized) { + this._changeQueue.push('GL_SUBSCRIBE', UIEvent); + } + if (!this._requestingUpdate) { + this._requestUpdate(); + } + if (!this._requestingUpdate) this._requestUpdate(); +}; + /** * Queues instance to be updated. * From 0541ee0293220b38e4ca2317927edc4d8119ab3b Mon Sep 17 00:00:00 2001 From: Farhad Ghayour Date: Wed, 3 Jun 2015 22:56:26 -0700 Subject: [PATCH 03/18] Feat: Add callback store functionality to Mesh --- webgl-renderables/Mesh.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/webgl-renderables/Mesh.js b/webgl-renderables/Mesh.js index 291ec175..11061838 100644 --- a/webgl-renderables/Mesh.js +++ b/webgl-renderables/Mesh.js @@ -24,6 +24,7 @@ 'use strict'; var Geometry = require('../webgl-geometries'); +var CallbackStore = require('../utilities/CallbackStore'); /** * The Mesh class is responsible for providing the API for how @@ -42,9 +43,13 @@ var Geometry = require('../webgl-geometries'); function Mesh (node, options) { this._node = node; this._changeQueue = []; + this._UIEvents = []; + this._callbacks = new CallbackStore(); + this._initialized = false; this._requestingUpdate = false; this._inDraw = false; + this.value = { drawOptions: null, color: null, @@ -57,6 +62,7 @@ function Mesh (node, options) { }; if (options) this.setDrawOptions(options); + this._id = node.addComponent(this); } @@ -622,6 +628,25 @@ Mesh.prototype._subscribe = function _subscribe(UIEvent) { if (!this._requestingUpdate) this._requestUpdate(); }; +/** + * Function to be invoked by the Node whenever an event is being received. + * There are two different ways to subscribe for those events: + * + * 1. By overriding the onReceive method (and possibly using `switch` in order + * to differentiate between the different event types). + * 2. By using Mesh and using the built-in CallbackStore. + * + * @method + * + * @param {String} event Event type (e.g. `click`) + * @param {Object} payload Event object. + * + * @return {undefined} undefined + */ +Mesh.prototype.onReceive = function onReceive(event, payload) { + this._callbacks.trigger(event, payload); +}; + /** * Queues instance to be updated. * From 03dbd492153264731944a89bc6df98c8f3d9c5f8 Mon Sep 17 00:00:00 2001 From: Farhad Ghayour Date: Wed, 3 Jun 2015 22:57:18 -0700 Subject: [PATCH 04/18] Feat: Add .on subscribing to Mesh --- webgl-renderables/Mesh.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/webgl-renderables/Mesh.js b/webgl-renderables/Mesh.js index 11061838..c63a1158 100644 --- a/webgl-renderables/Mesh.js +++ b/webgl-renderables/Mesh.js @@ -43,7 +43,6 @@ var CallbackStore = require('../utilities/CallbackStore'); function Mesh (node, options) { this._node = node; this._changeQueue = []; - this._UIEvents = []; this._callbacks = new CallbackStore(); this._initialized = false; @@ -647,6 +646,22 @@ Mesh.prototype.onReceive = function onReceive(event, payload) { this._callbacks.trigger(event, payload); }; +/** + * Subscribes to a Mesh using + * + * @method on + * + * @param {String} event The event type (e.g. `click`). + * @param {Function} listener Handler function for the specified event type + * in which the payload event object will be + * passed into. + * + * @return {Function} A function to call if you want to remove the callback + */ +Mesh.prototype.on = function on(event, listener) { + return this._callbacks.on(event, listener); +}; + /** * Queues instance to be updated. * From 43a018f0a02fadcb26455e0acac5c82d7f3ccae9 Mon Sep 17 00:00:00 2001 From: Farhad Ghayour Date: Wed, 3 Jun 2015 23:01:32 -0700 Subject: [PATCH 05/18] Feat: Add subscribe to WebGLRenderer --- renderers/Context.js | 5 +++++ webgl-renderers/WebGLRenderer.js | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/renderers/Context.js b/renderers/Context.js index 0481c48d..c430cf18 100644 --- a/renderers/Context.js +++ b/renderers/Context.js @@ -261,6 +261,11 @@ Context.prototype.receive = function receive(path, commands, iterator) { this.DOMRenderer.allowDefault(commands[++localIterator]); break; + case 'GL_SUBSCRIBE': + if (!this.WebGLRenderer) this.initWebGL(); + this.WebGLRenderer.subscribe(path, commands[++localIterator]); + break; + case 'GL_SET_DRAW_OPTIONS': if (!this.WebGLRenderer) this.initWebGL(); this.WebGLRenderer.setMeshOptions(path, commands[++localIterator]); diff --git a/webgl-renderers/WebGLRenderer.js b/webgl-renderers/WebGLRenderer.js index bda15f38..5394ec3b 100644 --- a/webgl-renderers/WebGLRenderer.js +++ b/webgl-renderers/WebGLRenderer.js @@ -134,8 +134,26 @@ function WebGLRenderer(canvas, compositor) { this.bufferRegistry.allocate(cutout.spec.id, 'a_texCoord', cutout.spec.bufferValues[1], 2); this.bufferRegistry.allocate(cutout.spec.id, 'a_normals', cutout.spec.bufferValues[2], 3); this.bufferRegistry.allocate(cutout.spec.id, 'indices', cutout.spec.bufferValues[3], 1); + + this.listeners = []; } +/** + * Attaches an EventListener to the element associated with the passed in path. + * + * @method + * + * @param {String} path Path for the given Mesh. + * @param {String} type event type (e.g. click). + * + * @return {undefined} undefined + */ +WebGLRenderer.prototype.subscribe = function subscribe(path, type) { + var mesh = this.meshRegistry[path]; + this.listeners[mesh.id] = mesh; + return this; +}; + /** * Attempts to retreive the WebGLRenderer context using several * accessors. For browser compatability. Throws on error. @@ -839,4 +857,12 @@ WebGLRenderer.prototype.resetOptions = function resetOptions(options) { if (options.side === 'back') gl.cullFace(gl.BACK); }; +WebGLRenderer.DEFAULT_STYLES = { + pointerEvents: 'none', + position: 'absolute', + zIndex: 1, + top: '0px', + left: '0px' +}; + module.exports = WebGLRenderer; From 740b01327d41f72d5a352777b2dd048ed7a277e5 Mon Sep 17 00:00:00 2001 From: Farhad Ghayour Date: Wed, 3 Jun 2015 23:03:53 -0700 Subject: [PATCH 06/18] Feat: Add click event to WebGL canvas --- webgl-renderers/WebGLRenderer.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/webgl-renderers/WebGLRenderer.js b/webgl-renderers/WebGLRenderer.js index 5394ec3b..0a894823 100644 --- a/webgl-renderers/WebGLRenderer.js +++ b/webgl-renderers/WebGLRenderer.js @@ -135,9 +135,35 @@ function WebGLRenderer(canvas, compositor) { this.bufferRegistry.allocate(cutout.spec.id, 'a_normals', cutout.spec.bufferValues[2], 3); this.bufferRegistry.allocate(cutout.spec.id, 'indices', cutout.spec.bufferValues[3], 1); + /** + * WebGL Picking for hit testing + */ this.listeners = []; + this.meshIds = 0; + this.canvas.onmousedown = this.handleClick.bind(this); } +/** + * Handle mousedown clicks on Canvas for determining the position of the cursor, in order to do WebGL picking. + * + * @method + * + * @param {Object} ev Event object + * + * @return {undefined} undefined + */ +WebGLRenderer.prototype.handleClick = function handleClicke(ev) { + var _this = this; + + var x = ev.clientX, y = ev.clientY; + var rect = ev.target.getBoundingClientRect(); + if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) { + x = x - rect.left, y = rect.bottom - y; + } + + return this; +}; + /** * Attaches an EventListener to the element associated with the passed in path. * From 2959ff6ff09ebc8211d8b708c53a1c7c96ba0260 Mon Sep 17 00:00:00 2001 From: Farhad Ghayour Date: Wed, 3 Jun 2015 23:07:14 -0700 Subject: [PATCH 07/18] Feat: Add Canvas alpha color picking --- webgl-renderers/Program.js | 13 +++++------ webgl-renderers/WebGLRenderer.js | 38 +++++++++++++++++++++++++++---- webgl-shaders/FragmentShader.glsl | 36 ++++++++++++++++++----------- 3 files changed, 63 insertions(+), 24 deletions(-) diff --git a/webgl-renderers/Program.js b/webgl-renderers/Program.js index 526f8e64..55e04c29 100644 --- a/webgl-renderers/Program.js +++ b/webgl-renderers/Program.js @@ -80,7 +80,9 @@ var uniforms = keyValueToArrays({ u_lightColor: identityMatrix, u_ambientLight: [0, 0, 0], u_flatShading: 0, - u_numLights: 0 + u_numLights: 0, + u_clicked: 0, + u_meshIdColor: [-1, -1, -1, -1] }); /** @@ -389,13 +391,10 @@ Program.prototype.setUniforms = function (uniformNames, uniformValue) { // requesting a new location from the WebGL context // if it does not yet exist. - location = this.uniformLocations[name]; + location = this.uniformLocations[name] || gl.getUniformLocation(this.program, name); + if (!location) continue; - if (location === null) continue; - if (location === undefined) { - location = gl.getUniformLocation(this.program, name); - this.uniformLocations[name] = location; - } + this.uniformLocations[name] = location; // Check if the value is already set for the // given uniform. diff --git a/webgl-renderers/WebGLRenderer.js b/webgl-renderers/WebGLRenderer.js index 0a894823..8236f447 100644 --- a/webgl-renderers/WebGLRenderer.js +++ b/webgl-renderers/WebGLRenderer.js @@ -153,17 +153,43 @@ function WebGLRenderer(canvas, compositor) { * @return {undefined} undefined */ WebGLRenderer.prototype.handleClick = function handleClicke(ev) { - var _this = this; - var x = ev.clientX, y = ev.clientY; + var pixelRatio = window.devicePixelRatio || 1; var rect = ev.target.getBoundingClientRect(); if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) { x = x - rect.left, y = rect.bottom - y; + this.check(x, y); } return this; }; +/** + * Determine the alpha channel, given an x and y coordinate for the WebGL canvas. + * + * @method + * + * @param {Number} x X coordinate + * @param {Number} y Y coordinate + * + * @return {undefined} undefined + */ +WebGLRenderer.prototype.check = function check(x, y) { + var gl = this.gl; + + gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); + this.program.setUniforms(['u_clicked'], [1.0]); + this.drawMeshes(); + + var pixels = new Uint8Array(4); + gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels); + + this.program.setUniforms(['u_clicked'], [0.0]); + this.drawMeshes(); + + return this.listeners[pixels[3]]; +}; + /** * Attaches an EventListener to the element associated with the passed in path. * @@ -240,6 +266,7 @@ WebGLRenderer.prototype.createLight = function createLight(path) { */ WebGLRenderer.prototype.createMesh = function createMesh(path) { this.meshRegistryKeys.push(path); + var meshIdColor = ++this.meshIds; var uniforms = keyValueToArrays({ u_opacity: 1, @@ -249,7 +276,8 @@ WebGLRenderer.prototype.createMesh = function createMesh(path) { u_positionOffset: [0, 0, 0], u_normals: [0, 0, 0], u_flatShading: 0, - u_glossiness: [0, 0, 0, 0] + u_glossiness: [0, 0, 0, 0], + u_meshIdColor: [0.0, 0.0, 0.0, meshIdColor/255] }); this.meshRegistry[path] = { depth: null, @@ -259,7 +287,9 @@ WebGLRenderer.prototype.createMesh = function createMesh(path) { geometry: null, drawType: null, textures: [], - visible: true + visible: true, + path: path, + id: meshIdColor }; return this.meshRegistry[path]; }; diff --git a/webgl-shaders/FragmentShader.glsl b/webgl-shaders/FragmentShader.glsl index a0eed7ce..d1541ce8 100644 --- a/webgl-shaders/FragmentShader.glsl +++ b/webgl-shaders/FragmentShader.glsl @@ -1,18 +1,18 @@ /** * The MIT License (MIT) - * + * * Copyright (c) 2015 Famous Industries Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -35,19 +35,29 @@ * */ void main() { - vec4 material = u_baseColor.r >= 0.0 ? u_baseColor : applyMaterial(u_baseColor); /** - * Apply lights only if flat shading is false - * and at least one light is added to the scene + * Writes the encoded Mesh IDs into the color channels, + * when the WebGL canvas has been clicked. */ - bool lightsEnabled = (u_flatShading == 0.0) && (u_numLights > 0.0 || length(u_ambientLight) > 0.0); + if (u_clicked > 0.0) { + gl_FragColor = u_meshIdColor; + } + else { + vec4 material = u_baseColor.r >= 0.0 ? u_baseColor : applyMaterial(u_baseColor); + + /** + * Apply lights only if flat shading is false + * and at least one light is added to the scene + */ + bool lightsEnabled = (u_flatShading == 0.0) && (u_numLights > 0.0 || length(u_ambientLight) > 0.0); - vec3 normal = normalize(v_normal); - vec4 glossiness = u_glossiness.x < 0.0 ? applyMaterial(u_glossiness) : u_glossiness; + vec3 normal = normalize(v_normal); + vec4 glossiness = u_glossiness.x < 0.0 ? applyMaterial(u_glossiness) : u_glossiness; - vec4 color = lightsEnabled ? applyLight(material, normalize(v_normal), glossiness) : material; + vec4 color = lightsEnabled ? applyLight(material, normalize(v_normal), glossiness) : material; - gl_FragColor = color; - gl_FragColor.a *= u_opacity; + gl_FragColor = color; + gl_FragColor.a *= u_opacity; + } } From f95f7f4a8aa0608abff9cbefe070ad30c5bce0d8 Mon Sep 17 00:00:00 2001 From: Farhad Ghayour Date: Wed, 3 Jun 2015 23:09:08 -0700 Subject: [PATCH 08/18] Fix: Update Canvas color pick for Retina displays --- webgl-renderers/WebGLRenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webgl-renderers/WebGLRenderer.js b/webgl-renderers/WebGLRenderer.js index 8236f447..6a26d13a 100644 --- a/webgl-renderers/WebGLRenderer.js +++ b/webgl-renderers/WebGLRenderer.js @@ -158,7 +158,7 @@ WebGLRenderer.prototype.handleClick = function handleClicke(ev) { var rect = ev.target.getBoundingClientRect(); if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) { x = x - rect.left, y = rect.bottom - y; - this.check(x, y); + this.check(x * pixelRatio, y * pixelRatio); } return this; From 57a4e74a829a3542a56676618d88c1f97770d5ce Mon Sep 17 00:00:00 2001 From: Farhad Ghayour Date: Wed, 3 Jun 2015 23:16:16 -0700 Subject: [PATCH 09/18] Feat: Ability to encode millions of pickable meshes --- webgl-renderables/Mesh.js | 1 + webgl-renderers/WebGLRenderer.js | 87 +++++++++++++++++++++++++++----- 2 files changed, 75 insertions(+), 13 deletions(-) diff --git a/webgl-renderables/Mesh.js b/webgl-renderables/Mesh.js index c63a1158..65ef72e6 100644 --- a/webgl-renderables/Mesh.js +++ b/webgl-renderables/Mesh.js @@ -43,6 +43,7 @@ var CallbackStore = require('../utilities/CallbackStore'); function Mesh (node, options) { this._node = node; this._changeQueue = []; + this._UIEvents = []; this._callbacks = new CallbackStore(); this._initialized = false; diff --git a/webgl-renderers/WebGLRenderer.js b/webgl-renderers/WebGLRenderer.js index 6a26d13a..27b162bf 100644 --- a/webgl-renderers/WebGLRenderer.js +++ b/webgl-renderers/WebGLRenderer.js @@ -153,12 +153,18 @@ function WebGLRenderer(canvas, compositor) { * @return {undefined} undefined */ WebGLRenderer.prototype.handleClick = function handleClicke(ev) { - var x = ev.clientX, y = ev.clientY; - var pixelRatio = window.devicePixelRatio || 1; + var x = ev.clientX; + var y = ev.clientY; + + this.pixelRatio = window.devicePixelRatio || 1; var rect = ev.target.getBoundingClientRect(); - if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) { - x = x - rect.left, y = rect.bottom - y; - this.check(x * pixelRatio, y * pixelRatio); + + if (rect.left <= x && x < rect.right && + rect.top <= y && y < rect.bottom) { + + var canvasX = (x - rect.left) * this.pixelRatio; + var canvasY = (rect.bottom - y) * this.pixelRatio; + this.check(canvasX, canvasY); } return this; @@ -187,7 +193,14 @@ WebGLRenderer.prototype.check = function check(x, y) { this.program.setUniforms(['u_clicked'], [0.0]); this.drawMeshes(); - return this.listeners[pixels[3]]; + var meshId = this.decodeMeshIdColor(pixels, 255); + var picked = this.listeners[meshId]; + + if (picked) { + this.compositor.sendEvent(picked.path, 'click', {}); + } + + return picked; }; /** @@ -255,6 +268,54 @@ WebGLRenderer.prototype.createLight = function createLight(path) { return this.lightRegistry[path]; }; +/** + * Algorithm for encoding an ID to the normalized RGBA hash in base 255. + * + * @method + * + * @param {Number} meshId Mesh ID number + * @param {Number} base Base for conversion + * + * @returns {Array} Encoded value + */ +WebGLRenderer.prototype.encodedMeshIdColor = function encodedMeshIdColor(meshId, base) { + var result = []; + + while (meshId) { + var normalizedRemainder = (meshId%base) / 255; + result.unshift(normalizedRemainder); + meshId = (meshId / base) | 0; + } + + while (result.length !== 4) { + result.unshift(0); + } + + return result; +}; + +/** + * Algorithm for decoding the mesh ID from the WebGL shader. + * + * @method + * + * @param {Buffer} pixelsBuffer Pixel buffer from the WebGL shader + * @param {Number} base Base for conversion + * + * @returns {Number} Mesh ID + */ +WebGLRenderer.prototype.decodeMeshIdColor = function decodeMeshIdColor(pixelsBuffer, base) { + var result = 0; + var baseColumn = 0; + + var len = pixelsBuffer.length - 1; + for(var i = len; i >= 0; i--) { + result += pixelsBuffer[i] * Math.pow(base, baseColumn++); + } + + return result; +}; + /** * Adds a new base spec to the mesh registry at a given path. * @@ -266,7 +327,6 @@ WebGLRenderer.prototype.createLight = function createLight(path) { */ WebGLRenderer.prototype.createMesh = function createMesh(path) { this.meshRegistryKeys.push(path); - var meshIdColor = ++this.meshIds; var uniforms = keyValueToArrays({ u_opacity: 1, @@ -277,7 +337,7 @@ WebGLRenderer.prototype.createMesh = function createMesh(path) { u_normals: [0, 0, 0], u_flatShading: 0, u_glossiness: [0, 0, 0, 0], - u_meshIdColor: [0.0, 0.0, 0.0, meshIdColor/255] + u_meshIdColor: this.encodedMeshIdColor(++this.meshIds, 255) }); this.meshRegistry[path] = { depth: null, @@ -289,7 +349,7 @@ WebGLRenderer.prototype.createMesh = function createMesh(path) { textures: [], visible: true, path: path, - id: meshIdColor + id: this.meshIds }; return this.meshRegistry[path]; }; @@ -619,7 +679,8 @@ WebGLRenderer.prototype.drawMeshes = function drawMeshes() { var buffers; var mesh; - for(var i = 0; i < this.meshRegistryKeys.length; i++) { + var len = this.meshRegistryKeys.length; + for(var i = 0; i < len; i++) { mesh = this.meshRegistry[this.meshRegistryKeys[i]]; buffers = this.bufferRegistry.registry[mesh.geometry]; @@ -853,9 +914,9 @@ WebGLRenderer.prototype.drawBuffers = function drawBuffers(vertexBuffers, mode, */ WebGLRenderer.prototype.updateSize = function updateSize(size) { if (size) { - var pixelRatio = window.devicePixelRatio || 1; - var displayWidth = ~~(size[0] * pixelRatio); - var displayHeight = ~~(size[1] * pixelRatio); + this.pixelRatio = window.devicePixelRatio || 1; + var displayWidth = ~~(size[0] * this.pixelRatio); + var displayHeight = ~~(size[1] * this.pixelRatio); this.canvas.width = displayWidth; this.canvas.height = displayHeight; this.gl.viewport(0, 0, displayWidth, displayHeight); From 9914b25bde16bb274f715e816cd2103f4acc8b8d Mon Sep 17 00:00:00 2001 From: Farhad Ghayour Date: Thu, 4 Jun 2015 00:40:57 -0700 Subject: [PATCH 10/18] Feat: GL unsubscribing functionality --- core/Node.js | 32 ++++++++++++++++++-- renderers/Context.js | 5 ++++ webgl-renderables/Mesh.js | 51 +++++++++++++++++++++++++++----- webgl-renderers/WebGLRenderer.js | 21 ++++++++++++- 4 files changed, 97 insertions(+), 12 deletions(-) diff --git a/core/Node.js b/core/Node.js index cf8c7b36..878e7cde 100644 --- a/core/Node.js +++ b/core/Node.js @@ -641,8 +641,8 @@ Node.prototype.getComponent = function getComponent (index) { * * @method removeComponent * - * @param {Object} component An component that has previously been added - * using {@link Node#addComponent}. + * @param {Object} component A component that has previously been added + * using @{@link addComponent}. * * @return {Node} this */ @@ -661,6 +661,32 @@ Node.prototype.removeComponent = function removeComponent (component) { return component; }; +/** +* Removes a node's subscription to a particular UIEvent. All components +* on the node will have the opportunity to remove all listeners depending +* on this event. +* +* @method +* +* @param {String} eventName the name of the event +* +* @return {undefined} undefined +*/ +Node.prototype.removeUIEvent = function removeUIEvent (eventName) { + var UIEvents = this.getUIEvents(); + var components = this._components; + var component; + + var index = UIEvents.indexOf(eventName); + if (index !== -1) { + UIEvents.splice(index, 1); + for (var i = 0, len = components.length ; i < len ; i++) { + component = components[i]; + if (component && component.onRemoveUIEvent) component.onRemoveUIEvent(eventName); + } + } +}; + /** * Subscribes a node to a UI Event. All components on the node * will have the opportunity to begin listening to that event @@ -1105,7 +1131,7 @@ Node.prototype.setOpacity = function setOpacity (val) { * Sets the size mode being used for determining the node's final width, height * and depth. * Size modes are a way to define the way the node's size is being calculated. - * Size modes are enums set on the {@link Size} constructor (and aliased on + * Size modes are enums set on the @{@link Size} constructor (and aliased on * the Node). * * @example diff --git a/renderers/Context.js b/renderers/Context.js index c430cf18..66949637 100644 --- a/renderers/Context.js +++ b/renderers/Context.js @@ -266,6 +266,11 @@ Context.prototype.receive = function receive(path, commands, iterator) { this.WebGLRenderer.subscribe(path, commands[++localIterator]); break; + case 'GL_UNSUBSCRIBE': + if (!this.WebGLRenderer) this.initWebGL(); + this.WebGLRenderer.unsubscribe(path, commands[++localIterator]); + break; + case 'GL_SET_DRAW_OPTIONS': if (!this.WebGLRenderer) this.initWebGL(); this.WebGLRenderer.setMeshOptions(path, commands[++localIterator]); diff --git a/webgl-renderables/Mesh.js b/webgl-renderables/Mesh.js index 65ef72e6..93acce9f 100644 --- a/webgl-renderables/Mesh.js +++ b/webgl-renderables/Mesh.js @@ -589,11 +589,10 @@ Mesh.prototype.onOpacityChange = function onOpacityChange (opacity) { }; /** - * Adds functionality for UI events + * Method to be invoked by the node as soon as a new UIEvent is being added. + * This results into an `SUBSCRIBE` command being sent. * - * @method - * - * @param {String} UIEvent UI Event + * @param {String} UIEvent UIEvent to be subscribed to (`click`) * * @return {undefined} undefined */ @@ -609,12 +608,32 @@ Mesh.prototype.onAddUIEvent = function onAddUIEvent (UIEvent) { }; /** - * Appends an `ADD_EVENT_LISTENER` command to the command queue. + * Method to be invoked by the node as soon as a UIEvent is removed from + * the node. This results into an `UNSUBSCRIBE` command being sent. + * + * @param {String} UIEvent UIEvent to be removed (`click`) + * + * @return {undefined} undefined + */ +Mesh.prototype.onRemoveUIEvent = function onRemoveUIEvent(UIEvent) { + var index = this._UIEvents.indexOf(UIEvent); + if (index !== -1) { + this._unsubscribe(UIEvent); + this._UIEvents.splice(index, 1); + } + else if (this._inDraw) { + this._unsubscribe(UIEvent); + } + return this; +}; + +/** + * Appends an `SUBSCRIBE` command to the command queue. * * @method * @private * - * @param {String} UIEvent Event type (e.g. `click`) + * @param {String} UIEvent Event type (`click`) * * @return {undefined} undefined */ @@ -622,9 +641,25 @@ Mesh.prototype._subscribe = function _subscribe(UIEvent) { if (this._initialized) { this._changeQueue.push('GL_SUBSCRIBE', UIEvent); } - if (!this._requestingUpdate) { - this._requestUpdate(); + + if (!this._requestingUpdate) this._requestUpdate(); +}; + +/** + * Appends an `UNSUBSCRIBE` command to the command queue. + * + * @method + * @private + * + * @param {String} UIEvent Event type (`click`) + * + * @return {undefined} undefined + */ +Mesh.prototype._unsubscribe = function _unsubscribe (UIEvent) { + if (this._initialized) { + this._changeQueue.push('GL_UNSUBSCRIBE', UIEvent); } + if (!this._requestingUpdate) this._requestUpdate(); }; diff --git a/webgl-renderers/WebGLRenderer.js b/webgl-renderers/WebGLRenderer.js index 27b162bf..b8f5fd01 100644 --- a/webgl-renderers/WebGLRenderer.js +++ b/webgl-renderers/WebGLRenderer.js @@ -209,16 +209,35 @@ WebGLRenderer.prototype.check = function check(x, y) { * @method * * @param {String} path Path for the given Mesh. - * @param {String} type event type (e.g. click). + * @param {String} type Event type ('click'). * * @return {undefined} undefined */ WebGLRenderer.prototype.subscribe = function subscribe(path, type) { + if (type !== 'click') return false; var mesh = this.meshRegistry[path]; this.listeners[mesh.id] = mesh; return this; }; +/** + * Removes an EventListener of given type from the element on which it was + * registered. + * + * @method + * + * @param {String} path Path for the given Mesh. + * @param {String} type Event type ('click'). + * + * @return {undefined} undefined + */ +WebGLRenderer.prototype.unsubscribe = function unsubscribe(path, type) { + if (type !== 'click') return false; + var mesh = this.meshRegistry[path]; + this.listeners.splice(mesh.id, 1); + return this; +}; + /** * Attempts to retreive the WebGLRenderer context using several * accessors. For browser compatability. Throws on error. From bf5938bcea08566fc66fc55d8b0439b8ab823cc9 Mon Sep 17 00:00:00 2001 From: Farhad Ghayour Date: Thu, 4 Jun 2015 01:18:57 -0700 Subject: [PATCH 11/18] Fix: Update Mesh ID encoding function name --- webgl-renderers/WebGLRenderer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webgl-renderers/WebGLRenderer.js b/webgl-renderers/WebGLRenderer.js index b8f5fd01..71e6cb76 100644 --- a/webgl-renderers/WebGLRenderer.js +++ b/webgl-renderers/WebGLRenderer.js @@ -297,7 +297,7 @@ WebGLRenderer.prototype.createLight = function createLight(path) { * * @returns {Array} Encoded value */ -WebGLRenderer.prototype.encodedMeshIdColor = function encodedMeshIdColor(meshId, base) { +WebGLRenderer.prototype.encodeMeshIdColor = function encodeMeshIdColor(meshId, base) { var result = []; while (meshId) { @@ -356,7 +356,7 @@ WebGLRenderer.prototype.createMesh = function createMesh(path) { u_normals: [0, 0, 0], u_flatShading: 0, u_glossiness: [0, 0, 0, 0], - u_meshIdColor: this.encodedMeshIdColor(++this.meshIds, 255) + u_meshIdColor: this.encodeMeshIdColor(++this.meshIds, 255) }); this.meshRegistry[path] = { depth: null, From 49f46efe90fab71e14430eb2d2de4c2339acd0b6 Mon Sep 17 00:00:00 2001 From: Farhad Ghayour Date: Thu, 4 Jun 2015 01:28:29 -0700 Subject: [PATCH 12/18] Clean: Remove bind from canvas event --- webgl-renderers/WebGLRenderer.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/webgl-renderers/WebGLRenderer.js b/webgl-renderers/WebGLRenderer.js index 71e6cb76..c16cc7ff 100644 --- a/webgl-renderers/WebGLRenderer.js +++ b/webgl-renderers/WebGLRenderer.js @@ -61,6 +61,7 @@ var globalUniforms = keyValueToArrays({ function WebGLRenderer(canvas, compositor) { canvas.classList.add('famous-webgl-renderer'); + var _this = this; this.canvas = canvas; this.compositor = compositor; @@ -140,7 +141,9 @@ function WebGLRenderer(canvas, compositor) { */ this.listeners = []; this.meshIds = 0; - this.canvas.onmousedown = this.handleClick.bind(this); + canvas.onmousedown = function(ev) { + _this.handleClick(ev); + }; } /** @@ -152,7 +155,7 @@ function WebGLRenderer(canvas, compositor) { * * @return {undefined} undefined */ -WebGLRenderer.prototype.handleClick = function handleClicke(ev) { +WebGLRenderer.prototype.handleClick = function handleClick(ev) { var x = ev.clientX; var y = ev.clientY; From 6f6d3f696b21126474d39c699c3a9604b6a2b79c Mon Sep 17 00:00:00 2001 From: Farhad Ghayour Date: Thu, 4 Jun 2015 01:30:14 -0700 Subject: [PATCH 13/18] Clean: Device pixel ratio determined in the WebGL constructor --- webgl-renderers/WebGLRenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webgl-renderers/WebGLRenderer.js b/webgl-renderers/WebGLRenderer.js index c16cc7ff..2a26e954 100644 --- a/webgl-renderers/WebGLRenderer.js +++ b/webgl-renderers/WebGLRenderer.js @@ -64,6 +64,7 @@ function WebGLRenderer(canvas, compositor) { var _this = this; this.canvas = canvas; this.compositor = compositor; + this.pixelRatio = window.devicePixelRatio || 1; var gl = this.gl = this.getWebGLContext(this.canvas); @@ -159,7 +160,6 @@ WebGLRenderer.prototype.handleClick = function handleClick(ev) { var x = ev.clientX; var y = ev.clientY; - this.pixelRatio = window.devicePixelRatio || 1; var rect = ev.target.getBoundingClientRect(); if (rect.left <= x && x < rect.right && From 9fc9c14da43f229690b571703410a99a16736b30 Mon Sep 17 00:00:00 2001 From: Farhad Ghayour Date: Thu, 4 Jun 2015 01:30:57 -0700 Subject: [PATCH 14/18] Clean: Remove default styles --- webgl-renderers/WebGLRenderer.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/webgl-renderers/WebGLRenderer.js b/webgl-renderers/WebGLRenderer.js index 2a26e954..9fb4601c 100644 --- a/webgl-renderers/WebGLRenderer.js +++ b/webgl-renderers/WebGLRenderer.js @@ -996,12 +996,4 @@ WebGLRenderer.prototype.resetOptions = function resetOptions(options) { if (options.side === 'back') gl.cullFace(gl.BACK); }; -WebGLRenderer.DEFAULT_STYLES = { - pointerEvents: 'none', - position: 'absolute', - zIndex: 1, - top: '0px', - left: '0px' -}; - module.exports = WebGLRenderer; From de72646fd590a12b907b7224ef19460e448ae810 Mon Sep 17 00:00:00 2001 From: Farhad Ghayour Date: Thu, 4 Jun 2015 01:43:28 -0700 Subject: [PATCH 15/18] Perf: Bit wise math floor --- webgl-renderers/WebGLRenderer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webgl-renderers/WebGLRenderer.js b/webgl-renderers/WebGLRenderer.js index 9fb4601c..833b8377 100644 --- a/webgl-renderers/WebGLRenderer.js +++ b/webgl-renderers/WebGLRenderer.js @@ -937,8 +937,8 @@ WebGLRenderer.prototype.drawBuffers = function drawBuffers(vertexBuffers, mode, WebGLRenderer.prototype.updateSize = function updateSize(size) { if (size) { this.pixelRatio = window.devicePixelRatio || 1; - var displayWidth = ~~(size[0] * this.pixelRatio); - var displayHeight = ~~(size[1] * this.pixelRatio); + var displayWidth = (size[0] * this.pixelRatio) | 0; + var displayHeight = (size[1] * this.pixelRatio) | 0; this.canvas.width = displayWidth; this.canvas.height = displayHeight; this.gl.viewport(0, 0, displayWidth, displayHeight); From 0caab09832a660f03c2df4ba16da24940b8e16a5 Mon Sep 17 00:00:00 2001 From: Farhad Ghayour Date: Thu, 4 Jun 2015 16:53:03 -0700 Subject: [PATCH 16/18] Fix: Allow for DOM and GL events --- renderers/Context.js | 2 +- webgl-renderables/Mesh.js | 8 ++--- webgl-renderers/Program.js | 11 +++--- webgl-renderers/WebGLRenderer.js | 60 ++++++++++++++++++++----------- webgl-shaders/FragmentShader.glsl | 2 +- 5 files changed, 52 insertions(+), 31 deletions(-) diff --git a/renderers/Context.js b/renderers/Context.js index 66949637..0f8acbdd 100644 --- a/renderers/Context.js +++ b/renderers/Context.js @@ -147,7 +147,7 @@ Context.prototype.getRootSize = function getRootSize() { Context.prototype.initWebGL = function initWebGL() { this.canvas = document.createElement('canvas'); this._rootEl.appendChild(this.canvas); - this.WebGLRenderer = new WebGLRenderer(this.canvas, this._compositor); + this.WebGLRenderer = new WebGLRenderer(this.canvas, this._compositor, this.DOMLayerEl); this.updateSize(); }; diff --git a/webgl-renderables/Mesh.js b/webgl-renderables/Mesh.js index 93acce9f..cd1d09de 100644 --- a/webgl-renderables/Mesh.js +++ b/webgl-renderables/Mesh.js @@ -592,7 +592,7 @@ Mesh.prototype.onOpacityChange = function onOpacityChange (opacity) { * Method to be invoked by the node as soon as a new UIEvent is being added. * This results into an `SUBSCRIBE` command being sent. * - * @param {String} UIEvent UIEvent to be subscribed to (`click`) + * @param {String} UIEvent UIEvent to be subscribed to (e.g. `click`) * * @return {undefined} undefined */ @@ -611,7 +611,7 @@ Mesh.prototype.onAddUIEvent = function onAddUIEvent (UIEvent) { * Method to be invoked by the node as soon as a UIEvent is removed from * the node. This results into an `UNSUBSCRIBE` command being sent. * - * @param {String} UIEvent UIEvent to be removed (`click`) + * @param {String} UIEvent UIEvent to be removed (e.g. `click`) * * @return {undefined} undefined */ @@ -633,7 +633,7 @@ Mesh.prototype.onRemoveUIEvent = function onRemoveUIEvent(UIEvent) { * @method * @private * - * @param {String} UIEvent Event type (`click`) + * @param {String} UIEvent Event type (e.g. `click`) * * @return {undefined} undefined */ @@ -651,7 +651,7 @@ Mesh.prototype._subscribe = function _subscribe(UIEvent) { * @method * @private * - * @param {String} UIEvent Event type (`click`) + * @param {String} UIEvent Event type (e.g. `click`) * * @return {undefined} undefined */ diff --git a/webgl-renderers/Program.js b/webgl-renderers/Program.js index 55e04c29..912478ea 100644 --- a/webgl-renderers/Program.js +++ b/webgl-renderers/Program.js @@ -81,7 +81,7 @@ var uniforms = keyValueToArrays({ u_ambientLight: [0, 0, 0], u_flatShading: 0, u_numLights: 0, - u_clicked: 0, + u_pickingMode: 0, u_meshIdColor: [-1, -1, -1, -1] }); @@ -391,10 +391,13 @@ Program.prototype.setUniforms = function (uniformNames, uniformValue) { // requesting a new location from the WebGL context // if it does not yet exist. - location = this.uniformLocations[name] || gl.getUniformLocation(this.program, name); - if (!location) continue; + location = this.uniformLocations[name]; - this.uniformLocations[name] = location; + if (location === null) continue; + if (location === undefined) { + location = gl.getUniformLocation(this.program, name); + this.uniformLocations[name] = location; + } // Check if the value is already set for the // given uniform. diff --git a/webgl-renderers/WebGLRenderer.js b/webgl-renderers/WebGLRenderer.js index 833b8377..8414b162 100644 --- a/webgl-renderers/WebGLRenderer.js +++ b/webgl-renderers/WebGLRenderer.js @@ -55,10 +55,11 @@ var globalUniforms = keyValueToArrays({ * * @param {Element} canvas The DOM element that GL will paint itself onto. * @param {Compositor} compositor Compositor used for querying the time from. + * @param {Element} eventDiv The main DOM element that is used for capturing events. * * @return {undefined} undefined */ -function WebGLRenderer(canvas, compositor) { +function WebGLRenderer(canvas, compositor, eventDiv) { canvas.classList.add('famous-webgl-renderer'); var _this = this; @@ -138,13 +139,22 @@ function WebGLRenderer(canvas, compositor) { this.bufferRegistry.allocate(cutout.spec.id, 'indices', cutout.spec.bufferValues[3], 1); /** - * WebGL Picking for hit testing + * WebGL Picking by caputing hit testing ('clicks') using the + * main famous HTML element for capturing events. */ - this.listeners = []; + this.listeners = {}; this.meshIds = 0; - canvas.onmousedown = function(ev) { - _this.handleClick(ev); - }; + this.eventsMap = ['click']; + var ev; + + var len = this.eventsMap.length; + for(var i = 0; i < len; i++) { + ev = this.eventsMap[i]; + this.listeners[ev] = []; + eventDiv.addEventListener(ev, function(e) { + _this.handleEvent(e); + }); + } } /** @@ -156,18 +166,20 @@ function WebGLRenderer(canvas, compositor) { * * @return {undefined} undefined */ -WebGLRenderer.prototype.handleClick = function handleClick(ev) { +WebGLRenderer.prototype.handleEvent = function handleEvent(ev) { var x = ev.clientX; var y = ev.clientY; + var canvasX; + var canvasY; var rect = ev.target.getBoundingClientRect(); if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) { - var canvasX = (x - rect.left) * this.pixelRatio; - var canvasY = (rect.bottom - y) * this.pixelRatio; - this.check(canvasX, canvasY); + canvasX = (x - rect.left) * this.pixelRatio; + canvasY = (rect.bottom - y) * this.pixelRatio; + this.checkEvent(canvasX, canvasY, ev.type); } return this; @@ -180,24 +192,25 @@ WebGLRenderer.prototype.handleClick = function handleClick(ev) { * * @param {Number} x X coordinate * @param {Number} y Y coordinate + * @param {String} type Event type * * @return {undefined} undefined */ -WebGLRenderer.prototype.check = function check(x, y) { +WebGLRenderer.prototype.checkEvent = function checkEvent(x, y, type) { var gl = this.gl; gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); - this.program.setUniforms(['u_clicked'], [1.0]); + this.program.setUniforms(['u_pickingMode'], [1.0]); this.drawMeshes(); var pixels = new Uint8Array(4); gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels); - this.program.setUniforms(['u_clicked'], [0.0]); + this.program.setUniforms(['u_pickingMode'], [0.0]); this.drawMeshes(); var meshId = this.decodeMeshIdColor(pixels, 255); - var picked = this.listeners[meshId]; + var picked = this.listeners[type][meshId]; if (picked) { this.compositor.sendEvent(picked.path, 'click', {}); @@ -212,14 +225,16 @@ WebGLRenderer.prototype.check = function check(x, y) { * @method * * @param {String} path Path for the given Mesh. - * @param {String} type Event type ('click'). + * @param {String} type Event type (e.g. 'click'). * * @return {undefined} undefined */ WebGLRenderer.prototype.subscribe = function subscribe(path, type) { - if (type !== 'click') return false; + if (this.eventsMap.indexOf(type) === -1) { + return false; + } var mesh = this.meshRegistry[path]; - this.listeners[mesh.id] = mesh; + this.listeners[type][mesh.id] = mesh; return this; }; @@ -230,14 +245,16 @@ WebGLRenderer.prototype.subscribe = function subscribe(path, type) { * @method * * @param {String} path Path for the given Mesh. - * @param {String} type Event type ('click'). + * @param {String} type Event type (e.g. 'click'). * * @return {undefined} undefined */ WebGLRenderer.prototype.unsubscribe = function unsubscribe(path, type) { - if (type !== 'click') return false; + if (this.eventsMap.indexOf(type) === -1) { + return false; + } var mesh = this.meshRegistry[path]; - this.listeners.splice(mesh.id, 1); + this.listeners[type].splice(mesh.id, 1); return this; }; @@ -302,9 +319,10 @@ WebGLRenderer.prototype.createLight = function createLight(path) { */ WebGLRenderer.prototype.encodeMeshIdColor = function encodeMeshIdColor(meshId, base) { var result = []; + var normalizedRemainder; while (meshId) { - var normalizedRemainder = (meshId%base) / 255; + normalizedRemainder = (meshId%base) / 255; result.unshift(normalizedRemainder); meshId = (meshId / base) | 0; } diff --git a/webgl-shaders/FragmentShader.glsl b/webgl-shaders/FragmentShader.glsl index d1541ce8..9e350923 100644 --- a/webgl-shaders/FragmentShader.glsl +++ b/webgl-shaders/FragmentShader.glsl @@ -40,7 +40,7 @@ void main() { * Writes the encoded Mesh IDs into the color channels, * when the WebGL canvas has been clicked. */ - if (u_clicked > 0.0) { + if (u_pickingMode > 0.0) { gl_FragColor = u_meshIdColor; } else { From 4502c8fe8f60655de1785426f1ca552adf804d2f Mon Sep 17 00:00:00 2001 From: Farhad Ghayour Date: Fri, 5 Jun 2015 17:05:18 -0700 Subject: [PATCH 17/18] Feat: WebGL events functionality for various discrete events --- webgl-renderables/Mesh.js | 2 +- webgl-renderers/WebGLRenderer.js | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/webgl-renderables/Mesh.js b/webgl-renderables/Mesh.js index cd1d09de..dfe68124 100644 --- a/webgl-renderables/Mesh.js +++ b/webgl-renderables/Mesh.js @@ -596,7 +596,7 @@ Mesh.prototype.onOpacityChange = function onOpacityChange (opacity) { * * @return {undefined} undefined */ -Mesh.prototype.onAddUIEvent = function onAddUIEvent (UIEvent) { +Mesh.prototype.onAddUIEvent = function onAddUIEvent(UIEvent) { if (this._UIEvents.indexOf(UIEvent) === -1) { this._subscribe(UIEvent); this._UIEvents.push(UIEvent); diff --git a/webgl-renderers/WebGLRenderer.js b/webgl-renderers/WebGLRenderer.js index 8414b162..4ec2bff4 100644 --- a/webgl-renderers/WebGLRenderer.js +++ b/webgl-renderers/WebGLRenderer.js @@ -139,13 +139,15 @@ function WebGLRenderer(canvas, compositor, eventDiv) { this.bufferRegistry.allocate(cutout.spec.id, 'indices', cutout.spec.bufferValues[3], 1); /** - * WebGL Picking by caputing hit testing ('clicks') using the + * WebGL Picking by caputing events (e.g. 'click') using the * main famous HTML element for capturing events. */ + var ev; this.listeners = {}; this.meshIds = 0; - this.eventsMap = ['click']; - var ev; + this.eventsMap = ['click', 'dblclick', 'mousewheel', 'touchstart', + 'keyup', 'keydown', 'mousedown', 'mouseup', + 'scroll', 'select', 'touchend', 'wheel']; var len = this.eventsMap.length; for(var i = 0; i < len; i++) { @@ -213,7 +215,7 @@ WebGLRenderer.prototype.checkEvent = function checkEvent(x, y, type) { var picked = this.listeners[type][meshId]; if (picked) { - this.compositor.sendEvent(picked.path, 'click', {}); + this.compositor.sendEvent(picked.path, type, {}); } return picked; From eec45aab8e3ab83a7539dbdce1340fb66704cb37 Mon Sep 17 00:00:00 2001 From: Farhad Ghayour Date: Mon, 8 Jun 2015 15:26:23 -0700 Subject: [PATCH 18/18] Clean: Allow for turning various events on/off --- core/Node.js | 6 +++-- renderers/Context.js | 2 +- webgl-renderables/Mesh.js | 4 +-- webgl-renderers/WebGLRenderer.js | 42 +++++++++++++++++++------------- 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/core/Node.js b/core/Node.js index 878e7cde..dfe139c6 100644 --- a/core/Node.js +++ b/core/Node.js @@ -670,7 +670,7 @@ Node.prototype.removeComponent = function removeComponent (component) { * * @param {String} eventName the name of the event * -* @return {undefined} undefined +* @return {Node} this */ Node.prototype.removeUIEvent = function removeUIEvent (eventName) { var UIEvents = this.getUIEvents(); @@ -685,6 +685,7 @@ Node.prototype.removeUIEvent = function removeUIEvent (eventName) { if (component && component.onRemoveUIEvent) component.onRemoveUIEvent(eventName); } } + return this; }; /** @@ -696,7 +697,7 @@ Node.prototype.removeUIEvent = function removeUIEvent (eventName) { * * @param {String} eventName the name of the event * - * @return {undefined} undefined + * @return {Node} this */ Node.prototype.addUIEvent = function addUIEvent (eventName) { var UIEvents = this.getUIEvents(); @@ -711,6 +712,7 @@ Node.prototype.addUIEvent = function addUIEvent (eventName) { if (component && component.onAddUIEvent) component.onAddUIEvent(eventName); } } + return this; }; /** diff --git a/renderers/Context.js b/renderers/Context.js index 0f8acbdd..c90e97ef 100644 --- a/renderers/Context.js +++ b/renderers/Context.js @@ -147,7 +147,7 @@ Context.prototype.getRootSize = function getRootSize() { Context.prototype.initWebGL = function initWebGL() { this.canvas = document.createElement('canvas'); this._rootEl.appendChild(this.canvas); - this.WebGLRenderer = new WebGLRenderer(this.canvas, this._compositor, this.DOMLayerEl); + this.WebGLRenderer = new WebGLRenderer(this.canvas, this._compositor, this._domLayerEl); this.updateSize(); }; diff --git a/webgl-renderables/Mesh.js b/webgl-renderables/Mesh.js index dfe68124..02049ba3 100644 --- a/webgl-renderables/Mesh.js +++ b/webgl-renderables/Mesh.js @@ -594,7 +594,7 @@ Mesh.prototype.onOpacityChange = function onOpacityChange (opacity) { * * @param {String} UIEvent UIEvent to be subscribed to (e.g. `click`) * - * @return {undefined} undefined + * @return {Mesh} this */ Mesh.prototype.onAddUIEvent = function onAddUIEvent(UIEvent) { if (this._UIEvents.indexOf(UIEvent) === -1) { @@ -613,7 +613,7 @@ Mesh.prototype.onAddUIEvent = function onAddUIEvent(UIEvent) { * * @param {String} UIEvent UIEvent to be removed (e.g. `click`) * - * @return {undefined} undefined + * @return {Mesh} this */ Mesh.prototype.onRemoveUIEvent = function onRemoveUIEvent(UIEvent) { var index = this._UIEvents.indexOf(UIEvent); diff --git a/webgl-renderers/WebGLRenderer.js b/webgl-renderers/WebGLRenderer.js index 4ec2bff4..3be5c733 100644 --- a/webgl-renderers/WebGLRenderer.js +++ b/webgl-renderers/WebGLRenderer.js @@ -141,17 +141,29 @@ function WebGLRenderer(canvas, compositor, eventDiv) { /** * WebGL Picking by caputing events (e.g. 'click') using the * main famous HTML element for capturing events. + * + * TODO: Famous config file should contain these options for flexibility. */ - var ev; this.listeners = {}; this.meshIds = 0; - this.eventsMap = ['click', 'dblclick', 'mousewheel', 'touchstart', - 'keyup', 'keydown', 'mousedown', 'mouseup', - 'scroll', 'select', 'touchend', 'wheel']; + this.eventsMap = { + click: true, + dblclick: true, + mousewheel: false, + touchstart: true, + keyup: true, + keydown: true, + mousedown: false, + mouseup: false, + scroll: false, + select: false, + touchend: false, + wheel: false + }; - var len = this.eventsMap.length; - for(var i = 0; i < len; i++) { - ev = this.eventsMap[i]; + // If event is tracked, set the listener + for(var ev in this.eventsMap) { + if (!this.eventsMap[ev]) continue; this.listeners[ev] = []; eventDiv.addEventListener(ev, function(e) { _this.handleEvent(e); @@ -232,12 +244,10 @@ WebGLRenderer.prototype.checkEvent = function checkEvent(x, y, type) { * @return {undefined} undefined */ WebGLRenderer.prototype.subscribe = function subscribe(path, type) { - if (this.eventsMap.indexOf(type) === -1) { - return false; + if (this.eventsMap[type]) { + var mesh = this.meshRegistry[path]; + this.listeners[type][mesh.id] = mesh; } - var mesh = this.meshRegistry[path]; - this.listeners[type][mesh.id] = mesh; - return this; }; /** @@ -252,12 +262,10 @@ WebGLRenderer.prototype.subscribe = function subscribe(path, type) { * @return {undefined} undefined */ WebGLRenderer.prototype.unsubscribe = function unsubscribe(path, type) { - if (this.eventsMap.indexOf(type) === -1) { - return false; + if (this.eventsMap[type]) { + var mesh = this.meshRegistry[path]; + this.listeners[type].splice(mesh.id, 1); } - var mesh = this.meshRegistry[path]; - this.listeners[type].splice(mesh.id, 1); - return this; }; /**