'use strict'; exports.__esModule = true; var _SystemRenderer2 = require('../SystemRenderer'); var _SystemRenderer3 = _interopRequireDefault(_SystemRenderer2); var _MaskManager = require('./managers/MaskManager'); var _MaskManager2 = _interopRequireDefault(_MaskManager); var _StencilManager = require('./managers/StencilManager'); var _StencilManager2 = _interopRequireDefault(_StencilManager); var _FilterManager = require('./managers/FilterManager'); var _FilterManager2 = _interopRequireDefault(_FilterManager); var _RenderTarget = require('./utils/RenderTarget'); var _RenderTarget2 = _interopRequireDefault(_RenderTarget); var _ObjectRenderer = require('./utils/ObjectRenderer'); var _ObjectRenderer2 = _interopRequireDefault(_ObjectRenderer); var _TextureManager = require('./TextureManager'); var _TextureManager2 = _interopRequireDefault(_TextureManager); var _BaseTexture = require('../../textures/BaseTexture'); var _BaseTexture2 = _interopRequireDefault(_BaseTexture); var _TextureGarbageCollector = require('./TextureGarbageCollector'); var _TextureGarbageCollector2 = _interopRequireDefault(_TextureGarbageCollector); var _WebGLState = require('./WebGLState'); var _WebGLState2 = _interopRequireDefault(_WebGLState); var _mapWebGLDrawModesToPixi = require('./utils/mapWebGLDrawModesToPixi'); var _mapWebGLDrawModesToPixi2 = _interopRequireDefault(_mapWebGLDrawModesToPixi); var _validateContext = require('./utils/validateContext'); var _validateContext2 = _interopRequireDefault(_validateContext); var _utils = require('../../utils'); var _pixiGlCore = require('pixi-gl-core'); var _pixiGlCore2 = _interopRequireDefault(_pixiGlCore); var _const = require('../../const'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var CONTEXT_UID = 0; /** * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) * * @class * @memberof PIXI * @extends PIXI.SystemRenderer */ var WebGLRenderer = function (_SystemRenderer) { _inherits(WebGLRenderer, _SystemRenderer); /** * * @param {number} [width=0] - the width of the canvas view * @param {number} [height=0] - the height of the canvas view * @param {object} [options] - The optional renderer parameters * @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false * @param {boolean} [options.antialias=false] - sets antialias. If not available natively then FXAA * antialiasing is used * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. */ function WebGLRenderer(width, height) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; _classCallCheck(this, WebGLRenderer); /** * The type of this renderer as a standardised const * * @member {number} * @see PIXI.RENDERER_TYPE */ var _this = _possibleConstructorReturn(this, _SystemRenderer.call(this, 'WebGL', width, height, options)); _this.type = _const.RENDERER_TYPE.WEBGL; _this.handleContextLost = _this.handleContextLost.bind(_this); _this.handleContextRestored = _this.handleContextRestored.bind(_this); _this.view.addEventListener('webglcontextlost', _this.handleContextLost, false); _this.view.addEventListener('webglcontextrestored', _this.handleContextRestored, false); /** * The options passed in to create a new webgl context. * * @member {object} * @private */ _this._contextOptions = { alpha: _this.transparent, antialias: options.antialias, premultipliedAlpha: _this.transparent && _this.transparent !== 'notMultiplied', stencil: true, preserveDrawingBuffer: options.preserveDrawingBuffer }; _this._backgroundColorRgba[3] = _this.transparent ? 0 : 1; /** * Manages the masks using the stencil buffer. * * @member {PIXI.MaskManager} */ _this.maskManager = new _MaskManager2.default(_this); /** * Manages the stencil buffer. * * @member {PIXI.StencilManager} */ _this.stencilManager = new _StencilManager2.default(_this); /** * An empty renderer. * * @member {PIXI.ObjectRenderer} */ _this.emptyRenderer = new _ObjectRenderer2.default(_this); /** * The currently active ObjectRenderer. * * @member {PIXI.ObjectRenderer} */ _this.currentRenderer = _this.emptyRenderer; _this.initPlugins(); /** * The current WebGL rendering context, it is created here * * @member {WebGLRenderingContext} */ // initialize the context so it is ready for the managers. if (options.context) { // checks to see if a context is valid.. (0, _validateContext2.default)(options.context); } _this.gl = options.context || _pixiGlCore2.default.createContext(_this.view, _this._contextOptions); _this.CONTEXT_UID = CONTEXT_UID++; /** * The currently active ObjectRenderer. * * @member {PIXI.WebGLState} */ _this.state = new _WebGLState2.default(_this.gl); _this.renderingToScreen = true; /** * Holds the current state of textures bound to the GPU. * @type {Array} */ _this.boundTextures = null; _this._initContext(); /** * Manages the filters. * * @member {PIXI.FilterManager} */ _this.filterManager = new _FilterManager2.default(_this); // map some webGL blend and drawmodes.. _this.drawModes = (0, _mapWebGLDrawModesToPixi2.default)(_this.gl); /** * Holds the current shader * * @member {PIXI.Shader} */ _this._activeShader = null; _this._activeVao = null; /** * Holds the current render target * * @member {PIXI.RenderTarget} */ _this._activeRenderTarget = null; _this._nextTextureLocation = 0; _this.setBlendMode(0); return _this; } /** * Creates the WebGL context * * @private */ WebGLRenderer.prototype._initContext = function _initContext() { var gl = this.gl; // restore a context if it was previously lost if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) { gl.getExtension('WEBGL_lose_context').restoreContext(); } var maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); this.boundTextures = new Array(maxTextures); this.emptyTextures = new Array(maxTextures); // create a texture manager... this.textureManager = new _TextureManager2.default(this); this.textureGC = new _TextureGarbageCollector2.default(this); this.state.resetToDefault(); this.rootRenderTarget = new _RenderTarget2.default(gl, this.width, this.height, null, this.resolution, true); this.rootRenderTarget.clearColor = this._backgroundColorRgba; this.bindRenderTarget(this.rootRenderTarget); // now lets fill up the textures with empty ones! var emptyGLTexture = new _pixiGlCore2.default.GLTexture.fromData(gl, null, 1, 1); var tempObj = { _glTextures: {} }; tempObj._glTextures[this.CONTEXT_UID] = {}; for (var i = 0; i < maxTextures; i++) { var empty = new _BaseTexture2.default(); empty._glTextures[this.CONTEXT_UID] = emptyGLTexture; this.boundTextures[i] = tempObj; this.emptyTextures[i] = empty; this.bindTexture(null, i); } this.emit('context', gl); // setup the width/height properties and gl viewport this.resize(this.width, this.height); }; /** * Renders the object to its webGL view * * @param {PIXI.DisplayObject} displayObject - the object to be rendered * @param {PIXI.RenderTexture} renderTexture - The render texture to render to. * @param {boolean} [clear] - Should the canvas be cleared before the new render * @param {PIXI.Transform} [transform] - A transform to apply to the render texture before rendering. * @param {boolean} [skipUpdateTransform] - Should we skip the update transform pass? */ WebGLRenderer.prototype.render = function render(displayObject, renderTexture, clear, transform, skipUpdateTransform) { // can be handy to know! this.renderingToScreen = !renderTexture; this.emit('prerender'); // no point rendering if our context has been blown up! if (!this.gl || this.gl.isContextLost()) { return; } this._nextTextureLocation = 0; if (!renderTexture) { this._lastObjectRendered = displayObject; } if (!skipUpdateTransform) { // update the scene graph var cacheParent = displayObject.parent; displayObject.parent = this._tempDisplayObjectParent; displayObject.updateTransform(); displayObject.parent = cacheParent; // displayObject.hitArea = //TODO add a temp hit area } this.bindRenderTexture(renderTexture, transform); this.currentRenderer.start(); if (clear !== undefined ? clear : this.clearBeforeRender) { this._activeRenderTarget.clear(); } displayObject.renderWebGL(this); // apply transform.. this.currentRenderer.flush(); // this.setObjectRenderer(this.emptyRenderer); this.textureGC.update(); this.emit('postrender'); }; /** * Changes the current renderer to the one given in parameter * * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. */ WebGLRenderer.prototype.setObjectRenderer = function setObjectRenderer(objectRenderer) { if (this.currentRenderer === objectRenderer) { return; } this.currentRenderer.stop(); this.currentRenderer = objectRenderer; this.currentRenderer.start(); }; /** * This should be called if you wish to do some custom rendering * It will basically render anything that may be batched up such as sprites * */ WebGLRenderer.prototype.flush = function flush() { this.setObjectRenderer(this.emptyRenderer); }; /** * Resizes the webGL view to the specified width and height. * * @param {number} width - the new width of the webGL view * @param {number} height - the new height of the webGL view */ WebGLRenderer.prototype.resize = function resize(width, height) { // if(width * this.resolution === this.width && height * this.resolution === this.height)return; _SystemRenderer3.default.prototype.resize.call(this, width, height); this.rootRenderTarget.resize(width, height); if (this._activeRenderTarget === this.rootRenderTarget) { this.rootRenderTarget.activate(); if (this._activeShader) { this._activeShader.uniforms.projectionMatrix = this.rootRenderTarget.projectionMatrix.toArray(true); } } }; /** * Resizes the webGL view to the specified width and height. * * @param {number} blendMode - the desired blend mode */ WebGLRenderer.prototype.setBlendMode = function setBlendMode(blendMode) { this.state.setBlendMode(blendMode); }; /** * Erases the active render target and fills the drawing area with a colour * * @param {number} [clearColor] - The colour */ WebGLRenderer.prototype.clear = function clear(clearColor) { this._activeRenderTarget.clear(clearColor); }; /** * Sets the transform of the active render target to the given matrix * * @param {PIXI.Matrix} matrix - The transformation matrix */ WebGLRenderer.prototype.setTransform = function setTransform(matrix) { this._activeRenderTarget.transform = matrix; }; /** * Binds a render texture for rendering * * @param {PIXI.RenderTexture} renderTexture - The render texture to render * @param {PIXI.Transform} transform - The transform to be applied to the render texture * @return {PIXI.WebGLRenderer} Returns itself. */ WebGLRenderer.prototype.bindRenderTexture = function bindRenderTexture(renderTexture, transform) { var renderTarget = void 0; if (renderTexture) { var baseTexture = renderTexture.baseTexture; if (!baseTexture._glRenderTargets[this.CONTEXT_UID]) { // bind the current texture this.textureManager.updateTexture(baseTexture, 0); } this.unbindTexture(baseTexture); renderTarget = baseTexture._glRenderTargets[this.CONTEXT_UID]; renderTarget.setFrame(renderTexture.frame); } else { renderTarget = this.rootRenderTarget; } renderTarget.transform = transform; this.bindRenderTarget(renderTarget); return this; }; /** * Changes the current render target to the one given in parameter * * @param {PIXI.RenderTarget} renderTarget - the new render target * @return {PIXI.WebGLRenderer} Returns itself. */ WebGLRenderer.prototype.bindRenderTarget = function bindRenderTarget(renderTarget) { if (renderTarget !== this._activeRenderTarget) { this._activeRenderTarget = renderTarget; renderTarget.activate(); if (this._activeShader) { this._activeShader.uniforms.projectionMatrix = renderTarget.projectionMatrix.toArray(true); } this.stencilManager.setMaskStack(renderTarget.stencilMaskStack); } return this; }; /** * Changes the current shader to the one given in parameter * * @param {PIXI.Shader} shader - the new shader * @return {PIXI.WebGLRenderer} Returns itself. */ WebGLRenderer.prototype.bindShader = function bindShader(shader) { // TODO cache if (this._activeShader !== shader) { this._activeShader = shader; shader.bind(); // automatically set the projection matrix shader.uniforms.projectionMatrix = this._activeRenderTarget.projectionMatrix.toArray(true); } return this; }; /** * Binds the texture. This will return the location of the bound texture. * It may not be the same as the one you pass in. This is due to optimisation that prevents * needless binding of textures. For example if the texture is already bound it will return the * current location of the texture instead of the one provided. To bypass this use force location * * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location * @return {PIXI.WebGLRenderer} Returns itself. */ WebGLRenderer.prototype.bindTexture = function bindTexture(texture, location, forceLocation) { texture = texture || this.emptyTextures[location]; texture = texture.baseTexture || texture; texture.touched = this.textureGC.count; if (!forceLocation) { // TODO - maybe look into adding boundIds.. save us the loop? for (var i = 0; i < this.boundTextures.length; i++) { if (this.boundTextures[i] === texture) { return i; } } if (location === undefined) { this._nextTextureLocation++; this._nextTextureLocation %= this.boundTextures.length; location = this.boundTextures.length - this._nextTextureLocation - 1; } } else { location = location || 0; } var gl = this.gl; var glTexture = texture._glTextures[this.CONTEXT_UID]; if (!glTexture) { // this will also bind the texture.. this.textureManager.updateTexture(texture, location); } else { // bind the current texture this.boundTextures[location] = texture; gl.activeTexture(gl.TEXTURE0 + location); gl.bindTexture(gl.TEXTURE_2D, glTexture.texture); } return location; }; /** * unbinds the texture ... * * @param {PIXI.Texture} texture - the texture to unbind * @return {PIXI.WebGLRenderer} Returns itself. */ WebGLRenderer.prototype.unbindTexture = function unbindTexture(texture) { var gl = this.gl; texture = texture.baseTexture || texture; for (var i = 0; i < this.boundTextures.length; i++) { if (this.boundTextures[i] === texture) { this.boundTextures[i] = this.emptyTextures[i]; gl.activeTexture(gl.TEXTURE0 + i); gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[i]._glTextures[this.CONTEXT_UID].texture); } } return this; }; /** * Creates a new VAO from this renderer's context and state. * * @return {VertexArrayObject} The new VAO. */ WebGLRenderer.prototype.createVao = function createVao() { return new _pixiGlCore2.default.VertexArrayObject(this.gl, this.state.attribState); }; /** * Changes the current Vao to the one given in parameter * * @param {PIXI.VertexArrayObject} vao - the new Vao * @return {PIXI.WebGLRenderer} Returns itself. */ WebGLRenderer.prototype.bindVao = function bindVao(vao) { if (this._activeVao === vao) { return this; } if (vao) { vao.bind(); } else if (this._activeVao) { // TODO this should always be true i think? this._activeVao.unbind(); } this._activeVao = vao; return this; }; /** * Resets the WebGL state so you can render things however you fancy! * * @return {PIXI.WebGLRenderer} Returns itself. */ WebGLRenderer.prototype.reset = function reset() { this.setObjectRenderer(this.emptyRenderer); this._activeShader = null; this._activeRenderTarget = this.rootRenderTarget; // bind the main frame buffer (the screen); this.rootRenderTarget.activate(); this.state.resetToDefault(); return this; }; /** * Handles a lost webgl context * * @private * @param {WebGLContextEvent} event - The context lost event. */ WebGLRenderer.prototype.handleContextLost = function handleContextLost(event) { event.preventDefault(); }; /** * Handles a restored webgl context * * @private */ WebGLRenderer.prototype.handleContextRestored = function handleContextRestored() { this._initContext(); this.textureManager.removeAll(); }; /** * Removes everything from the renderer (event listeners, spritebatch, etc...) * * @param {boolean} [removeView=false] - Removes the Canvas element from the DOM. * See: https://github.com/pixijs/pixi.js/issues/2233 */ WebGLRenderer.prototype.destroy = function destroy(removeView) { this.destroyPlugins(); // remove listeners this.view.removeEventListener('webglcontextlost', this.handleContextLost); this.view.removeEventListener('webglcontextrestored', this.handleContextRestored); this.textureManager.destroy(); // call base destroy _SystemRenderer.prototype.destroy.call(this, removeView); this.uid = 0; // destroy the managers this.maskManager.destroy(); this.stencilManager.destroy(); this.filterManager.destroy(); this.maskManager = null; this.filterManager = null; this.textureManager = null; this.currentRenderer = null; this.handleContextLost = null; this.handleContextRestored = null; this._contextOptions = null; this.gl.useProgram(null); if (this.gl.getExtension('WEBGL_lose_context')) { this.gl.getExtension('WEBGL_lose_context').loseContext(); } this.gl = null; // this = null; }; return WebGLRenderer; }(_SystemRenderer3.default); exports.default = WebGLRenderer; _utils.pluginTarget.mixin(WebGLRenderer); //# sourceMappingURL=WebGLRenderer.js.map