var utils = require('../../utils'), math = require('../../math'), CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), WebGLGraphicsData = require('./WebGLGraphicsData'), PrimitiveShader = require('./shaders/PrimitiveShader'), // some drawing functions.. buildLine = require('./utils/buildLine'); buildPoly = require('./utils/buildPoly'); buildComplexPoly = require('./utils/buildComplexPoly'); buildRectangle = require('./utils/buildRectangle'); buildRoundedRectangle = require('./utils/buildRoundedRectangle'); buildCircle = require('./utils/buildCircle'); earcut = require('earcut'); /** * Renders the graphics object. * * @class * @private * @memberof PIXI * @extends PIXI.ObjectRenderer * @param renderer {PIXI.WebGLRenderer} The renderer this object renderer works for. */ function GraphicsRenderer(renderer) { ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; this.primitiveShader = null; this.complexPrimitiveShader = null; this.gl = renderer.gl; /** * This is the maximum number of points a poly can contain before it is rendered as a complex polygon (using the stencil buffer) * @type {Number} */ this.maximumSimplePolySize = 200; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); GraphicsRenderer.prototype.constructor = GraphicsRenderer; module.exports = GraphicsRenderer; WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); /** * Called when there is a WebGL context change * * @private * */ GraphicsRenderer.prototype.onContextChange = function() { this.gl = this.renderer.gl; this.primitiveShader = new PrimitiveShader(this.gl) }; /** * Destroys this renderer. * */ GraphicsRenderer.prototype.destroy = function () { ObjectRenderer.prototype.destroy.call(this); for (var i = 0; i < this.graphicsDataPool.length; ++i) { this.graphicsDataPool[i].destroy(); } this.graphicsDataPool = null; }; /** * Renders a graphics object. * * @param graphics {PIXI.Graphics} The graphics object to render. */ GraphicsRenderer.prototype.render = function(graphics) { var renderer = this.renderer; var gl = renderer.gl; var webGLData; // console.log(graphics) if (graphics.dirty || !graphics._webGL[gl.id]) { this.updateGraphics(graphics); } var webGL = graphics._webGL[gl.id]; // This could be speeded up for sure! var shader = this.primitiveShader; renderer.bindShader(shader) renderer.blendModeManager.setBlendMode( graphics.blendMode ); for (var i = 0, n = webGL.data.length; i < n; i++) { webGLData = webGL.data[i]; shader.uniforms.translationMatrix = graphics.worldTransform.toArray(true); shader.uniforms.tint = utils.hex2rgb(graphics.tint); shader.uniforms.alpha = graphics.worldAlpha; webGLData.vao.bind() .draw(gl.TRIANGLE_STRIP, webGLData.indices.length) .unbind(); renderer.drawCount++; } }; /** * Updates the graphics object * * @private * @param graphics {PIXI.Graphics} The graphics object to update */ 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; // loop 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 < this.maximumSimplePolySize * 2) { webGLData = this.switchMode(webGL, 0); var canDrawUsingSimple = buildPoly(data, webGLData); if (!canDrawUsingSimple) { webGLData = this.switchMode(webGL, 1); buildComplexPoly(data, webGLData); } } else { webGLData = this.switchMode(webGL, 1); buildComplexPoly(data, webGLData); } } } if (data.lineWidth > 0) { webGLData = this.switchMode(webGL, 0); buildLine(data, webGLData); } } else { webGLData = this.switchMode(webGL, 0); if (data.type === CONST.SHAPES.RECT) { buildRectangle(data, webGLData); } else if (data.type === CONST.SHAPES.CIRC || data.type === CONST.SHAPES.ELIP) { buildCircle(data, webGLData); } else if (data.type === CONST.SHAPES.RREC) { 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(); } } }; /** * * * @private * @param webGL {WebGLRenderingContext} the current WebGL drawing context * @param type {number} TODO @Alvin */ GraphicsRenderer.prototype.switchMode = function (webGL, type) { var webGLData; if (!webGL.data.length) { webGLData = this.graphicsDataPool.pop() || new WebGLGraphicsData(webGL.gl, this.primitiveShader); webGLData.mode = type; webGL.data.push(webGLData); } else { webGLData = webGL.data[webGL.data.length-1]; if ((webGLData.points.length > 320000) || webGLData.mode !== type || type === 1) { webGLData = this.graphicsDataPool.pop() || new WebGLGraphicsData(webGL.gl, this.primitiveShader); webGLData.mode = type; webGL.data.push(webGLData); } } webGLData.dirty = true; return webGLData; };