diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 28d8d5d..707da24 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -187,6 +187,7 @@ // this is where content gets renderd.. // watch this space for a little render state manager.. + renderer.setObjectRendererer(renderer.spriteBatch); renderer.spriteBatch.render(this); }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 28d8d5d..707da24 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -187,6 +187,7 @@ // this is where content gets renderd.. // watch this space for a little render state manager.. + renderer.setObjectRendererer(renderer.spriteBatch); renderer.spriteBatch.render(this); }; diff --git a/src/core/primitives/Graphics.js b/src/core/primitives/Graphics.js index 6ee7465..261c534 100644 --- a/src/core/primitives/Graphics.js +++ b/src/core/primitives/Graphics.js @@ -681,23 +681,17 @@ return; } - else */ + */ + + // check if the webgl graphic needs to be updated + if (this.glDirty) { + this.dirty = true; + this.glDirty = false; + } - renderer.spriteBatch.stop(); - renderer.blendModeManager.setBlendMode(this.blendMode); + renderer.setObjectRendererer(renderer.graphicsRenderer); + renderer.graphicsRenderer.render(this); - // check blend mode - renderer.blendModeManager.setBlendMode( this.blendMode ); - - // check if the webgl graphic needs to be updated - if (this.glDirty) { - this.dirty = true; - this.glDirty = false; - } - - WebGLGraphics.renderGraphics(this, renderer); - - renderer.spriteBatch.start(); }; /** diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 28d8d5d..707da24 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -187,6 +187,7 @@ // this is where content gets renderd.. // watch this space for a little render state manager.. + renderer.setObjectRendererer(renderer.spriteBatch); renderer.spriteBatch.render(this); }; diff --git a/src/core/primitives/Graphics.js b/src/core/primitives/Graphics.js index 6ee7465..261c534 100644 --- a/src/core/primitives/Graphics.js +++ b/src/core/primitives/Graphics.js @@ -681,23 +681,17 @@ return; } - else */ + */ + + // check if the webgl graphic needs to be updated + if (this.glDirty) { + this.dirty = true; + this.glDirty = false; + } - renderer.spriteBatch.stop(); - renderer.blendModeManager.setBlendMode(this.blendMode); + renderer.setObjectRendererer(renderer.graphicsRenderer); + renderer.graphicsRenderer.render(this); - // check blend mode - renderer.blendModeManager.setBlendMode( this.blendMode ); - - // check if the webgl graphic needs to be updated - if (this.glDirty) { - this.dirty = true; - this.glDirty = false; - } - - WebGLGraphics.renderGraphics(this, renderer); - - renderer.spriteBatch.start(); }; /** diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 0063821..1f3e655 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,4 +1,5 @@ var WebGLSpriteBatch = require('./utils/WebGLSpriteBatch'), + GraphicsRenderer = require('./utils/GraphicsRenderer'), WebGLFastSpriteBatch = require('./utils/WebGLFastSpriteBatch'), WebGLShaderManager = require('./managers/WebGLShaderManager'), WebGLMaskManager = require('./managers/WebGLMaskManager'), @@ -27,17 +28,22 @@ * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you need to call toDataUrl on the webgl context * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 */ -function WebGLRenderer(width, height, options) { +function WebGLRenderer(width, height, options) +{ utils.sayHello('webGL'); - if (options) { - for (var i in CONST.defaultRenderOptions) { - if (typeof options[i] === 'undefined') { + if (options) + { + for (var i in CONST.defaultRenderOptions) + { + if (typeof options[i] === 'undefined') + { options[i] = CONST.defaultRenderOptions[i]; } } } - else { + else + { options = CONST.defaultRenderOptions; } @@ -189,6 +195,8 @@ */ this.spriteBatch = new WebGLSpriteBatch(this); + this.graphicsRenderer = new GraphicsRenderer(this); + /** * Manages the rendering of sprites * @member {WebGLFastSpriteBatch} @@ -235,7 +243,10 @@ * @member DisplayObject * @private */ - this._tempDisplayObjectParent = new PIXI.DisplayObject(); + this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; + + this.currentRenderer = this.spriteBatch; + } // constructor @@ -244,18 +255,22 @@ utils.eventTarget.mixin(WebGLRenderer.prototype); -Object.defineProperties(WebGLRenderer.prototype, { +Object.defineProperties(WebGLRenderer.prototype, +{ /** * The background color to fill if not transparent * * @member {number} * @memberof WebGLRenderer# */ - backgroundColor: { - get: function () { + backgroundColor: + { + get: function () + { return this._backgroundColor; }, - set: function (val) { + set: function (val) + { this._backgroundColor = val; utils.hex2rgb(val, this._backgroundColorRgb); } @@ -266,11 +281,13 @@ * * @private */ -WebGLRenderer.prototype._initContext = function () { +WebGLRenderer.prototype._initContext = function () +{ var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; - if (!gl) { + if (!gl) + { // fail, not able to get a context throw new Error('This browser does not support webGL. Try using the canvas renderer'); } @@ -295,9 +312,11 @@ * * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (object) { +WebGLRenderer.prototype.render = function (object) +{ // no point rendering if our context has been blown up! - if (this.gl.isContextLost()) { + if (this.gl.isContextLost()) + { return; } @@ -317,11 +336,14 @@ // make sure we are bound to the main frame buffer gl.bindFramebuffer(gl.FRAMEBUFFER, null); - if (this.clearBeforeRender) { - if (this.transparent) { + if (this.clearBeforeRender) + { + if (this.transparent) + { gl.clearColor(0, 0, 0, 0); } - else { + else + { gl.clearColor(this._backgroundColorRgb[0], this._backgroundColorRgb[1], this._backgroundColorRgb[2], 1); } @@ -338,7 +360,8 @@ * @param projection {Point} The projection * @param buffer {Array} a standard WebGL buffer */ -WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { +WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) +{ this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. @@ -366,20 +389,34 @@ this.spriteBatch.end(); }; +WebGLRenderer.prototype.setObjectRendererer = function (objectRenderer) +{ + if(this.currentRenderer === objectRenderer) + { + return; + } + + this.currentRenderer.stop(); + this.currentRenderer = objectRenderer; + this.currentRenderer.start(); +} + /** * Resizes the webGL view to the specified width and height. * * @param width {number} the new width of the webGL view * @param height {number} the new height of the webGL view */ -WebGLRenderer.prototype.resize = function (width, height) { +WebGLRenderer.prototype.resize = function (width, height) +{ this.width = width * this.resolution; this.height = height * this.resolution; this.view.width = this.width; this.view.height = this.height; - if (this.autoResize) { + if (this.autoResize) + { this.view.style.width = this.width / this.resolution + 'px'; this.view.style.height = this.height / this.resolution + 'px'; } @@ -395,16 +432,19 @@ * * @param texture {BaseTexture|Texture} the texture to update */ -WebGLRenderer.prototype.updateTexture = function (texture) { +WebGLRenderer.prototype.updateTexture = function (texture) +{ texture = texture.baseTexture || texture; - if (!texture.hasLoaded) { + if (!texture.hasLoaded) + { return; } var gl = this.gl; - if (!texture._glTextures[gl.id]) { + if (!texture._glTextures[gl.id]) + { texture._glTextures[gl.id] = gl.createTexture(); texture.on('update', this._boundUpdateTexture); texture.on('dispose', this._boundDestroyTexture); @@ -418,19 +458,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === CONST.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); - if (texture.mipmap && utils.isPowerOfTwo(texture.width, texture.height)) { + if (texture.mipmap && utils.isPowerOfTwo(texture.width, texture.height)) + { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === CONST.scaleModes.LINEAR ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); gl.generateMipmap(gl.TEXTURE_2D); } - else { + else + { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === CONST.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); } - if (!texture._powerOf2) { + if (!texture._powerOf2) + { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); } - else { + else + { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } @@ -438,14 +482,17 @@ return texture._glTextures[gl.id]; }; -WebGLRenderer.prototype.destroyTexture = function (texture) { +WebGLRenderer.prototype.destroyTexture = function (texture) +{ texture = texture.baseTexture || texture; - if (!texture.hasLoaded) { + if (!texture.hasLoaded) + { return; } - if (texture._glTextures[this.gl.id]) { + if (texture._glTextures[this.gl.id]) + { this.gl.deleteTexture(texture._glTextures[this.gl.id]); } }; @@ -456,7 +503,8 @@ * @param event {Event} * @private */ -WebGLRenderer.prototype.handleContextLost = function (event) { +WebGLRenderer.prototype.handleContextLost = function (event) +{ event.preventDefault(); }; @@ -466,11 +514,13 @@ * @param event {Event} * @private */ -WebGLRenderer.prototype.handleContextRestored = function () { +WebGLRenderer.prototype.handleContextRestored = function () +{ this._initContext(); // empty all the ol gl textures as they are useless now - for (var key in utils.TextureCache) { + for (var key in utils.TextureCache) + { var texture = utils.TextureCache[key].baseTexture; texture._glTextures = []; } @@ -481,8 +531,10 @@ * * @param [removeView=false] {boolean} Removes the Canvas element from the DOM. */ -WebGLRenderer.prototype.destroy = function (removeView) { - if (removeView && this.view.parent) { +WebGLRenderer.prototype.destroy = function (removeView) +{ + if (removeView && this.view.parent) + { this.view.parent.removeChild(this.view); } @@ -542,10 +594,12 @@ * * @private */ -WebGLRenderer.prototype._mapBlendModes = function () { +WebGLRenderer.prototype._mapBlendModes = function () +{ var gl = this.gl; - if (!this.blendModes) { + if (!this.blendModes) + { this.blendModes = {}; this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 28d8d5d..707da24 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -187,6 +187,7 @@ // this is where content gets renderd.. // watch this space for a little render state manager.. + renderer.setObjectRendererer(renderer.spriteBatch); renderer.spriteBatch.render(this); }; diff --git a/src/core/primitives/Graphics.js b/src/core/primitives/Graphics.js index 6ee7465..261c534 100644 --- a/src/core/primitives/Graphics.js +++ b/src/core/primitives/Graphics.js @@ -681,23 +681,17 @@ return; } - else */ + */ + + // check if the webgl graphic needs to be updated + if (this.glDirty) { + this.dirty = true; + this.glDirty = false; + } - renderer.spriteBatch.stop(); - renderer.blendModeManager.setBlendMode(this.blendMode); + renderer.setObjectRendererer(renderer.graphicsRenderer); + renderer.graphicsRenderer.render(this); - // check blend mode - renderer.blendModeManager.setBlendMode( this.blendMode ); - - // check if the webgl graphic needs to be updated - if (this.glDirty) { - this.dirty = true; - this.glDirty = false; - } - - WebGLGraphics.renderGraphics(this, renderer); - - renderer.spriteBatch.start(); }; /** diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 0063821..1f3e655 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,4 +1,5 @@ var WebGLSpriteBatch = require('./utils/WebGLSpriteBatch'), + GraphicsRenderer = require('./utils/GraphicsRenderer'), WebGLFastSpriteBatch = require('./utils/WebGLFastSpriteBatch'), WebGLShaderManager = require('./managers/WebGLShaderManager'), WebGLMaskManager = require('./managers/WebGLMaskManager'), @@ -27,17 +28,22 @@ * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you need to call toDataUrl on the webgl context * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 */ -function WebGLRenderer(width, height, options) { +function WebGLRenderer(width, height, options) +{ utils.sayHello('webGL'); - if (options) { - for (var i in CONST.defaultRenderOptions) { - if (typeof options[i] === 'undefined') { + if (options) + { + for (var i in CONST.defaultRenderOptions) + { + if (typeof options[i] === 'undefined') + { options[i] = CONST.defaultRenderOptions[i]; } } } - else { + else + { options = CONST.defaultRenderOptions; } @@ -189,6 +195,8 @@ */ this.spriteBatch = new WebGLSpriteBatch(this); + this.graphicsRenderer = new GraphicsRenderer(this); + /** * Manages the rendering of sprites * @member {WebGLFastSpriteBatch} @@ -235,7 +243,10 @@ * @member DisplayObject * @private */ - this._tempDisplayObjectParent = new PIXI.DisplayObject(); + this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; + + this.currentRenderer = this.spriteBatch; + } // constructor @@ -244,18 +255,22 @@ utils.eventTarget.mixin(WebGLRenderer.prototype); -Object.defineProperties(WebGLRenderer.prototype, { +Object.defineProperties(WebGLRenderer.prototype, +{ /** * The background color to fill if not transparent * * @member {number} * @memberof WebGLRenderer# */ - backgroundColor: { - get: function () { + backgroundColor: + { + get: function () + { return this._backgroundColor; }, - set: function (val) { + set: function (val) + { this._backgroundColor = val; utils.hex2rgb(val, this._backgroundColorRgb); } @@ -266,11 +281,13 @@ * * @private */ -WebGLRenderer.prototype._initContext = function () { +WebGLRenderer.prototype._initContext = function () +{ var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; - if (!gl) { + if (!gl) + { // fail, not able to get a context throw new Error('This browser does not support webGL. Try using the canvas renderer'); } @@ -295,9 +312,11 @@ * * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (object) { +WebGLRenderer.prototype.render = function (object) +{ // no point rendering if our context has been blown up! - if (this.gl.isContextLost()) { + if (this.gl.isContextLost()) + { return; } @@ -317,11 +336,14 @@ // make sure we are bound to the main frame buffer gl.bindFramebuffer(gl.FRAMEBUFFER, null); - if (this.clearBeforeRender) { - if (this.transparent) { + if (this.clearBeforeRender) + { + if (this.transparent) + { gl.clearColor(0, 0, 0, 0); } - else { + else + { gl.clearColor(this._backgroundColorRgb[0], this._backgroundColorRgb[1], this._backgroundColorRgb[2], 1); } @@ -338,7 +360,8 @@ * @param projection {Point} The projection * @param buffer {Array} a standard WebGL buffer */ -WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { +WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) +{ this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. @@ -366,20 +389,34 @@ this.spriteBatch.end(); }; +WebGLRenderer.prototype.setObjectRendererer = function (objectRenderer) +{ + if(this.currentRenderer === objectRenderer) + { + return; + } + + this.currentRenderer.stop(); + this.currentRenderer = objectRenderer; + this.currentRenderer.start(); +} + /** * Resizes the webGL view to the specified width and height. * * @param width {number} the new width of the webGL view * @param height {number} the new height of the webGL view */ -WebGLRenderer.prototype.resize = function (width, height) { +WebGLRenderer.prototype.resize = function (width, height) +{ this.width = width * this.resolution; this.height = height * this.resolution; this.view.width = this.width; this.view.height = this.height; - if (this.autoResize) { + if (this.autoResize) + { this.view.style.width = this.width / this.resolution + 'px'; this.view.style.height = this.height / this.resolution + 'px'; } @@ -395,16 +432,19 @@ * * @param texture {BaseTexture|Texture} the texture to update */ -WebGLRenderer.prototype.updateTexture = function (texture) { +WebGLRenderer.prototype.updateTexture = function (texture) +{ texture = texture.baseTexture || texture; - if (!texture.hasLoaded) { + if (!texture.hasLoaded) + { return; } var gl = this.gl; - if (!texture._glTextures[gl.id]) { + if (!texture._glTextures[gl.id]) + { texture._glTextures[gl.id] = gl.createTexture(); texture.on('update', this._boundUpdateTexture); texture.on('dispose', this._boundDestroyTexture); @@ -418,19 +458,23 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === CONST.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); - if (texture.mipmap && utils.isPowerOfTwo(texture.width, texture.height)) { + if (texture.mipmap && utils.isPowerOfTwo(texture.width, texture.height)) + { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === CONST.scaleModes.LINEAR ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); gl.generateMipmap(gl.TEXTURE_2D); } - else { + else + { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === CONST.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); } - if (!texture._powerOf2) { + if (!texture._powerOf2) + { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); } - else { + else + { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } @@ -438,14 +482,17 @@ return texture._glTextures[gl.id]; }; -WebGLRenderer.prototype.destroyTexture = function (texture) { +WebGLRenderer.prototype.destroyTexture = function (texture) +{ texture = texture.baseTexture || texture; - if (!texture.hasLoaded) { + if (!texture.hasLoaded) + { return; } - if (texture._glTextures[this.gl.id]) { + if (texture._glTextures[this.gl.id]) + { this.gl.deleteTexture(texture._glTextures[this.gl.id]); } }; @@ -456,7 +503,8 @@ * @param event {Event} * @private */ -WebGLRenderer.prototype.handleContextLost = function (event) { +WebGLRenderer.prototype.handleContextLost = function (event) +{ event.preventDefault(); }; @@ -466,11 +514,13 @@ * @param event {Event} * @private */ -WebGLRenderer.prototype.handleContextRestored = function () { +WebGLRenderer.prototype.handleContextRestored = function () +{ this._initContext(); // empty all the ol gl textures as they are useless now - for (var key in utils.TextureCache) { + for (var key in utils.TextureCache) + { var texture = utils.TextureCache[key].baseTexture; texture._glTextures = []; } @@ -481,8 +531,10 @@ * * @param [removeView=false] {boolean} Removes the Canvas element from the DOM. */ -WebGLRenderer.prototype.destroy = function (removeView) { - if (removeView && this.view.parent) { +WebGLRenderer.prototype.destroy = function (removeView) +{ + if (removeView && this.view.parent) + { this.view.parent.removeChild(this.view); } @@ -542,10 +594,12 @@ * * @private */ -WebGLRenderer.prototype._mapBlendModes = function () { +WebGLRenderer.prototype._mapBlendModes = function () +{ var gl = this.gl; - if (!this.blendModes) { + if (!this.blendModes) + { this.blendModes = {}; this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; diff --git a/src/core/renderers/webgl/utils/GraphicsRenderer.js b/src/core/renderers/webgl/utils/GraphicsRenderer.js new file mode 100644 index 0000000..c6cedf3 --- /dev/null +++ b/src/core/renderers/webgl/utils/GraphicsRenderer.js @@ -0,0 +1,887 @@ +var utils = require('../../../utils'), + math = require('../../../math'), + CONST = require('../../../const'), + WebGLGraphicsData = require('./WebGLGraphicsData'); + +/** + * A set of functions used by the webGL renderer to draw the primitive graphics data + * + * @namespace PIXI + * @private + */ +var WebGLGraphics = module.exports = {}; + +/** + * Renders the graphics object + * + * @static + * @private + * @param graphics {Graphics} + * @param renderer {WebGLRenderer} + */ + +var GraphicsRenderer = function(renderer) +{ + this.renderer = renderer; + this.graphicsDataPool = []; +} + +module.exports = GraphicsRenderer; + +GraphicsRenderer.prototype.start = function() +{ + // flush! +} + +GraphicsRenderer.prototype.stop = function() +{ + // flush! +} + + +GraphicsRenderer.prototype.render = function(graphics) +{ + var renderer = this.renderer; + var gl = renderer.gl; + + var projection = renderer.projection, + offset = renderer.offset, + shader = renderer.shaderManager.primitiveShader, + webGLData; + + if (graphics.dirty) + { + this.updateGraphics(graphics, gl); + } + + var webGL = graphics._webGL[gl.id]; + + // This could be speeded up for sure! + + renderer.blendModeManager.setBlendMode( graphics.blendMode ); + + for (var i = 0; i < webGL.data.length; i++) + { + if (webGL.data[i].mode === 1) + { + webGLData = webGL.data[i]; + + renderer.stencilManager.pushStencil(graphics, webGLData, renderer); + + // render quad.. + gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); + + renderer.stencilManager.popStencil(graphics, webGLData, renderer); + } + else + { + webGLData = webGL.data[i]; + + + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); + shader = renderer.shaderManager.primitiveShader; + gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); + + gl.uniform1f(shader.uniforms.flipY._location, 1); + + gl.uniform2f(shader.uniforms.projectionVector._location, projection.x, -projection.y); + gl.uniform2f(shader.uniforms.offsetVector._location, -offset.x, -offset.y); + + gl.uniform3fv(shader.uniforms.tint._location, utils.hex2rgb(graphics.tint)); + + gl.uniform1f(shader.uniforms.alpha._location, graphics.worldAlpha); + + + gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.buffer); + + gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, 4 * 6, 0); + gl.vertexAttribPointer(shader.attributes.aColor, 4, gl.FLOAT, false,4 * 6, 2 * 4); + + // set the index buffer! + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer); + gl.drawElements(gl.TRIANGLE_STRIP, webGLData.indices.length, gl.UNSIGNED_SHORT, 0 ); + } + } +} + +/** + * Updates the graphics object + * + * @static + * @private + * @param graphicsData {Graphics} The graphics object to update + * @param gl {WebGLContext} the current WebGL drawing context + */ + +GraphicsRenderer.prototype.updateGraphics = function(graphics) +{ + var gl = this.renderer.gl; + + // get the contexts graphics object + var webGL = graphics._webGL[gl.id]; + + // if the graphics object does not exist in the webGL context time to create it! + if (!webGL) + { + webGL = graphics._webGL[gl.id] = {lastIndex:0, data:[], gl:gl}; + } + + // flag the graphics as not dirty as we are about to update it... + graphics.dirty = false; + + var i; + + // if the user cleared the graphics object we will need to clear every object + if (graphics.clearDirty) + { + graphics.clearDirty = false; + + // lop through and return all the webGLDatas to the object pool so than can be reused later on + for (i = 0; i < webGL.data.length; i++) + { + var graphicsData = webGL.data[i]; + graphicsData.reset(); + this.graphicsDataPool.push( graphicsData ); + } + + // clear the array and reset the index.. + webGL.data = []; + webGL.lastIndex = 0; + } + + var webGLData; + + // loop through the graphics datas and construct each one.. + // if the object is a complex fill then the new stencil buffer technique will be used + // other wise graphics objects will be pushed into a batch.. + for (i = webGL.lastIndex; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + + if (data.type === CONST.SHAPES.POLY) + { + // need to add the points the the graphics object.. + data.points = data.shape.points.slice(); + if (data.shape.closed) + { + // close the poly if the value is true! + if (data.points[0] !== data.points[data.points.length-2] || data.points[1] !== data.points[data.points.length-1]) + { + data.points.push(data.points[0], data.points[1]); + } + } + + // MAKE SURE WE HAVE THE CORRECT TYPE.. + if (data.fill) + { + if (data.points.length >= 6) + { + if (data.points.length < 6 * 2) + { + webGLData = this.switchMode(webGL, 0); + + var canDrawUsingSimple = this.buildPoly(data, webGLData); + // console.log(canDrawUsingSimple); + + if (!canDrawUsingSimple) + { + // console.log("<>>>") + webGLData = this.switchMode(webGL, 1); + this.buildComplexPoly(data, webGLData); + } + + } + else + { + webGLData = this.switchMode(webGL, 1); + this.buildComplexPoly(data, webGLData); + } + } + } + + if (data.lineWidth > 0) + { + webGLData = this.switchMode(webGL, 0); + this.buildLine(data, webGLData); + } + } + else + { + webGLData = this.switchMode(webGL, 0); + + if (data.type === CONST.SHAPES.RECT) + { + this.buildRectangle(data, webGLData); + } + else if (data.type === CONST.SHAPES.CIRC || data.type === CONST.SHAPES.ELIP) + { + this.buildCircle(data, webGLData); + } + else if (data.type === CONST.SHAPES.RREC) + { + this.buildRoundedRectangle(data, webGLData); + } + } + + webGL.lastIndex++; + } + + // upload all the dirty data... + for (i = 0; i < webGL.data.length; i++) + { + webGLData = webGL.data[i]; + + if (webGLData.dirty) + { + webGLData.upload(); + } + } +} + +/** + * @static + * @private + * @param webGL {WebGLContext} + * @param type {number} + */ +GraphicsRenderer.prototype.switchMode = function (webGL, type) +{ + var webGLData; + + if (!webGL.data.length) + { + webGLData = this.graphicsDataPool.pop() || new WebGLGraphicsData(webGL.gl); + webGLData.mode = type; + webGL.data.push(webGLData); + } + else + { + webGLData = webGL.data[webGL.data.length-1]; + + if (webGLData.mode !== type || type === 1) + { + webGLData = this.graphicsDataPool.pop() || new WebGLGraphicsData(webGL.gl); + webGLData.mode = type; + webGL.data.push(webGLData); + } + } + + webGLData.dirty = true; + + return webGLData; +}; + + + + + + +/** + * Builds a rectangle to draw + * + * @static + * @private + * @param graphicsData {Graphics} The graphics object containing all the necessary properties + * @param webGLData {object} + */ +GraphicsRenderer.prototype.buildRectangle = function (graphicsData, webGLData) +{ + // --- // + // need to convert points to a nice regular data + // + var rectData = graphicsData.shape; + var x = rectData.x; + var y = rectData.y; + var width = rectData.width; + var height = rectData.height; + + if (graphicsData.fill) + { + var color = utils.hex2rgb(graphicsData.fillColor); + var alpha = graphicsData.fillAlpha; + + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var verts = webGLData.points; + var indices = webGLData.indices; + + var vertPos = verts.length/6; + + // start + verts.push(x, y); + verts.push(r, g, b, alpha); + + verts.push(x + width, y); + verts.push(r, g, b, alpha); + + verts.push(x , y + height); + verts.push(r, g, b, alpha); + + verts.push(x + width, y + height); + verts.push(r, g, b, alpha); + + // insert 2 dead triangles.. + indices.push(vertPos, vertPos, vertPos+1, vertPos+2, vertPos+3, vertPos+3); + } + + if (graphicsData.lineWidth) + { + var tempPoints = graphicsData.points; + + graphicsData.points = [x, y, + x + width, y, + x + width, y + height, + x, y + height, + x, y]; + + + this.buildLine(graphicsData, webGLData); + + graphicsData.points = tempPoints; + } +}; + +/** + * Builds a rounded rectangle to draw + * + * @static + * @private + * @param graphicsData {Graphics} The graphics object containing all the necessary properties + * @param webGLData {object} + */ +GraphicsRenderer.prototype.buildRoundedRectangle = function (graphicsData, webGLData) +{ + var rrectData = graphicsData.shape; + var x = rrectData.x; + var y = rrectData.y; + var width = rrectData.width; + var height = rrectData.height; + + var radius = rrectData.radius; + + var recPoints = []; + recPoints.push(x, y + radius); + recPoints = recPoints.concat(this.quadraticBezierCurve(x, y + height - radius, x, y + height, x + radius, y + height)); + recPoints = recPoints.concat(this.quadraticBezierCurve(x + width - radius, y + height, x + width, y + height, x + width, y + height - radius)); + recPoints = recPoints.concat(this.quadraticBezierCurve(x + width, y + radius, x + width, y, x + width - radius, y)); + recPoints = recPoints.concat(this.quadraticBezierCurve(x + radius, y, x, y, x, y + radius)); + + if (graphicsData.fill) + { + var color = utils.hex2rgb(graphicsData.fillColor); + var alpha = graphicsData.fillAlpha; + + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var verts = webGLData.points; + var indices = webGLData.indices; + + var vecPos = verts.length/6; + + var triangles = utils.PolyK.Triangulate(recPoints); + + // + + var i = 0; + for (i = 0; i < triangles.length; i+=3) + { + indices.push(triangles[i] + vecPos); + indices.push(triangles[i] + vecPos); + indices.push(triangles[i+1] + vecPos); + indices.push(triangles[i+2] + vecPos); + indices.push(triangles[i+2] + vecPos); + } + + for (i = 0; i < recPoints.length; i++) + { + verts.push(recPoints[i], recPoints[++i], r, g, b, alpha); + } + } + + if (graphicsData.lineWidth) + { + var tempPoints = graphicsData.points; + + graphicsData.points = recPoints; + + this.buildLine(graphicsData, webGLData); + + graphicsData.points = tempPoints; + } +}; + +/** + * Calculate the points for a quadratic bezier curve. (helper function..) + * Based on: https://stackoverflow.com/questions/785097/how-do-i-implement-a-bezier-curve-in-c + * + * @static + * @private + * @param fromX {number} Origin point x + * @param fromY {number} Origin point x + * @param cpX {number} Control point x + * @param cpY {number} Control point y + * @param toX {number} Destination point x + * @param toY {number} Destination point y + * @return {number[]} + */ +GraphicsRenderer.prototype.quadraticBezierCurve = function (fromX, fromY, cpX, cpY, toX, toY) +{ + + var xa, + ya, + xb, + yb, + x, + y, + n = 20, + points = []; + + function getPt(n1 , n2, perc) { + var diff = n2 - n1; + + return n1 + ( diff * perc ); + } + + var j = 0; + for (var i = 0; i <= n; i++ ) { + j = i / n; + + // The Green Line + xa = getPt( fromX , cpX , j ); + ya = getPt( fromY , cpY , j ); + xb = getPt( cpX , toX , j ); + yb = getPt( cpY , toY , j ); + + // The Black Dot + x = getPt( xa , xb , j ); + y = getPt( ya , yb , j ); + + points.push(x, y); + } + return points; +}; + +/** + * Builds a circle to draw + * + * @static + * @private + * @param graphicsData {Graphics} The graphics object to draw + * @param webGLData {object} + */ +GraphicsRenderer.prototype.buildCircle = function (graphicsData, webGLData) +{ + // need to convert points to a nice regular data + var circleData = graphicsData.shape; + var x = circleData.x; + var y = circleData.y; + var width; + var height; + + // TODO - bit hacky?? + if (graphicsData.type === CONST.SHAPES.CIRC) + { + width = circleData.radius; + height = circleData.radius; + } + else + { + width = circleData.width; + height = circleData.height; + } + + var totalSegs = 40; + var seg = (Math.PI * 2) / totalSegs ; + + var i = 0; + + if (graphicsData.fill) + { + var color = utils.hex2rgb(graphicsData.fillColor); + var alpha = graphicsData.fillAlpha; + + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var verts = webGLData.points; + var indices = webGLData.indices; + + var vecPos = verts.length/6; + + indices.push(vecPos); + + for (i = 0; i < totalSegs + 1 ; i++) + { + verts.push(x,y, r, g, b, alpha); + + verts.push(x + Math.sin(seg * i) * width, + y + Math.cos(seg * i) * height, + r, g, b, alpha); + + indices.push(vecPos++, vecPos++); + } + + indices.push(vecPos-1); + } + + if (graphicsData.lineWidth) + { + var tempPoints = graphicsData.points; + + graphicsData.points = []; + + for (i = 0; i < totalSegs + 1; i++) + { + graphicsData.points.push(x + Math.sin(seg * i) * width, + y + Math.cos(seg * i) * height); + } + + this.buildLine(graphicsData, webGLData); + + graphicsData.points = tempPoints; + } +}; + +/** + * Builds a line to draw + * + * @static + * @private + * @param graphicsData {Graphics} The graphics object containing all the necessary properties + * @param webGLData {object} + */ +GraphicsRenderer.prototype.buildLine = function (graphicsData, webGLData) +{ + // TODO OPTIMISE! + var i = 0; + var points = graphicsData.points; + + if (points.length === 0) + { + return; + } + + // if the line width is an odd number add 0.5 to align to a whole pixel + if (graphicsData.lineWidth%2) + { + for (i = 0; i < points.length; i++) + { + points[i] += 0.5; + } + } + + // get first and last point.. figure out the middle! + var firstPoint = new math.Point(points[0], points[1]); + var lastPoint = new math.Point(points[points.length - 2], points[points.length - 1]); + + // if the first point is the last point - gonna have issues :) + if (firstPoint.x === lastPoint.x && firstPoint.y === lastPoint.y) + { + // need to clone as we are going to slightly modify the shape.. + points = points.slice(); + + points.pop(); + points.pop(); + + lastPoint = new math.Point(points[points.length - 2], points[points.length - 1]); + + var midPointX = lastPoint.x + (firstPoint.x - lastPoint.x) *0.5; + var midPointY = lastPoint.y + (firstPoint.y - lastPoint.y) *0.5; + + points.unshift(midPointX, midPointY); + points.push(midPointX, midPointY); + } + + var verts = webGLData.points; + var indices = webGLData.indices; + var length = points.length / 2; + var indexCount = points.length; + var indexStart = verts.length/6; + + // DRAW the Line + var width = graphicsData.lineWidth / 2; + + // sort color + var color = utils.hex2rgb(graphicsData.lineColor); + var alpha = graphicsData.lineAlpha; + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var px, py, p1x, p1y, p2x, p2y, p3x, p3y; + var perpx, perpy, perp2x, perp2y, perp3x, perp3y; + var a1, b1, c1, a2, b2, c2; + var denom, pdist, dist; + + p1x = points[0]; + p1y = points[1]; + + p2x = points[2]; + p2y = points[3]; + + perpx = -(p1y - p2y); + perpy = p1x - p2x; + + dist = Math.sqrt(perpx*perpx + perpy*perpy); + + perpx /= dist; + perpy /= dist; + perpx *= width; + perpy *= width; + + // start + verts.push(p1x - perpx , p1y - perpy, + r, g, b, alpha); + + verts.push(p1x + perpx , p1y + perpy, + r, g, b, alpha); + + for (i = 1; i < length-1; i++) + { + p1x = points[(i-1)*2]; + p1y = points[(i-1)*2 + 1]; + + p2x = points[(i)*2]; + p2y = points[(i)*2 + 1]; + + p3x = points[(i+1)*2]; + p3y = points[(i+1)*2 + 1]; + + perpx = -(p1y - p2y); + perpy = p1x - p2x; + + dist = Math.sqrt(perpx*perpx + perpy*perpy); + perpx /= dist; + perpy /= dist; + perpx *= width; + perpy *= width; + + perp2x = -(p2y - p3y); + perp2y = p2x - p3x; + + dist = Math.sqrt(perp2x*perp2x + perp2y*perp2y); + perp2x /= dist; + perp2y /= dist; + perp2x *= width; + perp2y *= width; + + a1 = (-perpy + p1y) - (-perpy + p2y); + b1 = (-perpx + p2x) - (-perpx + p1x); + c1 = (-perpx + p1x) * (-perpy + p2y) - (-perpx + p2x) * (-perpy + p1y); + a2 = (-perp2y + p3y) - (-perp2y + p2y); + b2 = (-perp2x + p2x) - (-perp2x + p3x); + c2 = (-perp2x + p3x) * (-perp2y + p2y) - (-perp2x + p2x) * (-perp2y + p3y); + + denom = a1*b2 - a2*b1; + + if (Math.abs(denom) < 0.1 ) + { + + denom+=10.1; + verts.push(p2x - perpx , p2y - perpy, + r, g, b, alpha); + + verts.push(p2x + perpx , p2y + perpy, + r, g, b, alpha); + + continue; + } + + px = (b1*c2 - b2*c1)/denom; + py = (a2*c1 - a1*c2)/denom; + + + pdist = (px -p2x) * (px -p2x) + (py -p2y) + (py -p2y); + + + if (pdist > 140 * 140) + { + perp3x = perpx - perp2x; + perp3y = perpy - perp2y; + + dist = Math.sqrt(perp3x*perp3x + perp3y*perp3y); + perp3x /= dist; + perp3y /= dist; + perp3x *= width; + perp3y *= width; + + verts.push(p2x - perp3x, p2y -perp3y); + verts.push(r, g, b, alpha); + + verts.push(p2x + perp3x, p2y +perp3y); + verts.push(r, g, b, alpha); + + verts.push(p2x - perp3x, p2y -perp3y); + verts.push(r, g, b, alpha); + + indexCount++; + } + else + { + + verts.push(px , py); + verts.push(r, g, b, alpha); + + verts.push(p2x - (px-p2x), p2y - (py - p2y)); + verts.push(r, g, b, alpha); + } + } + + p1x = points[(length-2)*2]; + p1y = points[(length-2)*2 + 1]; + + p2x = points[(length-1)*2]; + p2y = points[(length-1)*2 + 1]; + + perpx = -(p1y - p2y); + perpy = p1x - p2x; + + dist = Math.sqrt(perpx*perpx + perpy*perpy); + perpx /= dist; + perpy /= dist; + perpx *= width; + perpy *= width; + + verts.push(p2x - perpx , p2y - perpy); + verts.push(r, g, b, alpha); + + verts.push(p2x + perpx , p2y + perpy); + verts.push(r, g, b, alpha); + + indices.push(indexStart); + + for (i = 0; i < indexCount; i++) + { + indices.push(indexStart++); + } + + indices.push(indexStart-1); +}; + +/** + * Builds a complex polygon to draw + * + * @static + * @private + * @param graphicsData {Graphics} The graphics object containing all the necessary properties + * @param webGLData {object} + */ +GraphicsRenderer.prototype.buildComplexPoly = function (graphicsData, webGLData) +{ + //TODO - no need to copy this as it gets turned into a FLoat32Array anyways.. + var points = graphicsData.points.slice(); + + if (points.length < 6) + { + return; + } + + // get first and last point.. figure out the middle! + var indices = webGLData.indices; + webGLData.points = points; + webGLData.alpha = graphicsData.fillAlpha; + webGLData.color = utils.hex2rgb(graphicsData.fillColor); + + // calclate the bounds.. + var minX = Infinity; + var maxX = -Infinity; + + var minY = Infinity; + var maxY = -Infinity; + + var x,y; + + // get size.. + for (var i = 0; i < points.length; i+=2) + { + x = points[i]; + y = points[i+1]; + + minX = x < minX ? x : minX; + maxX = x > maxX ? x : maxX; + + minY = y < minY ? y : minY; + maxY = y > maxY ? y : maxY; + } + + // add a quad to the end cos there is no point making another buffer! + points.push(minX, minY, + maxX, minY, + maxX, maxY, + minX, maxY); + + // push a quad onto the end.. + + //TODO - this aint needed! + var length = points.length / 2; + for (i = 0; i < length; i++) + { + indices.push( i ); + } + +}; + +/** + * Builds a polygon to draw + * + * @static + * @private + * @param graphicsData {Graphics} The graphics object containing all the necessary properties + * @param webGLData {object} + */ +GraphicsRenderer.prototype.buildPoly = function (graphicsData, webGLData) +{ + var points = graphicsData.points; + + if (points.length < 6) + { + return; + } + + // get first and last point.. figure out the middle! + var verts = webGLData.points; + var indices = webGLData.indices; + + var length = points.length / 2; + + // sort color + var color = utils.hex2rgb(graphicsData.fillColor); + var alpha = graphicsData.fillAlpha; + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var triangles = utils.PolyK.Triangulate(points); + + if (!triangles) { + return false; + } + + var vertPos = verts.length / 6; + + var i = 0; + + for (i = 0; i < triangles.length; i+=3) + { + indices.push(triangles[i] + vertPos); + indices.push(triangles[i] + vertPos); + indices.push(triangles[i+1] + vertPos); + indices.push(triangles[i+2] +vertPos); + indices.push(triangles[i+2] + vertPos); + } + + for (i = 0; i < length; i++) + { + verts.push(points[i * 2], points[i * 2 + 1], + r, g, b, alpha); + } + + return true; +}; + +GraphicsRenderer.graphicsDataPool = [];