From 18b12dfc3e10027b9bf09625b058a86d979b589c Mon Sep 17 00:00:00 2001 From: photonstorm Date: Tue, 18 Oct 2016 17:03:25 +0100 Subject: [PATCH] Huge amount of work getting the WebGL renderer sorted out, tidied up and merged with the latest Texture and Transform components. --- src/core/Filter.js | 41 +- src/gameobjects/image/ImageWebGLRenderer.js | 9 +- src/gameobjects/stage/StageWebGLRenderer.js | 11 +- src/loader/Cache.js | 4 +- src/renderer/webgl/BatchManager.js | 597 ++++++++++++++------ src/renderer/webgl/ShaderManager.js | 12 +- src/renderer/webgl/WebGLRenderer.js | 91 +-- src/renderer/webgl/shaders/Sprite.js | 78 ++- src/renderer/webgl/shaders/SpriteBatch.js | 47 +- src/textures/Frame.js | 46 +- src/textures/Texture.js | 35 +- src/textures/TextureSource.js | 36 +- 12 files changed, 687 insertions(+), 320 deletions(-) diff --git a/src/core/Filter.js b/src/core/Filter.js index 207e45cfc..479bd2b16 100644 --- a/src/core/Filter.js +++ b/src/core/Filter.js @@ -32,8 +32,8 @@ * @param {object} [uniforms] - Uniform mappings object. The uniforms are added on the default uniforms, or replace them if the keys are the same. * @param {Array|string} [fragmentSrc] - The fragment shader code. Either an array, one element per line of code, or a string. */ -Phaser.Filter = function (game, uniforms, fragmentSrc) { - +Phaser.Filter = function (game, uniforms, fragmentSrc) +{ /** * @property {Phaser.Game} game - A reference to the currently running game. */ @@ -90,7 +90,7 @@ Phaser.Filter = function (game, uniforms, fragmentSrc) { resolution: { type: '2f', value: { x: 256, y: 256 }}, time: { type: '1f', value: 0 }, mouse: { type: '2f', value: { x: 0.0, y: 0.0 } }, - date: { type: '4fv', value: [ d.getFullYear(), d.getMonth(), d.getDate(), d.getHours() *60 * 60 + d.getMinutes() * 60 + d.getSeconds() ] }, + date: { type: '4fv', value: [ d.getFullYear(), d.getMonth(), d.getDate(), d.getHours() * 60 * 60 + d.getMinutes() * 60 + d.getSeconds() ] }, sampleRate: { type: '1f', value: 44100.0 }, iChannel0: { type: 'sampler2D', value: null, textureData: { repeat: true } }, iChannel1: { type: 'sampler2D', value: null, textureData: { repeat: true } }, @@ -125,10 +125,11 @@ Phaser.Filter.prototype = { /** * This should be over-ridden. Will receive a variable number of arguments. - * + * * @method Phaser.Filter#init */ - init: function () { + init: function () + { // This should be over-ridden. Will receive a variable number of arguments. @@ -141,21 +142,20 @@ Phaser.Filter.prototype = { * @param {number} width - The width of the display. * @param {number} height - The height of the display. */ - setResolution: function (width, height) { - + setResolution: function (width, height) + { this.uniforms.resolution.value.x = width; this.uniforms.resolution.value.y = height; - }, /** * Updates the filter. - * + * * @method Phaser.Filter#update * @param {Phaser.Pointer} [pointer] - A Pointer object to use for the filter. The coordinates are mapped to the mouse uniform. */ - update: function (pointer) { - + update: function (pointer) + { if (pointer) { var x = pointer.x / this.game.width; @@ -170,11 +170,10 @@ Phaser.Filter.prototype = { } this.uniforms.time.value = this.game.time.totalElapsedSeconds(); - }, /** - * Creates a new Phaser.Image object using a blank texture and assigns + * Creates a new Phaser.Image object using a blank texture and assigns * this Filter to it. The image is then added to the world. * * If you don't provide width and height values then Filter.width and Filter.height are used. @@ -191,8 +190,8 @@ Phaser.Filter.prototype = { * @param {number} [anchorY=0] - Set the y anchor point of the Image. A value between 0 and 1, where 0 is the top-left and 1 is bottom-right. * @return {Phaser.Image} The newly added Image object. */ - addToWorld: function (x, y, width, height, anchorX, anchorY) { - + addToWorld: function (x, y, width, height, anchorX, anchorY) + { if (anchorX === undefined) { anchorX = 0; } if (anchorY === undefined) { anchorY = 0; } @@ -232,22 +231,21 @@ Phaser.Filter.prototype = { * * @method Phaser.Filter#syncUniforms */ - syncUniforms: function () { - + syncUniforms: function () + { for (var i = 0; i < this.shaders.length; i++) { this.shaders[i].dirty = true; } - }, /** * Clear down this Filter and null out references to game. - * + * * @method Phaser.Filter#destroy */ - destroy: function () { - + destroy: function () + { this.passes.length = 0; this.shaders.length = 0; this.fragmentSrc.length = 0; @@ -255,7 +253,6 @@ Phaser.Filter.prototype = { this.game = null; this.uniforms = null; this.prevPoint = null; - } }; diff --git a/src/gameobjects/image/ImageWebGLRenderer.js b/src/gameobjects/image/ImageWebGLRenderer.js index cb0753907..152b32be7 100644 --- a/src/gameobjects/image/ImageWebGLRenderer.js +++ b/src/gameobjects/image/ImageWebGLRenderer.js @@ -6,14 +6,15 @@ Phaser.Renderer.WebGL.GameObjects.Image = { render: function (renderer, src) { - // If the sprite is not visible or the alpha is 0 then no need to render this element - if (!src.visible || src.alpha === 0 || !src.renderable) + var frame = src.frame; + + // Skip rendering? + + if (src.skipRender || !src.visible || src.worldAlpha <= 0 || !frame.cutWidth || !frame.cutHeight) { return; } - // Add back in: || src.texture.crop.width <= 0 || src.texture.crop.height <= 0 - renderer.spriteBatch.render(src); } diff --git a/src/gameobjects/stage/StageWebGLRenderer.js b/src/gameobjects/stage/StageWebGLRenderer.js index 66bcaf811..effec2d97 100644 --- a/src/gameobjects/stage/StageWebGLRenderer.js +++ b/src/gameobjects/stage/StageWebGLRenderer.js @@ -11,11 +11,19 @@ Phaser.Renderer.WebGL.GameObjects.Stage = { render: function (renderer, src) { - if (src.visible === false || src.alpha === 0 || src.children.length === 0) + if (!src.visible || src.alpha === 0 || src.children.list.length === 0) { return; } + for (var i = 0; i < src.children.list.length; i++) + { + var child = src.children.list[i]; + + child.render(renderer, child); + } + + /* var i; if (src._mask || src._filters) @@ -64,6 +72,7 @@ Phaser.Renderer.WebGL.GameObjects.Stage = { child.render(renderer, child); } } + */ } diff --git a/src/loader/Cache.js b/src/loader/Cache.js index 187ff4265..4b4fffc02 100644 --- a/src/loader/Cache.js +++ b/src/loader/Cache.js @@ -329,7 +329,7 @@ Phaser.Cache.prototype = { img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAAA1BMVEX///+nxBvIAAAAAXRSTlMAQObYZgAAABVJREFUeF7NwIEAAAAAgKD9qdeocAMAoAABm3DkcAAAAABJRU5ErkJggg=='; - this.game.textures.addImage('__DEFAULT', img); + // this.game.textures.addImage('__DEFAULT', img); }, /** @@ -347,7 +347,7 @@ Phaser.Cache.prototype = { img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJ9JREFUeNq01ssOwyAMRFG46v//Mt1ESmgh+DFmE2GPOBARKb2NVjo+17PXLD8a1+pl5+A+wSgFygymWYHBb0FtsKhJDdZlncG2IzJ4ayoMDv20wTmSMzClEgbWYNTAkQ0Z+OJ+A/eWnAaR9+oxCF4Os0H8htsMUp+pwcgBBiMNnAwF8GqIgL2hAzaGFFgZauDPKABmowZ4GL369/0rwACp2yA/ttmvsQAAAABJRU5ErkJggg=='; - this.game.textures.addImage('__MISSING', img); + // this.game.textures.addImage('__MISSING', img); }, /** diff --git a/src/renderer/webgl/BatchManager.js b/src/renderer/webgl/BatchManager.js index efd293c20..5af112177 100644 --- a/src/renderer/webgl/BatchManager.js +++ b/src/renderer/webgl/BatchManager.js @@ -59,18 +59,144 @@ Phaser.Renderer.WebGL.BatchManager = function (renderer) this.indices[i + 5] = j + 3; } - this.drawing = false; + this.currentTextureSource = null; + + // this.drawing = false; this.currentBatchSize = 0; - this.currentBaseTexture = null; this.dirty = true; this.textures = []; this.blendModes = []; this.shaders = []; this.sprites = []; - this.defaultShader = null; + // this.defaultShader = null; - this.MAX_TEXTURES = 0; + // Let's Merge Sprite in here + + /** + * The WebGL program. + * @property program + * @type Any + */ + this.program = null; + + /** + * The Default Vertex shader source. + * + * @property defaultVertexSrc + * @type String + */ + this.multivertexSrc = [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + 'attribute float aTextureIndex;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'varying float vTextureIndex;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' if (aTextureIndex > 0.0) gl_Position = vec4(0.0);', + ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + ' vTextureIndex = aTextureIndex;', + '}' + ]; + + this.vertexSrc = [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform vec2 projectionVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' gl_Position = vec4((aVertexPosition / projectionVector) + center, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vec3 color = mod(vec3(aColor.y / 65536.0, aColor.y / 256.0, aColor.y), 256.0) / 256.0;', + ' vColor = vec4(color * aColor.x, aColor.x);', + '}' + ]; + + /** + * The fragment shader. + * @property fragmentSrc + * @type Array + */ + this.multifragmentSrc = [ + 'precision lowp float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'varying float vTextureIndex;', + 'uniform sampler2D uSampler;', + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;', + '}' + ]; + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform sampler2D uSampler;', + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;', + '}' + ]; + + /** + * Uniform attributes cache. + * @property attributes + * @type Array + * @private + */ + this.attributes = []; + + /** + * A local texture counter for multi-texture shaders. + * @property textureCount + * @type Number + */ + this.textureCount = 0; + + // @type {WebGLUniformLocation } + this.uSampler; + + // @type {WebGLUniformLocation } + this.projectionVector; + + // @type {WebGLUniformLocation } + this.offsetVector; + + // @type {GLint} + this.colorAttribute; + + // @type {GLint} + this.aTextureIndex; + + // @type {GLint} + this.aVertexPosition; + + // @type {GLint} + this.aTextureCoord; + + // @type {WebGLUniformLocation } + this.translationMatrix; + + // @type {WebGLUniformLocation } + this.alpha; }; Phaser.Renderer.WebGL.BatchManager.prototype.constructor = Phaser.Renderer.WebGL.BatchManager; @@ -81,55 +207,56 @@ Phaser.Renderer.WebGL.BatchManager.prototype = { { this.gl = this.renderer.gl; - var gl = this.gl; - - this.MAX_TEXTURES = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); - if (this.renderer.enableMultiTextureToggle) { - var dynamicIfs = '\tif (vTextureIndex == 0.0) gl_FragColor = texture2D(uSamplerArray[0], vTextureCoord) * vColor;\n' - - for (var index = 1; index < this.MAX_TEXTURES; ++index) - { - dynamicIfs += '\telse if (vTextureIndex == ' + - index + '.0) gl_FragColor = texture2D(uSamplerArray[' + - index + '], vTextureCoord) * vColor;\n' - } - - this.defaultShader = new Phaser.Filter( - this.renderer.game, - undefined, - [ - '//WebGLBatchManager Fragment Shader.', - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'varying float vTextureIndex;', - 'uniform sampler2D uSamplerArray[' + this.MAX_TEXTURES + '];', - 'void main(void) {', - dynamicIfs, - '\telse gl_FragColor = texture2D(uSamplerArray[0], vTextureCoord) * vColor;', - '}' - ]); - } - else - { - this.defaultShader = new Phaser.Filter( - this.renderer.game, - undefined, - [ - '//WebGLBatchManager Fragment Shader.', - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'varying float vTextureIndex;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;', - '}' - ]); + this.initMultitexShader(); } + console.log('Compiled BatchManager Shader'); + + // console.log(this.vertexSrc.join('\n')); + // console.log(this.fragmentSrc.join('\n')); + // console.log('compiling ...'); + + var gl = this.gl; + + var program = this.renderer.compileProgram(this.vertexSrc, this.fragmentSrc); + + // Set Shader + gl.useProgram(program); + + // Get and store the attributes + this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); + this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); + this.colorAttribute = gl.getAttribLocation(program, 'aColor'); + // this.aTextureIndex = gl.getAttribLocation(program, 'aTextureIndex'); + + // Get and store the uniforms for the shader + // this part is different for multi-textures + this.uSampler = gl.getUniformLocation(program, 'uSampler'); + + // vertex position + gl.enableVertexAttribArray(0); + + // texture coordinate + gl.enableVertexAttribArray(1); + + // color attribute + gl.enableVertexAttribArray(2); + + // texture index + // gl.enableVertexAttribArray(3); + + // The projection vector (middle of the game world) + this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); + // this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); + + this.program = program; + + // setAttribs can also call this (which handles the enable array calls above) + // this.attributes = [ this.aVertexPosition, this.aTextureCoord, this.colorAttribute, this.aTextureIndex ]; + this.attributes = [ this.aVertexPosition, this.aTextureCoord, this.colorAttribute ]; + // Create a couple of buffers this.vertexBuffer = gl.createBuffer(); this.indexBuffer = gl.createBuffer(); @@ -142,25 +269,73 @@ Phaser.Renderer.WebGL.BatchManager.prototype = { gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); + }, - this.currentBlendMode = 99999; + initMultiTextureShader: function () + { + this.gl = this.renderer.gl; - var shader = new Phaser.Renderer.WebGL.Shaders.Sprite(this.renderer); + // var gl = this.gl; - shader.fragmentSrc = this.defaultShader.fragmentSrc; - shader.uniforms = {}; - shader.init(); + // New Fragment Source ... - this.defaultShader.shaders = shader; + /* + if (this.renderer.enableMultiTextureToggle) + { + var dynamicIfs = '\tif (vTextureIndex == 0.0) gl_FragColor = texture2D(uSamplerArray[0], vTextureCoord) * vColor;\n'; + + for (var index = 1; index < this.MAX_TEXTURES; ++index) + { + dynamicIfs += '\telse if (vTextureIndex == ' + + index + '.0) gl_FragColor = texture2D(uSamplerArray[' + + index + '], vTextureCoord) * vColor;\n'; + } + + // Does this need the vTextureIndex varying? Doesn't look like it + + this.defaultShader = new Phaser.Filter( + this.renderer.game, + undefined, + [ + 'precision lowp float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'varying float vTextureIndex;', + 'uniform sampler2D uSamplerArray[' + this.MAX_TEXTURES + '];', + 'void main(void) {', + dynamicIfs, + '\telse gl_FragColor = texture2D(uSamplerArray[0], vTextureCoord) * vColor;', + '}' + ]); + } + else + { + // Does this need the vTextureIndex varying? Doesn't look like it + + this.defaultShader = new Phaser.Filter( + this.renderer.game, + undefined, + [ + 'precision lowp float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'varying float vTextureIndex;', + 'uniform sampler2D uSampler;', + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;', + '}' + ]); + } + */ }, begin: function () { - this.shader = this.renderer.shaderManager.defaultShader; this.start(); }, - start: function () { + start: function () + { this.dirty = true; }, @@ -169,55 +344,63 @@ Phaser.Renderer.WebGL.BatchManager.prototype = { this.flush(); }, - stop: function () { + stop: function () + { this.flush(); this.dirty = true; }, - render: function (sprite) + render: function (src) { - var texture = sprite.texture; - var baseTexture = texture.baseTexture; - var gl = this.gl; + var frame = src.frame; + var source = frame.source; - if (this.renderer.textureArray[baseTexture.textureIndex] !== baseTexture) + // Check TextureSource + if (this.currentTextureSource !== source) + // if (this.renderer.textureArray[source.glTextureIndex] !== source) { - this.flush(); - gl.activeTexture(gl.TEXTURE0 + baseTexture.textureIndex); - gl.bindTexture(gl.TEXTURE_2D, baseTexture._glTextures); - this.renderer.textureArray[baseTexture.textureIndex] = baseTexture; + if (this.currentBatchSize > 0) + { + this.flush(); + } + + var gl = this.gl; + + gl.activeTexture(gl.TEXTURE0); + // gl.activeTexture(gl.TEXTURE0 + source.glTextureIndex); + gl.bindTexture(gl.TEXTURE_2D, source.glTexture); + + // this.renderer.textureArray[source.glTextureIndex] = source; + this.currentTextureSource = source; } - // check texture.. + // Check Batch Size if (this.currentBatchSize >= this.size) { this.flush(); - this.currentBaseTexture = texture.baseTexture; + this.currentTextureSource = source; } - // get the uvs for the texture - var uvs = texture._uvs; + // MOVE ALL OF THIS INTO THE SPRITE / IMAGE WEBGL CLASSES? and just expose 'addVerts' here instead - // if the uvs have not updated then no point rendering just yet! - if (!uvs) - { - return; - } + // Get the Texture UVs + var uvs = frame.uvs; - var aX = sprite.anchor.x; - var aY = sprite.anchor.y; + var aX = src.anchorX; + var aY = src.anchorY; var w0, w1, h0, h1; + /* if (texture.trim) { - // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords. + // If the sprite is trimmed, add the extra space before transforming var trim = texture.trim; - w1 = trim.x - aX * trim.width; + w1 = trim.x - (aX * trim.width); w0 = w1 + texture.crop.width; - h1 = trim.y - aY * trim.height; + h1 = trim.y - (aY * trim.height); h0 = h1 + texture.crop.height; } else @@ -228,13 +411,20 @@ Phaser.Renderer.WebGL.BatchManager.prototype = { h0 = texture.frame.height * (1 - aY); h1 = texture.frame.height * -aY; } + */ - var i = this.currentBatchSize * this.vertexSize; //4 * this.vertSize; - var tiOffset = this.currentBatchSize * 4; - var resolution = texture.baseTexture.resolution; - var textureIndex = texture.baseTexture.textureIndex; + w0 = (frame.width) * (1 - aX); + w1 = (frame.width) * -aX; - var wt = sprite.worldTransform; + h0 = frame.height * (1 - aY); + h1 = frame.height * -aY; + + var i = this.currentBatchSize * this.vertexSize; + + var resolution = source.resolution; + var textureIndex = source.textureIndex; + + var wt = src.transform.world; var a = wt.a / resolution; var b = wt.b / resolution; @@ -243,9 +433,10 @@ Phaser.Renderer.WebGL.BatchManager.prototype = { var tx = wt.tx; var ty = wt.ty; - var cw = texture.crop.width; - var ch = texture.crop.height; + // var cw = frame.cutWidth; + // var ch = frame.cutHeight; + /* if (texture.rotated) { var a0 = wt.a; @@ -273,13 +464,17 @@ Phaser.Renderer.WebGL.BatchManager.prototype = { w0 = h0; w1 = h1; h0 = _w0; - h1 = _w1; + h1 = _w1; } + */ + // These are just views into the same typed array var colors = this.colors; var positions = this.positions; - var tint = sprite.tint; - var color = (tint >> 16) + (tint & 0xff00) + ((tint & 0xff) << 16) + (sprite.worldAlpha * 255 << 24); + + // var tint = sprite.tint; + var tint = 0xffffff; + var color = (tint >> 16) + (tint & 0xff00) + ((tint & 0xff) << 16) + (src.worldAlpha * 255 << 24); if (this.renderer.roundPixels) { @@ -287,6 +482,11 @@ Phaser.Renderer.WebGL.BatchManager.prototype = { ty |= 0; } + // Interleaved vert and color data + // The color passed here includes an applied tint *see bitwise ops above * + // Better to calculate that in the shader? + + // Top Left vert (xy, uv, color) positions[i++] = a * w1 + c * h1 + tx; positions[i++] = d * h1 + b * w1 + ty; positions[i++] = uvs.x0; @@ -294,6 +494,7 @@ Phaser.Renderer.WebGL.BatchManager.prototype = { colors[i++] = color; positions[i++] = textureIndex; + // Top Right vert (xy, uv, color) positions[i++] = a * w0 + c * h1 + tx; positions[i++] = d * h1 + b * w0 + ty; positions[i++] = uvs.x1; @@ -301,6 +502,7 @@ Phaser.Renderer.WebGL.BatchManager.prototype = { colors[i++] = color; positions[i++] = textureIndex; + // Bottom Right vert (xy, uv, color) positions[i++] = a * w0 + c * h0 + tx; positions[i++] = d * h0 + b * w0 + ty; positions[i++] = uvs.x2; @@ -308,6 +510,7 @@ Phaser.Renderer.WebGL.BatchManager.prototype = { colors[i++] = color; positions[i++] = textureIndex; + // Bottom Left vert (xy, uv, color) positions[i++] = a * w1 + c * h0 + tx; positions[i++] = d * h0 + b * w1 + ty; positions[i++] = uvs.x3; @@ -315,10 +518,56 @@ Phaser.Renderer.WebGL.BatchManager.prototype = { colors[i++] = color; positions[i++] = textureIndex; - // increment the batchsize - this.sprites[this.currentBatchSize++] = sprite; + this.sprites[this.currentBatchSize++] = src; }, + /* + addVerts: function (uvs, wt, w0, h0, w1, h1, alpha, tint) + { + var a = wt.a; + var b = wt.b; + var c = wt.c; + var d = wt.d; + var tx = wt.tx; + var ty = wt.ty; + + var verts = this.vertices; + var i = this._size * 4 * this.vertSize; + + // Top Left vert (xy, uv, color) + verts[i++] = a * w1 + c * h1 + tx; + verts[i++] = d * h1 + b * w1 + ty; + verts[i++] = uvs.x0; + verts[i++] = uvs.y0; + verts[i++] = alpha; + verts[i++] = tint[0]; + + // Top Right vert (xy, uv, color) + verts[i++] = a * w0 + c * h1 + tx; + verts[i++] = d * h1 + b * w0 + ty; + verts[i++] = uvs.x1; + verts[i++] = uvs.y1; + verts[i++] = alpha; + verts[i++] = tint[1]; + + // Bottom Right vert (xy, uv, color) + verts[i++] = a * w0 + c * h0 + tx; + verts[i++] = d * h0 + b * w0 + ty; + verts[i++] = uvs.x2; + verts[i++] = uvs.y2; + verts[i++] = alpha; + verts[i++] = tint[2]; + + // Bottom Left vert (xy, uv, color) + verts[i++] = a * w1 + c * h0 + tx; + verts[i++] = d * h0 + b * w1 + ty; + verts[i++] = uvs.x3; + verts[i++] = uvs.y3; + verts[i++] = alpha; + verts[i++] = tint[3]; + }, + */ + flush: function () { // If the batch is length 0 then return as there is nothing to draw @@ -328,33 +577,37 @@ Phaser.Renderer.WebGL.BatchManager.prototype = { } var gl = this.gl; - var shader; if (this.dirty) { + // Always dirty the first pass through + // but subsequent calls may be clean this.dirty = false; - shader = this.defaultShader.shaders; - // bind the main texture gl.activeTexture(gl.TEXTURE0); // bind the buffers gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - // this is the same for each shader? - var stride = this.vertexSize; //this.vertSize * 4; - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, stride, 0); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, stride, 8); - // color attributes will be interpreted as unsigned bytes and normalized - gl.vertexAttribPointer(shader.colorAttribute, 4, gl.UNSIGNED_BYTE, true, stride, 16); + // set the projection vector (defaults to middle of game world on negative y) + gl.uniform2f(this.projectionVector, this.renderer.projection.x, this.renderer.projection.y); - // Texture index - gl.vertexAttribPointer(shader.aTextureIndex, 1, gl.FLOAT, false, stride, 20); + // vertex position + gl.vertexAttribPointer(this.aVertexPosition, 2, gl.FLOAT, false, this.stride, 0); + + // texture coordinate + gl.vertexAttribPointer(this.aTextureCoord, 2, gl.FLOAT, false, this.stride, 8); + + // color attribute + gl.vertexAttribPointer(this.colorAttribute, 2, gl.FLOAT, false, this.stride, 16); + + // texture index + // gl.vertexAttribPointer(this.aTextureIndex, 2, gl.FLOAT, false, this.stride, 20); } - // upload the verts to the buffer + // Upload verts to the buffer if (this.currentBatchSize > (this.size * 0.5)) { gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices); @@ -362,109 +615,79 @@ Phaser.Renderer.WebGL.BatchManager.prototype = { else { gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + var view = this.positions.subarray(0, this.currentBatchSize * this.vertexSize); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); } - var nextTexture, nextBlendMode, nextShader; - var batchSize = 0; - var start = 0; - - var currentBaseTexture = null; - var currentBlendMode = this.renderer.currentBlendMode; - var currentShader = null; - - var blendSwap = false; - var shaderSwap = false; var sprite; - var textureIndex = 0; - for (var i = 0, j = this.currentBatchSize; i < j; i++) + var start = 0; + var currentSize = 0; + + var nextSource = null; + + var blend = 0; + var nextBlend = null; + + for (var i = 0; i < this.currentBatchSize; i++) { sprite = this.sprites[i]; - if (sprite.tilingTexture) + nextBlend = sprite.blendMode; + + if (blend !== nextBlend) { - nextTexture = sprite.tilingTexture.baseTexture; - } - else - { - nextTexture = sprite.texture.baseTexture; + + // Unrolled for speed + /* + if (nextBlend === this.renderer.blendModes.NORMAL) + { + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + } + else if (nextBlend === BlendModes.ADD) + { + gl.blendFunc(gl.SRC_ALPHA, gl.DST_ALPHA); + } + else if (nextBlend === BlendModes.MULTIPLY) + { + gl.blendFunc(gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA); + } + else if (nextBlend === BlendModes.SCREEN) + { + gl.blendFunc(gl.SRC_ALPHA, gl.ONE); + } + */ } - nextBlendMode = sprite.blendMode; - nextShader = sprite.shader || this.defaultShader; + nextSource = sprite.frame.source; - blendSwap = currentBlendMode !== nextBlendMode; - shaderSwap = currentShader !== nextShader; - - var skip = nextTexture.skipRender; - - if (skip && sprite.children.length > 0) + if (nextSource !== this.currentTextureSource) { - skip = false; - } - - if (blendSwap || shaderSwap) - { - this.renderBatch(currentBaseTexture, batchSize, start); + if (currentSize > 0) + { + this.renderBatch(this.currentTextureSource, currentSize, start); + } start = i; - batchSize = 0; - currentBaseTexture = nextTexture; - - if (blendSwap) - { - currentBlendMode = nextBlendMode; - this.renderer.setBlendMode(currentBlendMode); - } - - if (shaderSwap) - { - currentShader = nextShader; - - shader = currentShader.shaders; - - if (!shader) - { - shader = new PIXI.PixiShader(gl); - - shader.fragmentSrc = currentShader.fragmentSrc; - shader.uniforms = currentShader.uniforms; - shader.init(); - - currentShader.shaders = shader; - } - - this.renderer.shaderManager.setShader(shader); - - if (shader.dirty) - { - shader.syncUniforms(); - } - - // both these only need to be set if they are changing.. - // set the projection - var projection = this.renderer.projection; - - gl.uniform2f(shader.projectionVector, projection.x, projection.y); - - var offsetVector = this.renderer.offset; - - gl.uniform2f(shader.offsetVector, offsetVector.x, offsetVector.y); - } + currentSize = 0; + this.currentTextureSource = nextSource; } - batchSize++; + currentSize++; } - this.renderBatch(currentBaseTexture, batchSize, start); + if (currentSize > 0) + { + this.renderBatch(this.currentTextureSource, currentSize, start); + } - // then reset the batch! + // Reset the batch this.currentBatchSize = 0; }, - renderBatch: function (texture, size, startIndex) + renderBatch: function (source, size, startIndex) { if (size === 0) { @@ -473,10 +696,9 @@ Phaser.Renderer.WebGL.BatchManager.prototype = { var gl = this.gl; - // check if a texture is dirty.. - if (texture._dirty) + if (source.glDirty) { - if (!this.renderer.updateTexture(texture)) + if (!this.renderer.updateTexture(source)) { // If updateTexture returns false then we cannot render it, so bail out now return; @@ -485,7 +707,6 @@ Phaser.Renderer.WebGL.BatchManager.prototype = { gl.drawElements(gl.TRIANGLES, size * 6, gl.UNSIGNED_SHORT, startIndex * 6 * 2); - // increment the draw count this.renderer.drawCount++; }, diff --git a/src/renderer/webgl/ShaderManager.js b/src/renderer/webgl/ShaderManager.js index a060625a1..f5c961c9c 100644 --- a/src/renderer/webgl/ShaderManager.js +++ b/src/renderer/webgl/ShaderManager.js @@ -48,21 +48,23 @@ Phaser.Renderer.WebGL.ShaderManager.prototype = { init: function () { + return; + this.gl = this.renderer.gl; - // this shader is used for the default sprite rendering + // This shader is used for the batched rendering of Images and Sprites this.defaultShader = new Phaser.Renderer.WebGL.Shaders.Sprite(this.renderer); - // this shader is used for the fast sprite rendering + // This shader is used for SpriteBatch Game Object rendering this.fastShader = new Phaser.Renderer.WebGL.Shaders.SpriteBatch(this.renderer); - // the next one is used for rendering triangle strips + // Tiling Sprites / Strips this.stripShader = new Phaser.Renderer.WebGL.Shaders.Strip(this.renderer); - // the next one is used for rendering primitives + // Simple Graphics (when vertices count is low) this.primitiveShader = new Phaser.Renderer.WebGL.Shaders.PrimitiveGraphics(this.renderer); - // the next one is used by the stencil buffer manager when Graphics.mode = 1 + // The next one is used by the stencil buffer manager when Graphics.mode = 1 this.complexPrimitiveShader = new Phaser.Renderer.WebGL.Shaders.ComplexPrimitiveGraphics(this.renderer); this.setShader(this.defaultShader); diff --git a/src/renderer/webgl/WebGLRenderer.js b/src/renderer/webgl/WebGLRenderer.js index d09414ee3..98b4f7017 100644 --- a/src/renderer/webgl/WebGLRenderer.js +++ b/src/renderer/webgl/WebGLRenderer.js @@ -155,13 +155,16 @@ Phaser.Renderer.WebGL = function (game) this.gl = null; - this.textureArray = []; + // Add a null entry to avoid an array look-up miss + this.textureArray = [ null, null ]; this.blendModes = []; this.drawCount = 0; this.flipY = 1; + this.contextLost = false; + this._fbErrors = { 36054: 'Incomplete attachment', 36055: 'Missing attachment', @@ -186,6 +189,7 @@ Phaser.Renderer.WebGL.prototype = { if (!this.gl) { + this.contextLost = true; throw new Error('This browser does not support WebGL. Try using the Canvas renderer.'); } @@ -378,7 +382,7 @@ Phaser.Renderer.WebGL.prototype = { */ render: function (stage) { - // no point rendering if our context has been blown up! + // No point rendering if our context has been blown up! if (this.contextLost) { return; @@ -390,20 +394,39 @@ Phaser.Renderer.WebGL.prototype = { gl.viewport(0, 0, this.width, this.height); - // make sure we are bound to the main frame buffer + // Make sure we are bound to the main frame buffer gl.bindFramebuffer(gl.FRAMEBUFFER, null); - if (this.game.clearBeforeRender) + // Transparent + gl.clearColor(0, 0, 0, 0); + + // Black + // gl.clearColor(0, 0, 0, 1); + // gl.clear(gl.COLOR_BUFFER_BIT); + + // Normal Blend Mode + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + + /* + if (this.clearBeforeRender) { - gl.clearColor(stage._bgColor.r, stage._bgColor.g, stage._bgColor.b, stage._bgColor.a); + // gl.clearColor(stage._bgColor.r, stage._bgColor.g, stage._bgColor.b, stage._bgColor.a); + gl.clearColor(0, 0.5, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); } - this.offset.x = this.game.camera._shake.x; - this.offset.y = this.game.camera._shake.y; + // this.offset.x = this.game.camera._shake.x; + // this.offset.y = this.game.camera._shake.y; + + this.offset.x = 0; + this.offset.y = 0; this.setBlendMode(this.blendModes.NORMAL); + */ + + this.offset.x = 0; + this.offset.y = 0; // Reset draw count this.drawCount = 0; @@ -443,57 +466,55 @@ Phaser.Renderer.WebGL.prototype = { texture._dirty = false; }, - updateTexture: function (texture) + // Takes a TextureSource object + updateTexture: function (source) { - if (!texture.hasLoaded) + if (source.compressionAlgorithm) { - return false; - } - - if (texture.source.compressionAlgorithm) - { - return this.updateCompressedTexture(texture); + return this.updateCompressedTexture(source); } var gl = this.gl; - if (!texture._glTextures) + if (!source.glTexture) { - texture._glTextures = gl.createTexture(); + source.glTexture = gl.createTexture(); } - gl.activeTexture(gl.TEXTURE0 + texture.textureIndex); + gl.activeTexture(gl.TEXTURE0 + source.glTextureIndex); - gl.bindTexture(gl.TEXTURE_2D, texture._glTextures); + gl.bindTexture(gl.TEXTURE_2D, source.glTexture); - gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultipliedAlpha); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, source.premultipliedAlpha); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.source); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source.image); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === Phaser.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, source.scaleMode === Phaser.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); - if (texture.mipmap && Phaser.Math.isPowerOfTwo(texture.width, texture.height)) + if (source.mipmap && source.isPowerOf2) { - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === Phaser.scaleModes.LINEAR ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); + 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, texture.scaleMode === Phaser.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, source.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 + 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); + } - texture._dirty = false; + source.glDirty = false; + + console.log('updateTexture', source.glTexture); return true; }, @@ -657,7 +678,9 @@ Phaser.Renderer.WebGL.prototype = { if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { console.log(gl.getProgramInfoLog(shaderProgram)); - console.log('Could not initialize shaders'); + console.log('Could not initialize shaders: Vertex & Fragment'); + console.log(vertexSrc.join('\n')); + console.log(fragmentSrc.join('\n')); } return shaderProgram; @@ -685,7 +708,7 @@ Phaser.Renderer.WebGL.prototype = { var gl = this.gl; var framebuffer = gl.createFramebuffer(); var depthStencilBuffer = gl.createRenderbuffer(); - var colorBuffer = null; + var colorBuffer = null; var fbStatus = 0; gl.activeTexture(gl.TEXTURE0 + textureUnit); diff --git a/src/renderer/webgl/shaders/Sprite.js b/src/renderer/webgl/shaders/Sprite.js index aa48e01d3..859584913 100644 --- a/src/renderer/webgl/shaders/Sprite.js +++ b/src/renderer/webgl/shaders/Sprite.js @@ -128,28 +128,66 @@ Phaser.Renderer.WebGL.Shaders.Sprite.prototype = { else { this.initDefaultShader(); - } + } }, initDefaultShader: function () { if (this.fragmentSrc === null) { - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'varying float vTextureIndex;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; + // this.fragmentSrc = [ + // 'precision lowp float;', + // 'varying vec2 vTextureCoord;', + // 'varying vec4 vColor;', + // 'varying float vTextureIndex;', + // 'uniform sampler2D uSampler;', + // 'void main(void) {', + // ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + // '}' + // ]; } + var fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform sampler2D uSampler;', + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;', + '}' + ]; + + var vertexSrc = [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform vec2 projectionVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' gl_Position = vec4((aVertexPosition / projectionVector) + center, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vec3 color = mod(vec3(aColor.y / 65536.0, aColor.y / 256.0, aColor.y), 256.0) / 256.0;', + ' vColor = vec4(color * aColor.x, aColor.x);', + '}' + ]; + + + console.log('initDefaultShader'); + console.log(vertexSrc.join('\n')); + console.log(fragmentSrc.join('\n')); + console.log('compiling ...'); + var gl = this.gl; - var program = this.renderer.compileProgram(this.vertexSrc, this.fragmentSrc); + var program = this.renderer.compileProgram(vertexSrc, fragmentSrc); + + // var program = this.renderer.compileProgram(this.vertexSrc, this.fragmentSrc); gl.useProgram(program); @@ -163,16 +201,20 @@ Phaser.Renderer.WebGL.Shaders.Sprite.prototype = { this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - this.aTextureIndex = gl.getAttribLocation(program, 'aTextureIndex'); + // this.aTextureIndex = gl.getAttribLocation(program, 'aTextureIndex'); - this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute, this.aTextureIndex]; + // this.attributes = [ this.aVertexPosition, this.aTextureCoord, this.colorAttribute, this.aTextureIndex ]; + this.attributes = [ this.aVertexPosition, this.aTextureCoord, this.colorAttribute ]; - // add those custom shaders! + // Add those custom shaders! + + /* for (var key in this.uniforms) { // get the uniform locations.. this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); } + */ this.initUniforms(); @@ -256,6 +298,8 @@ Phaser.Renderer.WebGL.Shaders.Sprite.prototype = { initUniforms: function () { + console.log('initUniforms', this.uniforms); + this.textureCount = 1; var gl = this.gl; var uniform; @@ -331,6 +375,8 @@ Phaser.Renderer.WebGL.Shaders.Sprite.prototype = { */ initSampler2D: function (uniform) { + console.log('initSampler2D'); + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { return; @@ -406,6 +452,8 @@ Phaser.Renderer.WebGL.Shaders.Sprite.prototype = { */ syncUniforms: function () { + console.log('syncUniforms'); + this.textureCount = 1; var uniform; diff --git a/src/renderer/webgl/shaders/SpriteBatch.js b/src/renderer/webgl/shaders/SpriteBatch.js index 42f035363..5a21c4cc8 100644 --- a/src/renderer/webgl/shaders/SpriteBatch.js +++ b/src/renderer/webgl/shaders/SpriteBatch.js @@ -91,13 +91,13 @@ Phaser.Renderer.WebGL.Shaders.SpriteBatch.prototype = { { if (this.renderer.enableMultiTextureToggle) { - var dynamicIfs = '\tif (vTextureIndex == 0.0) gl_FragColor = texture2D(uSamplerArray[0], vTextureCoord) * vColor;\n' + var dynamicIfs = '\tif (vTextureIndex == 0.0) gl_FragColor = texture2D(uSamplerArray[0], vTextureCoord) * vColor;\n'; for (var index = 1; index < this.renderer.getMaxTextureUnits(); ++index) { - dynamicIfs += '\telse if (vTextureIndex == ' + - index + '.0) gl_FragColor = texture2D(uSamplerArray[' + - index + '], vTextureCoord) * vColor;\n' + dynamicIfs += '\telse if (vTextureIndex == ' + + index + '.0) gl_FragColor = texture2D(uSamplerArray[' + + index + '], vTextureCoord) * vColor;\n'; } this.fragmentSrc = [ @@ -110,7 +110,7 @@ Phaser.Renderer.WebGL.Shaders.SpriteBatch.prototype = { 'const vec4 GREEN = vec4(0.0, 1.0, 0.0, 1.0);', 'void main(void) {', dynamicIfs, - 'else gl_FragColor = PINK;', + 'else gl_FragColor = PINK;', '}' ]; } @@ -126,9 +126,37 @@ Phaser.Renderer.WebGL.Shaders.SpriteBatch.prototype = { ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;', '}' ]; - } + } - this.vertexSrc = [ + this.vertexSrc = [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + 'attribute float aTextureIndex;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'varying float vTextureIndex;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vTextureIndex = aTextureIndex;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ]; + + // ' vec3 color = mod(vec3(aColor.y / 65536.0, aColor.y / 256.0, aColor.y), 256.0) / 256.0;', + // ' vColor = vec4(color * aColor.x, aColor.x);', + + + /* + this.vertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aPositionCoord;', 'attribute vec2 aScale;', @@ -152,13 +180,14 @@ Phaser.Renderer.WebGL.Shaders.SpriteBatch.prototype = { ' vec2 sv = aVertexPosition * aScale;', ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', - ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', - ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', + ' v = (uMatrix * vec3(v + aPositionCoord , 1.0)).xy ;', + ' gl_Position = vec4((v / projectionVector) + center , 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', ' vTextureIndex = aTextureIndex;', ' vColor = aColor;', '}' ]; + */ var program = this.renderer.compileProgram(this.vertexSrc, this.fragmentSrc); diff --git a/src/textures/Frame.js b/src/textures/Frame.js index c09e3ddc4..efc972631 100644 --- a/src/textures/Frame.js +++ b/src/textures/Frame.js @@ -142,6 +142,7 @@ Phaser.TextureFrame = function (texture, name, sourceIndex, x, y, width, height) } }; + this.updateUVs(); }; Phaser.TextureFrame.prototype.constructor = Phaser.TextureFrame; @@ -171,6 +172,8 @@ Phaser.TextureFrame.prototype = { this.cutHeight = Phaser.Math.clamp(height, 0, this.data.cut.h - this.cutY); } + this.updateUVs(); + return this; }, @@ -203,6 +206,8 @@ Phaser.TextureFrame.prototype = { this.width = destWidth; this.height = destHeight; + this.updateUVs(); + return this; }, @@ -214,21 +219,21 @@ Phaser.TextureFrame.prototype = { */ updateUVs: function () { - var tw = this.texture.width; - var th = this.texture.height; + var tw = this.source.width; + var th = this.source.height; var uvs = this.data.uvs; - uvs.x0 = this.x / tw; - uvs.y0 = this.y / th; + uvs.x0 = this.cutX / tw; + uvs.y0 = this.cutY / th; - uvs.x1 = (this.x + this.width) / tw; - uvs.y1 = this.y / th; + uvs.x1 = (this.cutX + this.cutWidth) / tw; + uvs.y1 = this.cutY / th; - uvs.x2 = (this.x + this.width) / tw; - uvs.y2 = (this.y + this.height) / th; + uvs.x2 = (this.cutX + this.cutWidth) / tw; + uvs.y2 = (this.cutY + this.cutHeight) / th; - uvs.x3 = this.x / tw; - uvs.y3 = (this.y + this.height) / th; + uvs.x3 = this.cutX / tw; + uvs.y3 = (this.cutY + this.cutHeight) / th; return this; }, @@ -241,8 +246,8 @@ Phaser.TextureFrame.prototype = { */ updateUVsInverted: function () { - var tw = this.texture.width; - var th = this.texture.height; + var tw = this.source.width; + var th = this.source.height; var uvs = this.data.uvs; uvs.x0 = this.x / tw; @@ -328,6 +333,23 @@ Object.defineProperties(Phaser.TextureFrame.prototype, { return this.data.sourceSize.h; } + }, + + /** + * UVs + * + * @name Phaser.TextureFrame#uvs + * @property {Object} uvs + */ + uvs: { + + enumerable: true, + + get: function () + { + return this.data.uvs; + } + } }); diff --git a/src/textures/Texture.js b/src/textures/Texture.js index 043bb174f..6f5473b79 100644 --- a/src/textures/Texture.js +++ b/src/textures/Texture.js @@ -47,21 +47,11 @@ Phaser.Texture = function (manager, key, source) this.frameTotal = 0; - this.dirty = true; - - /** - * @property _glTextures - * @type Array - * @private - */ - this.glTextures = null; - // Load the Sources for (var i = 0; i < source.length; i++) { this.source.push(new Phaser.TextureSource(this, source[i])); } - }; Phaser.Texture.prototype.constructor = Phaser.Texture; @@ -81,10 +71,21 @@ Phaser.Texture.prototype = { get: function (name) { - if (name === undefined || name === null) { name = '__BASE'; } + if (name === undefined || name === null || this.frameTotal === 1) + { + name = '__BASE'; + } - return this.frames[name]; + var frame = this.frames[name]; + if (!frame) + { + console.warn('No Texture.frame found with name ' + name); + } + else + { + return frame; + } }, /** @@ -94,16 +95,18 @@ Phaser.Texture.prototype = { */ destroy: function () { + + // Need to iterate though the TextureSources, and unload each one + // then clear out the frames + + /* if (this.source) { Phaser.CanvasPool.removeByCanvas(this.source); } this.source = null; - - this.unloadFromGPU(); - - // TODO: Clear out the Frames + */ } }; diff --git a/src/textures/TextureSource.js b/src/textures/TextureSource.js index 85ae6db05..c7af22c41 100644 --- a/src/textures/TextureSource.js +++ b/src/textures/TextureSource.js @@ -17,6 +17,8 @@ Phaser.TextureSource = function (texture, source) this.image = source; + this.compressionAlgorithm = null; + /** * The Resolution of the texture. * @@ -45,7 +47,7 @@ Phaser.TextureSource = function (texture, source) /** * The scale mode to apply when scaling this texture - * + * * @property scaleMode * @type {Number} * @default Phaser.scaleModes.DEFAULT; @@ -64,33 +66,43 @@ Phaser.TextureSource = function (texture, source) /** * Set this to true if a mipmap of this texture needs to be generated. This value needs to be set before the texture is used * Also the texture must be a power of two size to work - * + * * @property mipmap * @type {Boolean} */ this.mipmap = false; - /** - * The multi texture batching index number. - * @property textureIndex - * @type Number - */ - this.textureIndex = 0; - /** * A BaseTexture can be set to skip the rendering phase in the WebGL Sprite Batch. - * + * * You may want to do this if you have a parent Sprite with no visible texture (i.e. uses the internal `__default` texture) * that has children that you do want to render, without causing a batch flush in the process. - * + * * @property renderable * @type Boolean */ - this.renderable = false; + this.renderable = true; /** * @property isPowerOf2 * @type boolean */ this.isPowerOf2 = Phaser.Math.isPowerOfTwo(this.width, this.height); + + /** + * @property glTexture + */ + this.glTexture = null; + + /** + * The multi texture batching index number. + * @property glTextureIndex + * @type Number + */ + this.glTextureIndex = 0; + + /** + * @property glDirty + */ + this.glDirty = true; };