Phaser.Renderer.WebGL = function (game){ this.game = game; this.type = Phaser.WEBGL; this.clearBeforeRender = game.clearBeforeRender; this.transparent = game.transparent; this.autoResize = false ; this.width = game.width * game.resolution; this.height = game.height * game.resolution; this.resolution = game.resolution; this.view = game.canvas; this.stencilBufferLimit = 6; this.multiTexture = false ; this.extensions = { } ; this.preserveDrawingBuffer = game.preserveDrawingBuffer; this.contextOptions = { alpha: this.transparent, antialias: game.antialias, premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied', stencil: true , preserveDrawingBuffer: this.preserveDrawingBuffer} ; this.projection = new Phaser.Point(); this.offset = new Phaser.Point(); this.shaderManager = new Phaser.Renderer.WebGL.ShaderManager(this); this.batch = new Phaser.Renderer.WebGL.BatchManager(this, 2000); this.filterManager = new Phaser.Renderer.WebGL.FilterManager(this); this.stencilManager = new Phaser.Renderer.WebGL.StencilManager(this); this.gl = null ; this.textureArray = [] ; this.currentBlendMode = -1; this.currentTextureSource = null ; this.currentShader = null ; this.blendModes = [] ; this.flipY = 1; this.startTime = 0; this.endTime = 0; this.drawCount = 0; this.contextLost = false ; this._fbErrors = { 36054: 'Incomplete attachment', 36055: 'Missing attachment', 36057: 'Incomplete dimensions', 36061: 'Framebuffer unsupported'} ; _AN_Call_init('init', this); } ; Phaser.Renderer.WebGL.GameObjects = { } ; Phaser.Renderer.WebGL.Shaders = { } ; Phaser.Renderer.WebGL.prototype.constructor = Phaser.Renderer.WebGL; Phaser.Renderer.WebGL.prototype = { init: function (){ this.gl = this.view.getContext('webgl', this.contextOptions) || this.view.getContext('experimental-webgl', this.contextOptions); if (!this.gl) { this.contextLost = true ; throw new Error('This browser does not support WebGL. Try using the Canvas renderer.') } for (var renderer in Phaser.Renderer.WebGL.GameObjects){ var types = Phaser.Renderer.WebGL.GameObjects[renderer].TYPES; if (!types) { continue ; } for (var i = 0; i < _AN_Read_length('length', types); i++ ){ types[i].render = Phaser.Renderer.WebGL.GameObjects[renderer].render; } } var gl = this.gl; this.maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); if (this.maxTextures === 1) { this.multiTexture = false ; } gl.disable(gl.DEPTH_TEST); gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); _AN_Call_init('init', this.shaderManager); _AN_Call_init('init', this.batch); _AN_Call_init('init', this.filterManager); _AN_Call_init('init', this.stencilManager); this.resize(this.width, this.height); this.extensions.compression = { } ; var etc1 = gl.getExtension('WEBGL_compressed_texture_etc1') || gl.getExtension('WEBKIT_WEBGL_compressed_texture_etc1'); var pvrtc = gl.getExtension('WEBGL_compressed_texture_pvrtc') || gl.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc'); var s3tc = gl.getExtension('WEBGL_compressed_texture_s3tc') || gl.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc'); if (etc1) { this.extensions.compression.ETC1 = etc1; } if (pvrtc) { this.extensions.compression.PVRTC = pvrtc; } if (s3tc) { this.extensions.compression.S3TC = s3tc; } var add = [gl.SRC_ALPHA, gl.DST_ALPHA] ; var normal = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA] ; var multiply = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA] ; var screen = [gl.SRC_ALPHA, gl.ONE] ; this.blendModes = [normal, add, multiply, screen, normal, normal, normal, normal, normal, normal, normal, normal, normal, normal, normal, normal, normal] ; } , enableMultiTextureSupport: function (textureArray){ this.multiTexture = true ; this.batch.initMultiTexture(); if (Array.isArray(textureArray)) { var index = 0; for (var i = 0; i < _AN_Read_length('length', textureArray); i++ ){ var texture = this.game.textures.get(textureArray[i]); index = texture.setTextureIndex(index); } } } , resize: function (width, height){ this.width = width * this.game.resolution; this.height = height * this.game.resolution; this.view.width = this.width; this.view.height = this.height; if (this.autoResize) { this.view.style.width = (this.width / this.game.resolution) + 'px'; this.view.style.height = (this.height / this.game.resolution) + 'px'; } this.gl.viewport(0, 0, this.width, this.height); this.projection.x = (this.width / 2) / this.game.resolution; this.projection.y = - (this.height / 2) / this.game.resolution; } , setTexturePriority: function (textureNameCollection){ var maxTextures = this.maxTextures; var imageCache = this.game.cache._cache.image; var imageName = null ; for (var i = 0; i < _AN_Read_length('length', this.currentBatchedTextures); i++ ){ imageName = textureNameCollection[index]; if (!(imageName in imageCache)) { continue ; } imageCache[imageName].base.textureIndex = 0; } this.currentBatchedTextures.length = 0; for (var index = 0; index < _AN_Read_length('length', textureNameCollection); ++index){ imageName = textureNameCollection[index]; if (!(imageName in imageCache)) { continue ; } if (index + 1 < maxTextures) { imageCache[imageName].base.textureIndex = index + 1; } else { imageCache[imageName].base.textureIndex = maxTextures - 1; } this.currentBatchedTextures.push(imageName); } return this.currentBatchedTextures; } , getMaxTextureUnits: function (){ return this.gl.getParameter(this.gl.MAX_TEXTURE_IMAGE_UNITS); } , render: function (stage){ if (this.contextLost) { return ; } this.startTime = Date.now(); var gl = this.gl; gl.viewport(0, 0, this.width, this.height); gl.bindFramebuffer(gl.FRAMEBUFFER, null ); this.setBlendMode(Phaser.blendModes.NORMAL); this.offset.x = 0; this.offset.y = 0; this.drawCount = 0; this.flipY = 1; this.batch.begin(); stage.render(this, stage); this.batch.end(); this.endTime = Date.now(); } , unloadTexture: function (texture){ var gl = this.gl; var glTexture = texture._glTexture; if (gl && glTexture) { gl.deleteTexture(glTexture); } texture._glTexture = null ; texture._dirty = false ; } , updateTexture: function (source){ if (source.compressionAlgorithm) { return this.updateCompressedTexture(source); } var gl = this.gl; if (!source.glTexture) { source.glTexture = gl.createTexture(); } gl.activeTexture(gl.TEXTURE0 + source.glTextureIndex); gl.bindTexture(gl.TEXTURE_2D, source.glTexture); gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, source.premultipliedAlpha); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source.image); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, source.scaleMode === Phaser.scaleModes.LINEAR? gl.LINEAR: gl.NEAREST); if (source.mipmap && source.isPowerOf2) { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, source.scaleMode === Phaser.scaleModes.LINEAR? gl.LINEAR_MIPMAP_LINEAR: gl.NEAREST_MIPMAP_NEAREST); gl.generateMipmap(gl.TEXTURE_2D); } else { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, source.scaleMode === Phaser.scaleModes.LINEAR? gl.LINEAR: gl.NEAREST); } if (source.isPowerOf2) { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } else { 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); } source.glDirty = false ; return true ; } , updateCompressedTexture: function (texture){ if (!texture.hasLoaded) { return false ; } var gl = this.gl; var textureMetaData = texture.source; if (!texture._glTextures) { texture._glTextures = gl.createTexture(); } gl.activeTexture(gl.TEXTURE0 + texture.textureIndex); gl.bindTexture(gl.TEXTURE_2D, texture._glTextures); gl.compressedTexImage2D(gl.TEXTURE_2D, 0, textureMetaData.glExtensionFormat, textureMetaData.width, textureMetaData.height, 0, textureMetaData.textureData); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === Phaser.scaleModes.LINEAR? gl.LINEAR: gl.NEAREST); if (texture.mipmap && Phaser.Math.isPowerOfTwo(texture.width, texture.height)) { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === Phaser.scaleModes.LINEAR? gl.LINEAR_MIPMAP_LINEAR: gl.NEAREST_MIPMAP_NEAREST); gl.generateMipmap(gl.TEXTURE_2D); } else { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === Phaser.scaleModes.LINEAR? gl.LINEAR: gl.NEAREST); } 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 { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } texture._dirty = false ; return true ; } , setBlendMode: function (newBlendMode){ if (this.currentBlendMode === newBlendMode) { return false ; } var blendModeWebGL = this.blendModes[newBlendMode]; if (blendModeWebGL) { this.currentBlendMode = newBlendMode; this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); return true ; } else { return false ; } } , pushMask: function (maskData){ var gl = this.gl; if (maskData.dirty) { } if (maskData._webGL === undefined || maskData._webGL.data === undefined || _AN_Read_length('length', maskData._webGL.data) === 0) { return ; } this.stencilManager.pushStencil(maskData, maskData._webGL.data[0]); } , popMask: function (maskData){ if (maskData._webGL === undefined || maskData._webGL.data === undefined || _AN_Read_length('length', maskData._webGL.data) === 0) { return ; } this.stencilManager.popStencil(maskData, maskData._webGL.data[0]); } , compileVertexShader: function (src){ return this.compileShader(src, this.gl.VERTEX_SHADER); } , compileFragmentShader: function (src){ return this.compileShader(src, this.gl.FRAGMENT_SHADER); } , compileShader: function (src, type){ if (Array.isArray(src)) { src = src.join('\n'); } var shader = this.gl.createShader(type); this.gl.shaderSource(shader, src); this.gl.compileShader(shader); if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { console.log(this.gl.getShaderInfoLog(shader)); return null ; } return shader; } , compileProgram: function (vertexSrc, fragmentSrc){ var gl = this.gl; var fragmentShader = this.compileFragmentShader(fragmentSrc); var vertexShader = this.compileVertexShader(vertexSrc); var shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, fragmentShader); gl.linkProgram(shaderProgram); if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { console.log(gl.getProgramInfoLog(shaderProgram)); console.log('Could not initialize shaders: Vertex & Fragment'); console.log(vertexSrc.join('\n')); console.log(fragmentSrc.join('\n')); } return shaderProgram; } , deleteProgram: function (program){ var gl = this.gl; gl.deleteProgram(program); return this; } , createEmptyTexture: function (width, height, scaleMode){ var gl = this.gl; var texture = gl.createTexture(); var glScaleMode = (scaleMode === Phaser.scaleModes.LINEAR)? gl.LINEAR: gl.NEAREST; gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, texture); 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); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, glScaleMode); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, glScaleMode); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null ); return texture; } , createFramebuffer: function (width, height, scaleMode, textureUnit){ var gl = this.gl; var framebuffer = gl.createFramebuffer(); var depthStencilBuffer = gl.createRenderbuffer(); var colorBuffer = null ; var fbStatus = 0; gl.activeTexture(gl.TEXTURE0 + textureUnit); gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer); gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, this.renderBuffer); colorBuffer = this.createEmptyTexture(width, height, scaleMode); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, colorBuffer, 0); fbStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); if (fbStatus !== gl.FRAMEBUFFER_COMPLETE) { console.error('Incomplete GL framebuffer. ', this._fbErrors[fbStatus]); } framebuffer.width = width; framebuffer.height = height; framebuffer.targetTexture = colorBuffer; framebuffer.renderBuffer = depthStencilBuffer; return framebuffer; } , destroy: function (){ this.projection = null ; this.offset = null ; this.shaderManager.destroy(); this.batch.destroy(); this.maskManager.destroy(); this.filterManager.destroy(); this.shaderManager = null ; this.batch = null ; this.maskManager = null ; this.filterManager = null ; this.gl = null ; this.renderSession = null ; Phaser.CanvasPool.remove(this); } } ;