diff --git a/package.json b/package.json index 668d451..1dfeee5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "resource-loader": "^2.0.9" }, "devDependencies": { + "@pixi/jsdoc-template": "^2.0.0", "babel-cli": "^6.18.0", "babel-plugin-static-fs": "^1.1.0", "babel-plugin-version-inline": "^1.0.0", @@ -75,7 +76,6 @@ "electron": "^1.4.15", "eslint": "^3.5.0", "floss": "^2.0.1", - "jaguarjs-jsdoc": "^1.0.1", "js-md5": "^0.4.1", "jsdoc": "3.4.3", "minimist": "^1.2.0", diff --git a/package.json b/package.json index 668d451..1dfeee5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "resource-loader": "^2.0.9" }, "devDependencies": { + "@pixi/jsdoc-template": "^2.0.0", "babel-cli": "^6.18.0", "babel-plugin-static-fs": "^1.1.0", "babel-plugin-version-inline": "^1.0.0", @@ -75,7 +76,6 @@ "electron": "^1.4.15", "eslint": "^3.5.0", "floss": "^2.0.1", - "jaguarjs-jsdoc": "^1.0.1", "js-md5": "^0.4.1", "jsdoc": "3.4.3", "minimist": "^1.2.0", diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json index 8a3afee..2ee888c 100644 --- a/scripts/jsdoc.conf.json +++ b/scripts/jsdoc.conf.json @@ -51,6 +51,6 @@ "private" : false, "lenient" : true, "destination" : "./docs", - "template" : "./node_modules/jaguarjs-jsdoc" + "template" : "./node_modules/@pixi/jsdoc-template" } } diff --git a/package.json b/package.json index 668d451..1dfeee5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "resource-loader": "^2.0.9" }, "devDependencies": { + "@pixi/jsdoc-template": "^2.0.0", "babel-cli": "^6.18.0", "babel-plugin-static-fs": "^1.1.0", "babel-plugin-version-inline": "^1.0.0", @@ -75,7 +76,6 @@ "electron": "^1.4.15", "eslint": "^3.5.0", "floss": "^2.0.1", - "jaguarjs-jsdoc": "^1.0.1", "js-md5": "^0.4.1", "jsdoc": "3.4.3", "minimist": "^1.2.0", diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json index 8a3afee..2ee888c 100644 --- a/scripts/jsdoc.conf.json +++ b/scripts/jsdoc.conf.json @@ -51,6 +51,6 @@ "private" : false, "lenient" : true, "destination" : "./docs", - "template" : "./node_modules/jaguarjs-jsdoc" + "template" : "./node_modules/@pixi/jsdoc-template" } } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index f2d1c12..e63d2b0 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -127,6 +127,19 @@ */ this.currentRenderer = this.emptyRenderer; + /** + * Manages textures + * @member {PIXI.TextureManager} + */ + this.textureManager = null; + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = null; + this.initPlugins(); /** @@ -178,12 +191,6 @@ this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); // map some webGL blend and drawmodes.. this.drawModes = mapWebGLDrawModesToPixi(this.gl); @@ -236,6 +243,7 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.filterManager = new FilterManager(this); this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); @@ -683,6 +691,7 @@ handleContextRestored() { this.textureManager.removeAll(); + this.filterManager.destroy(true); this._initContext(); } diff --git a/package.json b/package.json index 668d451..1dfeee5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "resource-loader": "^2.0.9" }, "devDependencies": { + "@pixi/jsdoc-template": "^2.0.0", "babel-cli": "^6.18.0", "babel-plugin-static-fs": "^1.1.0", "babel-plugin-version-inline": "^1.0.0", @@ -75,7 +76,6 @@ "electron": "^1.4.15", "eslint": "^3.5.0", "floss": "^2.0.1", - "jaguarjs-jsdoc": "^1.0.1", "js-md5": "^0.4.1", "jsdoc": "3.4.3", "minimist": "^1.2.0", diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json index 8a3afee..2ee888c 100644 --- a/scripts/jsdoc.conf.json +++ b/scripts/jsdoc.conf.json @@ -51,6 +51,6 @@ "private" : false, "lenient" : true, "destination" : "./docs", - "template" : "./node_modules/jaguarjs-jsdoc" + "template" : "./node_modules/@pixi/jsdoc-template" } } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index f2d1c12..e63d2b0 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -127,6 +127,19 @@ */ this.currentRenderer = this.emptyRenderer; + /** + * Manages textures + * @member {PIXI.TextureManager} + */ + this.textureManager = null; + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = null; + this.initPlugins(); /** @@ -178,12 +191,6 @@ this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); // map some webGL blend and drawmodes.. this.drawModes = mapWebGLDrawModesToPixi(this.gl); @@ -236,6 +243,7 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.filterManager = new FilterManager(this); this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); @@ -683,6 +691,7 @@ handleContextRestored() { this.textureManager.removeAll(); + this.filterManager.destroy(true); this._initContext(); } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 7ed28ac..da00800 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -50,6 +50,10 @@ for (const i in this.uniformData) { this.uniforms[i] = this.uniformData[i].value; + if (this.uniformData[i].type) + { + this.uniformData[i].type = this.uniformData[i].type.toLowerCase(); + } } // this is where we store shader references.. diff --git a/package.json b/package.json index 668d451..1dfeee5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "resource-loader": "^2.0.9" }, "devDependencies": { + "@pixi/jsdoc-template": "^2.0.0", "babel-cli": "^6.18.0", "babel-plugin-static-fs": "^1.1.0", "babel-plugin-version-inline": "^1.0.0", @@ -75,7 +76,6 @@ "electron": "^1.4.15", "eslint": "^3.5.0", "floss": "^2.0.1", - "jaguarjs-jsdoc": "^1.0.1", "js-md5": "^0.4.1", "jsdoc": "3.4.3", "minimist": "^1.2.0", diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json index 8a3afee..2ee888c 100644 --- a/scripts/jsdoc.conf.json +++ b/scripts/jsdoc.conf.json @@ -51,6 +51,6 @@ "private" : false, "lenient" : true, "destination" : "./docs", - "template" : "./node_modules/jaguarjs-jsdoc" + "template" : "./node_modules/@pixi/jsdoc-template" } } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index f2d1c12..e63d2b0 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -127,6 +127,19 @@ */ this.currentRenderer = this.emptyRenderer; + /** + * Manages textures + * @member {PIXI.TextureManager} + */ + this.textureManager = null; + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = null; + this.initPlugins(); /** @@ -178,12 +191,6 @@ this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); // map some webGL blend and drawmodes.. this.drawModes = mapWebGLDrawModesToPixi(this.gl); @@ -236,6 +243,7 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.filterManager = new FilterManager(this); this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); @@ -683,6 +691,7 @@ handleContextRestored() { this.textureManager.removeAll(); + this.filterManager.destroy(true); this._initContext(); } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 7ed28ac..da00800 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -50,6 +50,10 @@ for (const i in this.uniformData) { this.uniforms[i] = this.uniformData[i].value; + if (this.uniformData[i].type) + { + this.uniformData[i].type = this.uniformData[i].type.toLowerCase(); + } } // this is where we store shader references.. diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index 851ad69..f955a9d 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,36 +40,13 @@ // this will map the filter coord so that a texture can be used based on the transform of a sprite export function calculateSpriteMatrix(outputMatrix, filterArea, textureSize, sprite) { - const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); const texture = sprite._texture.baseTexture; - - // TODO unwrap? - const mappedMatrix = outputMatrix.identity(); - - // scale.. - const ratio = textureSize.height / textureSize.width; - - mappedMatrix.translate(filterArea.x / textureSize.width, filterArea.y / textureSize.height); - - mappedMatrix.scale(1, ratio); - - const translateScaleX = (textureSize.width / texture.width); - const translateScaleY = (textureSize.height / texture.height); - - worldTransform.tx /= texture.width * translateScaleX; - - // this...? free beer for anyone who can explain why this makes sense! - worldTransform.ty /= texture.width * translateScaleX; - // worldTransform.ty /= texture.height * translateScaleY; + const mappedMatrix = outputMatrix.set(textureSize.width, 0, 0, textureSize.height, filterArea.x, filterArea.y); + const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); worldTransform.invert(); mappedMatrix.prepend(worldTransform); - - // apply inverse scale.. - mappedMatrix.scale(1, 1 / ratio); - - mappedMatrix.scale(translateScaleX, translateScaleY); - + mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/package.json b/package.json index 668d451..1dfeee5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "resource-loader": "^2.0.9" }, "devDependencies": { + "@pixi/jsdoc-template": "^2.0.0", "babel-cli": "^6.18.0", "babel-plugin-static-fs": "^1.1.0", "babel-plugin-version-inline": "^1.0.0", @@ -75,7 +76,6 @@ "electron": "^1.4.15", "eslint": "^3.5.0", "floss": "^2.0.1", - "jaguarjs-jsdoc": "^1.0.1", "js-md5": "^0.4.1", "jsdoc": "3.4.3", "minimist": "^1.2.0", diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json index 8a3afee..2ee888c 100644 --- a/scripts/jsdoc.conf.json +++ b/scripts/jsdoc.conf.json @@ -51,6 +51,6 @@ "private" : false, "lenient" : true, "destination" : "./docs", - "template" : "./node_modules/jaguarjs-jsdoc" + "template" : "./node_modules/@pixi/jsdoc-template" } } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index f2d1c12..e63d2b0 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -127,6 +127,19 @@ */ this.currentRenderer = this.emptyRenderer; + /** + * Manages textures + * @member {PIXI.TextureManager} + */ + this.textureManager = null; + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = null; + this.initPlugins(); /** @@ -178,12 +191,6 @@ this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); // map some webGL blend and drawmodes.. this.drawModes = mapWebGLDrawModesToPixi(this.gl); @@ -236,6 +243,7 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.filterManager = new FilterManager(this); this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); @@ -683,6 +691,7 @@ handleContextRestored() { this.textureManager.removeAll(); + this.filterManager.destroy(true); this._initContext(); } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 7ed28ac..da00800 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -50,6 +50,10 @@ for (const i in this.uniformData) { this.uniforms[i] = this.uniformData[i].value; + if (this.uniformData[i].type) + { + this.uniformData[i].type = this.uniformData[i].type.toLowerCase(); + } } // this is where we store shader references.. diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index 851ad69..f955a9d 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,36 +40,13 @@ // this will map the filter coord so that a texture can be used based on the transform of a sprite export function calculateSpriteMatrix(outputMatrix, filterArea, textureSize, sprite) { - const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); const texture = sprite._texture.baseTexture; - - // TODO unwrap? - const mappedMatrix = outputMatrix.identity(); - - // scale.. - const ratio = textureSize.height / textureSize.width; - - mappedMatrix.translate(filterArea.x / textureSize.width, filterArea.y / textureSize.height); - - mappedMatrix.scale(1, ratio); - - const translateScaleX = (textureSize.width / texture.width); - const translateScaleY = (textureSize.height / texture.height); - - worldTransform.tx /= texture.width * translateScaleX; - - // this...? free beer for anyone who can explain why this makes sense! - worldTransform.ty /= texture.width * translateScaleX; - // worldTransform.ty /= texture.height * translateScaleY; + const mappedMatrix = outputMatrix.set(textureSize.width, 0, 0, textureSize.height, filterArea.x, filterArea.y); + const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); worldTransform.invert(); mappedMatrix.prepend(worldTransform); - - // apply inverse scale.. - mappedMatrix.scale(1, 1 / ratio); - - mappedMatrix.scale(translateScaleX, translateScaleY); - + mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index fdaa5c1..53f2ae1 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -49,6 +49,8 @@ this.pool = {}; this.filterData = null; + + this.managedFilters = []; } /** @@ -232,6 +234,8 @@ shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); } + this.managedFilters.push(filter); + // TODO - this only needs to be done once? renderer.bindVao(null); @@ -327,7 +331,9 @@ // TODO Cacheing layer.. for (const i in uniformData) { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + const type = uniformData[i].type; + + if (type === 'sampler2d' && uniforms[i] !== 0) { if (uniforms[i].baseTexture) { @@ -352,7 +358,7 @@ textureCount++; } - else if (uniformData[i].type === 'mat3') + else if (type === 'mat3') { // check if its PixiJS matrix.. if (uniforms[i].a !== undefined) @@ -364,7 +370,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'vec2') + else if (type === 'vec2') { // check if its a point.. if (uniforms[i].x !== undefined) @@ -380,7 +386,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'float') + else if (type === 'float') { if (shader.uniforms.data[i].value !== uniformData[i]) { @@ -486,11 +492,32 @@ /** * Destroys this Filter Manager. * + * @param {boolean} [contextLost=false] context was lost, do not free shaders + * */ - destroy() + destroy(contextLost = false) { + const renderer = this.renderer; + const filters = this.managedFilters; + + for (let i = 0; i < filters.length; i++) + { + if (!contextLost) + { + filters[i].glShaders[renderer.CONTEXT_UID].destroy(); + } + delete filters[i].glShaders[renderer.CONTEXT_UID]; + } + this.shaderCache = {}; - this.emptyPool(); + if (!contextLost) + { + this.emptyPool(); + } + else + { + this.pool = {}; + } } /** diff --git a/package.json b/package.json index 668d451..1dfeee5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "resource-loader": "^2.0.9" }, "devDependencies": { + "@pixi/jsdoc-template": "^2.0.0", "babel-cli": "^6.18.0", "babel-plugin-static-fs": "^1.1.0", "babel-plugin-version-inline": "^1.0.0", @@ -75,7 +76,6 @@ "electron": "^1.4.15", "eslint": "^3.5.0", "floss": "^2.0.1", - "jaguarjs-jsdoc": "^1.0.1", "js-md5": "^0.4.1", "jsdoc": "3.4.3", "minimist": "^1.2.0", diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json index 8a3afee..2ee888c 100644 --- a/scripts/jsdoc.conf.json +++ b/scripts/jsdoc.conf.json @@ -51,6 +51,6 @@ "private" : false, "lenient" : true, "destination" : "./docs", - "template" : "./node_modules/jaguarjs-jsdoc" + "template" : "./node_modules/@pixi/jsdoc-template" } } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index f2d1c12..e63d2b0 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -127,6 +127,19 @@ */ this.currentRenderer = this.emptyRenderer; + /** + * Manages textures + * @member {PIXI.TextureManager} + */ + this.textureManager = null; + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = null; + this.initPlugins(); /** @@ -178,12 +191,6 @@ this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); // map some webGL blend and drawmodes.. this.drawModes = mapWebGLDrawModesToPixi(this.gl); @@ -236,6 +243,7 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.filterManager = new FilterManager(this); this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); @@ -683,6 +691,7 @@ handleContextRestored() { this.textureManager.removeAll(); + this.filterManager.destroy(true); this._initContext(); } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 7ed28ac..da00800 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -50,6 +50,10 @@ for (const i in this.uniformData) { this.uniforms[i] = this.uniformData[i].value; + if (this.uniformData[i].type) + { + this.uniformData[i].type = this.uniformData[i].type.toLowerCase(); + } } // this is where we store shader references.. diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index 851ad69..f955a9d 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,36 +40,13 @@ // this will map the filter coord so that a texture can be used based on the transform of a sprite export function calculateSpriteMatrix(outputMatrix, filterArea, textureSize, sprite) { - const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); const texture = sprite._texture.baseTexture; - - // TODO unwrap? - const mappedMatrix = outputMatrix.identity(); - - // scale.. - const ratio = textureSize.height / textureSize.width; - - mappedMatrix.translate(filterArea.x / textureSize.width, filterArea.y / textureSize.height); - - mappedMatrix.scale(1, ratio); - - const translateScaleX = (textureSize.width / texture.width); - const translateScaleY = (textureSize.height / texture.height); - - worldTransform.tx /= texture.width * translateScaleX; - - // this...? free beer for anyone who can explain why this makes sense! - worldTransform.ty /= texture.width * translateScaleX; - // worldTransform.ty /= texture.height * translateScaleY; + const mappedMatrix = outputMatrix.set(textureSize.width, 0, 0, textureSize.height, filterArea.x, filterArea.y); + const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); worldTransform.invert(); mappedMatrix.prepend(worldTransform); - - // apply inverse scale.. - mappedMatrix.scale(1, 1 / ratio); - - mappedMatrix.scale(translateScaleX, translateScaleY); - + mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index fdaa5c1..53f2ae1 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -49,6 +49,8 @@ this.pool = {}; this.filterData = null; + + this.managedFilters = []; } /** @@ -232,6 +234,8 @@ shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); } + this.managedFilters.push(filter); + // TODO - this only needs to be done once? renderer.bindVao(null); @@ -327,7 +331,9 @@ // TODO Cacheing layer.. for (const i in uniformData) { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + const type = uniformData[i].type; + + if (type === 'sampler2d' && uniforms[i] !== 0) { if (uniforms[i].baseTexture) { @@ -352,7 +358,7 @@ textureCount++; } - else if (uniformData[i].type === 'mat3') + else if (type === 'mat3') { // check if its PixiJS matrix.. if (uniforms[i].a !== undefined) @@ -364,7 +370,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'vec2') + else if (type === 'vec2') { // check if its a point.. if (uniforms[i].x !== undefined) @@ -380,7 +386,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'float') + else if (type === 'float') { if (shader.uniforms.data[i].value !== uniformData[i]) { @@ -486,11 +492,32 @@ /** * Destroys this Filter Manager. * + * @param {boolean} [contextLost=false] context was lost, do not free shaders + * */ - destroy() + destroy(contextLost = false) { + const renderer = this.renderer; + const filters = this.managedFilters; + + for (let i = 0; i < filters.length; i++) + { + if (!contextLost) + { + filters[i].glShaders[renderer.CONTEXT_UID].destroy(); + } + delete filters[i].glShaders[renderer.CONTEXT_UID]; + } + this.shaderCache = {}; - this.emptyPool(); + if (!contextLost) + { + this.emptyPool(); + } + else + { + this.pool = {}; + } } /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 0aeda37..dad38fb 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -143,6 +143,7 @@ { this._textureID = -1; this._textureTrimmedID = -1; + this.cachedTint = 0xFFFFFF; // so if _width is 0 then width was not set.. if (this._width) diff --git a/package.json b/package.json index 668d451..1dfeee5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "resource-loader": "^2.0.9" }, "devDependencies": { + "@pixi/jsdoc-template": "^2.0.0", "babel-cli": "^6.18.0", "babel-plugin-static-fs": "^1.1.0", "babel-plugin-version-inline": "^1.0.0", @@ -75,7 +76,6 @@ "electron": "^1.4.15", "eslint": "^3.5.0", "floss": "^2.0.1", - "jaguarjs-jsdoc": "^1.0.1", "js-md5": "^0.4.1", "jsdoc": "3.4.3", "minimist": "^1.2.0", diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json index 8a3afee..2ee888c 100644 --- a/scripts/jsdoc.conf.json +++ b/scripts/jsdoc.conf.json @@ -51,6 +51,6 @@ "private" : false, "lenient" : true, "destination" : "./docs", - "template" : "./node_modules/jaguarjs-jsdoc" + "template" : "./node_modules/@pixi/jsdoc-template" } } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index f2d1c12..e63d2b0 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -127,6 +127,19 @@ */ this.currentRenderer = this.emptyRenderer; + /** + * Manages textures + * @member {PIXI.TextureManager} + */ + this.textureManager = null; + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = null; + this.initPlugins(); /** @@ -178,12 +191,6 @@ this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); // map some webGL blend and drawmodes.. this.drawModes = mapWebGLDrawModesToPixi(this.gl); @@ -236,6 +243,7 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.filterManager = new FilterManager(this); this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); @@ -683,6 +691,7 @@ handleContextRestored() { this.textureManager.removeAll(); + this.filterManager.destroy(true); this._initContext(); } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 7ed28ac..da00800 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -50,6 +50,10 @@ for (const i in this.uniformData) { this.uniforms[i] = this.uniformData[i].value; + if (this.uniformData[i].type) + { + this.uniformData[i].type = this.uniformData[i].type.toLowerCase(); + } } // this is where we store shader references.. diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index 851ad69..f955a9d 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,36 +40,13 @@ // this will map the filter coord so that a texture can be used based on the transform of a sprite export function calculateSpriteMatrix(outputMatrix, filterArea, textureSize, sprite) { - const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); const texture = sprite._texture.baseTexture; - - // TODO unwrap? - const mappedMatrix = outputMatrix.identity(); - - // scale.. - const ratio = textureSize.height / textureSize.width; - - mappedMatrix.translate(filterArea.x / textureSize.width, filterArea.y / textureSize.height); - - mappedMatrix.scale(1, ratio); - - const translateScaleX = (textureSize.width / texture.width); - const translateScaleY = (textureSize.height / texture.height); - - worldTransform.tx /= texture.width * translateScaleX; - - // this...? free beer for anyone who can explain why this makes sense! - worldTransform.ty /= texture.width * translateScaleX; - // worldTransform.ty /= texture.height * translateScaleY; + const mappedMatrix = outputMatrix.set(textureSize.width, 0, 0, textureSize.height, filterArea.x, filterArea.y); + const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); worldTransform.invert(); mappedMatrix.prepend(worldTransform); - - // apply inverse scale.. - mappedMatrix.scale(1, 1 / ratio); - - mappedMatrix.scale(translateScaleX, translateScaleY); - + mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index fdaa5c1..53f2ae1 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -49,6 +49,8 @@ this.pool = {}; this.filterData = null; + + this.managedFilters = []; } /** @@ -232,6 +234,8 @@ shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); } + this.managedFilters.push(filter); + // TODO - this only needs to be done once? renderer.bindVao(null); @@ -327,7 +331,9 @@ // TODO Cacheing layer.. for (const i in uniformData) { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + const type = uniformData[i].type; + + if (type === 'sampler2d' && uniforms[i] !== 0) { if (uniforms[i].baseTexture) { @@ -352,7 +358,7 @@ textureCount++; } - else if (uniformData[i].type === 'mat3') + else if (type === 'mat3') { // check if its PixiJS matrix.. if (uniforms[i].a !== undefined) @@ -364,7 +370,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'vec2') + else if (type === 'vec2') { // check if its a point.. if (uniforms[i].x !== undefined) @@ -380,7 +386,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'float') + else if (type === 'float') { if (shader.uniforms.data[i].value !== uniformData[i]) { @@ -486,11 +492,32 @@ /** * Destroys this Filter Manager. * + * @param {boolean} [contextLost=false] context was lost, do not free shaders + * */ - destroy() + destroy(contextLost = false) { + const renderer = this.renderer; + const filters = this.managedFilters; + + for (let i = 0; i < filters.length; i++) + { + if (!contextLost) + { + filters[i].glShaders[renderer.CONTEXT_UID].destroy(); + } + delete filters[i].glShaders[renderer.CONTEXT_UID]; + } + this.shaderCache = {}; - this.emptyPool(); + if (!contextLost) + { + this.emptyPool(); + } + else + { + this.pool = {}; + } } /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 0aeda37..dad38fb 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -143,6 +143,7 @@ { this._textureID = -1; this._textureTrimmedID = -1; + this.cachedTint = 0xFFFFFF; // so if _width is 0 then width was not set.. if (this._width) diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 85abccf..1a9223f 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -151,6 +151,7 @@ { let frameIndex = initialFrameIndex; const maxFrames = Spritesheet.BATCH_SIZE; + const sourceScale = this.baseTexture.sourceScale; while (frameIndex - initialFrameIndex < maxFrames && frameIndex < this._frameKeys.length) { @@ -164,26 +165,26 @@ const orig = new Rectangle( 0, 0, - this._frames[i].sourceSize.w / this.resolution, - this._frames[i].sourceSize.h / this.resolution + Math.floor(this._frames[i].sourceSize.w * sourceScale) / this.resolution, + Math.floor(this._frames[i].sourceSize.h * sourceScale) / this.resolution ); if (this._frames[i].rotated) { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.h / this.resolution, - rect.w / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution ); } else { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } @@ -191,10 +192,10 @@ if (this._frames[i].trimmed) { trim = new Rectangle( - this._frames[i].spriteSourceSize.x / this.resolution, - this._frames[i].spriteSourceSize.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(this._frames[i].spriteSourceSize.x * sourceScale) / this.resolution, + Math.floor(this._frames[i].spriteSourceSize.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } diff --git a/package.json b/package.json index 668d451..1dfeee5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "resource-loader": "^2.0.9" }, "devDependencies": { + "@pixi/jsdoc-template": "^2.0.0", "babel-cli": "^6.18.0", "babel-plugin-static-fs": "^1.1.0", "babel-plugin-version-inline": "^1.0.0", @@ -75,7 +76,6 @@ "electron": "^1.4.15", "eslint": "^3.5.0", "floss": "^2.0.1", - "jaguarjs-jsdoc": "^1.0.1", "js-md5": "^0.4.1", "jsdoc": "3.4.3", "minimist": "^1.2.0", diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json index 8a3afee..2ee888c 100644 --- a/scripts/jsdoc.conf.json +++ b/scripts/jsdoc.conf.json @@ -51,6 +51,6 @@ "private" : false, "lenient" : true, "destination" : "./docs", - "template" : "./node_modules/jaguarjs-jsdoc" + "template" : "./node_modules/@pixi/jsdoc-template" } } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index f2d1c12..e63d2b0 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -127,6 +127,19 @@ */ this.currentRenderer = this.emptyRenderer; + /** + * Manages textures + * @member {PIXI.TextureManager} + */ + this.textureManager = null; + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = null; + this.initPlugins(); /** @@ -178,12 +191,6 @@ this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); // map some webGL blend and drawmodes.. this.drawModes = mapWebGLDrawModesToPixi(this.gl); @@ -236,6 +243,7 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.filterManager = new FilterManager(this); this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); @@ -683,6 +691,7 @@ handleContextRestored() { this.textureManager.removeAll(); + this.filterManager.destroy(true); this._initContext(); } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 7ed28ac..da00800 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -50,6 +50,10 @@ for (const i in this.uniformData) { this.uniforms[i] = this.uniformData[i].value; + if (this.uniformData[i].type) + { + this.uniformData[i].type = this.uniformData[i].type.toLowerCase(); + } } // this is where we store shader references.. diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index 851ad69..f955a9d 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,36 +40,13 @@ // this will map the filter coord so that a texture can be used based on the transform of a sprite export function calculateSpriteMatrix(outputMatrix, filterArea, textureSize, sprite) { - const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); const texture = sprite._texture.baseTexture; - - // TODO unwrap? - const mappedMatrix = outputMatrix.identity(); - - // scale.. - const ratio = textureSize.height / textureSize.width; - - mappedMatrix.translate(filterArea.x / textureSize.width, filterArea.y / textureSize.height); - - mappedMatrix.scale(1, ratio); - - const translateScaleX = (textureSize.width / texture.width); - const translateScaleY = (textureSize.height / texture.height); - - worldTransform.tx /= texture.width * translateScaleX; - - // this...? free beer for anyone who can explain why this makes sense! - worldTransform.ty /= texture.width * translateScaleX; - // worldTransform.ty /= texture.height * translateScaleY; + const mappedMatrix = outputMatrix.set(textureSize.width, 0, 0, textureSize.height, filterArea.x, filterArea.y); + const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); worldTransform.invert(); mappedMatrix.prepend(worldTransform); - - // apply inverse scale.. - mappedMatrix.scale(1, 1 / ratio); - - mappedMatrix.scale(translateScaleX, translateScaleY); - + mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index fdaa5c1..53f2ae1 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -49,6 +49,8 @@ this.pool = {}; this.filterData = null; + + this.managedFilters = []; } /** @@ -232,6 +234,8 @@ shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); } + this.managedFilters.push(filter); + // TODO - this only needs to be done once? renderer.bindVao(null); @@ -327,7 +331,9 @@ // TODO Cacheing layer.. for (const i in uniformData) { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + const type = uniformData[i].type; + + if (type === 'sampler2d' && uniforms[i] !== 0) { if (uniforms[i].baseTexture) { @@ -352,7 +358,7 @@ textureCount++; } - else if (uniformData[i].type === 'mat3') + else if (type === 'mat3') { // check if its PixiJS matrix.. if (uniforms[i].a !== undefined) @@ -364,7 +370,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'vec2') + else if (type === 'vec2') { // check if its a point.. if (uniforms[i].x !== undefined) @@ -380,7 +386,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'float') + else if (type === 'float') { if (shader.uniforms.data[i].value !== uniformData[i]) { @@ -486,11 +492,32 @@ /** * Destroys this Filter Manager. * + * @param {boolean} [contextLost=false] context was lost, do not free shaders + * */ - destroy() + destroy(contextLost = false) { + const renderer = this.renderer; + const filters = this.managedFilters; + + for (let i = 0; i < filters.length; i++) + { + if (!contextLost) + { + filters[i].glShaders[renderer.CONTEXT_UID].destroy(); + } + delete filters[i].glShaders[renderer.CONTEXT_UID]; + } + this.shaderCache = {}; - this.emptyPool(); + if (!contextLost) + { + this.emptyPool(); + } + else + { + this.pool = {}; + } } /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 0aeda37..dad38fb 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -143,6 +143,7 @@ { this._textureID = -1; this._textureTrimmedID = -1; + this.cachedTint = 0xFFFFFF; // so if _width is 0 then width was not set.. if (this._width) diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 85abccf..1a9223f 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -151,6 +151,7 @@ { let frameIndex = initialFrameIndex; const maxFrames = Spritesheet.BATCH_SIZE; + const sourceScale = this.baseTexture.sourceScale; while (frameIndex - initialFrameIndex < maxFrames && frameIndex < this._frameKeys.length) { @@ -164,26 +165,26 @@ const orig = new Rectangle( 0, 0, - this._frames[i].sourceSize.w / this.resolution, - this._frames[i].sourceSize.h / this.resolution + Math.floor(this._frames[i].sourceSize.w * sourceScale) / this.resolution, + Math.floor(this._frames[i].sourceSize.h * sourceScale) / this.resolution ); if (this._frames[i].rotated) { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.h / this.resolution, - rect.w / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution ); } else { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } @@ -191,10 +192,10 @@ if (this._frames[i].trimmed) { trim = new Rectangle( - this._frames[i].spriteSourceSize.x / this.resolution, - this._frames[i].spriteSourceSize.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(this._frames[i].spriteSourceSize.x * sourceScale) / this.resolution, + Math.floor(this._frames[i].spriteSourceSize.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 3a472be..75eb069 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -552,15 +552,22 @@ this.noFrame = false; - if (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height) + const { x, y, width, height } = frame; + const xNotFit = x + width > this.baseTexture.width; + const yNotFit = y + height > this.baseTexture.height; + + if (xNotFit || yNotFit) { + const relationship = xNotFit && yNotFit ? 'and' : 'or'; + const errorX = `X: ${x} + ${width} = ${x + width} > ${this.baseTexture.width}`; + const errorY = `Y: ${y} + ${height} = ${y + height} > ${this.baseTexture.height}`; + throw new Error('Texture Error: frame does not fit inside the base Texture dimensions: ' - + `X: ${frame.x} + ${frame.width} = ${frame.x + frame.width} > ${this.baseTexture.width} ` - + `Y: ${frame.y} + ${frame.height} = ${frame.y + frame.height} > ${this.baseTexture.height}`); + + `${errorX} ${relationship} ${errorY}`); } - // this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded; - this.valid = frame && frame.width && frame.height && this.baseTexture.hasLoaded; + // this.valid = width && height && this.baseTexture.source && this.baseTexture.hasLoaded; + this.valid = width && height && this.baseTexture.hasLoaded; if (!this.trim && !this.rotate) { diff --git a/package.json b/package.json index 668d451..1dfeee5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "resource-loader": "^2.0.9" }, "devDependencies": { + "@pixi/jsdoc-template": "^2.0.0", "babel-cli": "^6.18.0", "babel-plugin-static-fs": "^1.1.0", "babel-plugin-version-inline": "^1.0.0", @@ -75,7 +76,6 @@ "electron": "^1.4.15", "eslint": "^3.5.0", "floss": "^2.0.1", - "jaguarjs-jsdoc": "^1.0.1", "js-md5": "^0.4.1", "jsdoc": "3.4.3", "minimist": "^1.2.0", diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json index 8a3afee..2ee888c 100644 --- a/scripts/jsdoc.conf.json +++ b/scripts/jsdoc.conf.json @@ -51,6 +51,6 @@ "private" : false, "lenient" : true, "destination" : "./docs", - "template" : "./node_modules/jaguarjs-jsdoc" + "template" : "./node_modules/@pixi/jsdoc-template" } } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index f2d1c12..e63d2b0 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -127,6 +127,19 @@ */ this.currentRenderer = this.emptyRenderer; + /** + * Manages textures + * @member {PIXI.TextureManager} + */ + this.textureManager = null; + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = null; + this.initPlugins(); /** @@ -178,12 +191,6 @@ this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); // map some webGL blend and drawmodes.. this.drawModes = mapWebGLDrawModesToPixi(this.gl); @@ -236,6 +243,7 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.filterManager = new FilterManager(this); this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); @@ -683,6 +691,7 @@ handleContextRestored() { this.textureManager.removeAll(); + this.filterManager.destroy(true); this._initContext(); } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 7ed28ac..da00800 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -50,6 +50,10 @@ for (const i in this.uniformData) { this.uniforms[i] = this.uniformData[i].value; + if (this.uniformData[i].type) + { + this.uniformData[i].type = this.uniformData[i].type.toLowerCase(); + } } // this is where we store shader references.. diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index 851ad69..f955a9d 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,36 +40,13 @@ // this will map the filter coord so that a texture can be used based on the transform of a sprite export function calculateSpriteMatrix(outputMatrix, filterArea, textureSize, sprite) { - const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); const texture = sprite._texture.baseTexture; - - // TODO unwrap? - const mappedMatrix = outputMatrix.identity(); - - // scale.. - const ratio = textureSize.height / textureSize.width; - - mappedMatrix.translate(filterArea.x / textureSize.width, filterArea.y / textureSize.height); - - mappedMatrix.scale(1, ratio); - - const translateScaleX = (textureSize.width / texture.width); - const translateScaleY = (textureSize.height / texture.height); - - worldTransform.tx /= texture.width * translateScaleX; - - // this...? free beer for anyone who can explain why this makes sense! - worldTransform.ty /= texture.width * translateScaleX; - // worldTransform.ty /= texture.height * translateScaleY; + const mappedMatrix = outputMatrix.set(textureSize.width, 0, 0, textureSize.height, filterArea.x, filterArea.y); + const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); worldTransform.invert(); mappedMatrix.prepend(worldTransform); - - // apply inverse scale.. - mappedMatrix.scale(1, 1 / ratio); - - mappedMatrix.scale(translateScaleX, translateScaleY); - + mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index fdaa5c1..53f2ae1 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -49,6 +49,8 @@ this.pool = {}; this.filterData = null; + + this.managedFilters = []; } /** @@ -232,6 +234,8 @@ shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); } + this.managedFilters.push(filter); + // TODO - this only needs to be done once? renderer.bindVao(null); @@ -327,7 +331,9 @@ // TODO Cacheing layer.. for (const i in uniformData) { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + const type = uniformData[i].type; + + if (type === 'sampler2d' && uniforms[i] !== 0) { if (uniforms[i].baseTexture) { @@ -352,7 +358,7 @@ textureCount++; } - else if (uniformData[i].type === 'mat3') + else if (type === 'mat3') { // check if its PixiJS matrix.. if (uniforms[i].a !== undefined) @@ -364,7 +370,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'vec2') + else if (type === 'vec2') { // check if its a point.. if (uniforms[i].x !== undefined) @@ -380,7 +386,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'float') + else if (type === 'float') { if (shader.uniforms.data[i].value !== uniformData[i]) { @@ -486,11 +492,32 @@ /** * Destroys this Filter Manager. * + * @param {boolean} [contextLost=false] context was lost, do not free shaders + * */ - destroy() + destroy(contextLost = false) { + const renderer = this.renderer; + const filters = this.managedFilters; + + for (let i = 0; i < filters.length; i++) + { + if (!contextLost) + { + filters[i].glShaders[renderer.CONTEXT_UID].destroy(); + } + delete filters[i].glShaders[renderer.CONTEXT_UID]; + } + this.shaderCache = {}; - this.emptyPool(); + if (!contextLost) + { + this.emptyPool(); + } + else + { + this.pool = {}; + } } /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 0aeda37..dad38fb 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -143,6 +143,7 @@ { this._textureID = -1; this._textureTrimmedID = -1; + this.cachedTint = 0xFFFFFF; // so if _width is 0 then width was not set.. if (this._width) diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 85abccf..1a9223f 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -151,6 +151,7 @@ { let frameIndex = initialFrameIndex; const maxFrames = Spritesheet.BATCH_SIZE; + const sourceScale = this.baseTexture.sourceScale; while (frameIndex - initialFrameIndex < maxFrames && frameIndex < this._frameKeys.length) { @@ -164,26 +165,26 @@ const orig = new Rectangle( 0, 0, - this._frames[i].sourceSize.w / this.resolution, - this._frames[i].sourceSize.h / this.resolution + Math.floor(this._frames[i].sourceSize.w * sourceScale) / this.resolution, + Math.floor(this._frames[i].sourceSize.h * sourceScale) / this.resolution ); if (this._frames[i].rotated) { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.h / this.resolution, - rect.w / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution ); } else { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } @@ -191,10 +192,10 @@ if (this._frames[i].trimmed) { trim = new Rectangle( - this._frames[i].spriteSourceSize.x / this.resolution, - this._frames[i].spriteSourceSize.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(this._frames[i].spriteSourceSize.x * sourceScale) / this.resolution, + Math.floor(this._frames[i].spriteSourceSize.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 3a472be..75eb069 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -552,15 +552,22 @@ this.noFrame = false; - if (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height) + const { x, y, width, height } = frame; + const xNotFit = x + width > this.baseTexture.width; + const yNotFit = y + height > this.baseTexture.height; + + if (xNotFit || yNotFit) { + const relationship = xNotFit && yNotFit ? 'and' : 'or'; + const errorX = `X: ${x} + ${width} = ${x + width} > ${this.baseTexture.width}`; + const errorY = `Y: ${y} + ${height} = ${y + height} > ${this.baseTexture.height}`; + throw new Error('Texture Error: frame does not fit inside the base Texture dimensions: ' - + `X: ${frame.x} + ${frame.width} = ${frame.x + frame.width} > ${this.baseTexture.width} ` - + `Y: ${frame.y} + ${frame.height} = ${frame.y + frame.height} > ${this.baseTexture.height}`); + + `${errorX} ${relationship} ${errorY}`); } - // this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded; - this.valid = frame && frame.width && frame.height && this.baseTexture.hasLoaded; + // this.valid = width && height && this.baseTexture.source && this.baseTexture.hasLoaded; + this.valid = width && height && this.baseTexture.hasLoaded; if (!this.trim && !this.rotate) { diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index 1754e0e..c605fa8 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -271,6 +271,7 @@ { this._texture = this._textures[this.currentFrame]; this._textureID = -1; + this.cachedTint = 0xFFFFFF; if (this.onFrameChange) { diff --git a/package.json b/package.json index 668d451..1dfeee5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "resource-loader": "^2.0.9" }, "devDependencies": { + "@pixi/jsdoc-template": "^2.0.0", "babel-cli": "^6.18.0", "babel-plugin-static-fs": "^1.1.0", "babel-plugin-version-inline": "^1.0.0", @@ -75,7 +76,6 @@ "electron": "^1.4.15", "eslint": "^3.5.0", "floss": "^2.0.1", - "jaguarjs-jsdoc": "^1.0.1", "js-md5": "^0.4.1", "jsdoc": "3.4.3", "minimist": "^1.2.0", diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json index 8a3afee..2ee888c 100644 --- a/scripts/jsdoc.conf.json +++ b/scripts/jsdoc.conf.json @@ -51,6 +51,6 @@ "private" : false, "lenient" : true, "destination" : "./docs", - "template" : "./node_modules/jaguarjs-jsdoc" + "template" : "./node_modules/@pixi/jsdoc-template" } } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index f2d1c12..e63d2b0 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -127,6 +127,19 @@ */ this.currentRenderer = this.emptyRenderer; + /** + * Manages textures + * @member {PIXI.TextureManager} + */ + this.textureManager = null; + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = null; + this.initPlugins(); /** @@ -178,12 +191,6 @@ this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); // map some webGL blend and drawmodes.. this.drawModes = mapWebGLDrawModesToPixi(this.gl); @@ -236,6 +243,7 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.filterManager = new FilterManager(this); this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); @@ -683,6 +691,7 @@ handleContextRestored() { this.textureManager.removeAll(); + this.filterManager.destroy(true); this._initContext(); } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 7ed28ac..da00800 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -50,6 +50,10 @@ for (const i in this.uniformData) { this.uniforms[i] = this.uniformData[i].value; + if (this.uniformData[i].type) + { + this.uniformData[i].type = this.uniformData[i].type.toLowerCase(); + } } // this is where we store shader references.. diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index 851ad69..f955a9d 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,36 +40,13 @@ // this will map the filter coord so that a texture can be used based on the transform of a sprite export function calculateSpriteMatrix(outputMatrix, filterArea, textureSize, sprite) { - const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); const texture = sprite._texture.baseTexture; - - // TODO unwrap? - const mappedMatrix = outputMatrix.identity(); - - // scale.. - const ratio = textureSize.height / textureSize.width; - - mappedMatrix.translate(filterArea.x / textureSize.width, filterArea.y / textureSize.height); - - mappedMatrix.scale(1, ratio); - - const translateScaleX = (textureSize.width / texture.width); - const translateScaleY = (textureSize.height / texture.height); - - worldTransform.tx /= texture.width * translateScaleX; - - // this...? free beer for anyone who can explain why this makes sense! - worldTransform.ty /= texture.width * translateScaleX; - // worldTransform.ty /= texture.height * translateScaleY; + const mappedMatrix = outputMatrix.set(textureSize.width, 0, 0, textureSize.height, filterArea.x, filterArea.y); + const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); worldTransform.invert(); mappedMatrix.prepend(worldTransform); - - // apply inverse scale.. - mappedMatrix.scale(1, 1 / ratio); - - mappedMatrix.scale(translateScaleX, translateScaleY); - + mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index fdaa5c1..53f2ae1 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -49,6 +49,8 @@ this.pool = {}; this.filterData = null; + + this.managedFilters = []; } /** @@ -232,6 +234,8 @@ shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); } + this.managedFilters.push(filter); + // TODO - this only needs to be done once? renderer.bindVao(null); @@ -327,7 +331,9 @@ // TODO Cacheing layer.. for (const i in uniformData) { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + const type = uniformData[i].type; + + if (type === 'sampler2d' && uniforms[i] !== 0) { if (uniforms[i].baseTexture) { @@ -352,7 +358,7 @@ textureCount++; } - else if (uniformData[i].type === 'mat3') + else if (type === 'mat3') { // check if its PixiJS matrix.. if (uniforms[i].a !== undefined) @@ -364,7 +370,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'vec2') + else if (type === 'vec2') { // check if its a point.. if (uniforms[i].x !== undefined) @@ -380,7 +386,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'float') + else if (type === 'float') { if (shader.uniforms.data[i].value !== uniformData[i]) { @@ -486,11 +492,32 @@ /** * Destroys this Filter Manager. * + * @param {boolean} [contextLost=false] context was lost, do not free shaders + * */ - destroy() + destroy(contextLost = false) { + const renderer = this.renderer; + const filters = this.managedFilters; + + for (let i = 0; i < filters.length; i++) + { + if (!contextLost) + { + filters[i].glShaders[renderer.CONTEXT_UID].destroy(); + } + delete filters[i].glShaders[renderer.CONTEXT_UID]; + } + this.shaderCache = {}; - this.emptyPool(); + if (!contextLost) + { + this.emptyPool(); + } + else + { + this.pool = {}; + } } /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 0aeda37..dad38fb 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -143,6 +143,7 @@ { this._textureID = -1; this._textureTrimmedID = -1; + this.cachedTint = 0xFFFFFF; // so if _width is 0 then width was not set.. if (this._width) diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 85abccf..1a9223f 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -151,6 +151,7 @@ { let frameIndex = initialFrameIndex; const maxFrames = Spritesheet.BATCH_SIZE; + const sourceScale = this.baseTexture.sourceScale; while (frameIndex - initialFrameIndex < maxFrames && frameIndex < this._frameKeys.length) { @@ -164,26 +165,26 @@ const orig = new Rectangle( 0, 0, - this._frames[i].sourceSize.w / this.resolution, - this._frames[i].sourceSize.h / this.resolution + Math.floor(this._frames[i].sourceSize.w * sourceScale) / this.resolution, + Math.floor(this._frames[i].sourceSize.h * sourceScale) / this.resolution ); if (this._frames[i].rotated) { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.h / this.resolution, - rect.w / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution ); } else { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } @@ -191,10 +192,10 @@ if (this._frames[i].trimmed) { trim = new Rectangle( - this._frames[i].spriteSourceSize.x / this.resolution, - this._frames[i].spriteSourceSize.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(this._frames[i].spriteSourceSize.x * sourceScale) / this.resolution, + Math.floor(this._frames[i].spriteSourceSize.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 3a472be..75eb069 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -552,15 +552,22 @@ this.noFrame = false; - if (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height) + const { x, y, width, height } = frame; + const xNotFit = x + width > this.baseTexture.width; + const yNotFit = y + height > this.baseTexture.height; + + if (xNotFit || yNotFit) { + const relationship = xNotFit && yNotFit ? 'and' : 'or'; + const errorX = `X: ${x} + ${width} = ${x + width} > ${this.baseTexture.width}`; + const errorY = `Y: ${y} + ${height} = ${y + height} > ${this.baseTexture.height}`; + throw new Error('Texture Error: frame does not fit inside the base Texture dimensions: ' - + `X: ${frame.x} + ${frame.width} = ${frame.x + frame.width} > ${this.baseTexture.width} ` - + `Y: ${frame.y} + ${frame.height} = ${frame.y + frame.height} > ${this.baseTexture.height}`); + + `${errorX} ${relationship} ${errorY}`); } - // this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded; - this.valid = frame && frame.width && frame.height && this.baseTexture.hasLoaded; + // this.valid = width && height && this.baseTexture.source && this.baseTexture.hasLoaded; + this.valid = width && height && this.baseTexture.hasLoaded; if (!this.trim && !this.rotate) { diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index 1754e0e..c605fa8 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -271,6 +271,7 @@ { this._texture = this._textures[this.currentFrame]; this._textureID = -1; + this.cachedTint = 0xFFFFFF; if (this.onFrameChange) { diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 6282341..736b207 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -136,6 +136,7 @@ { this.uvTransform.texture = this._texture; } + this.cachedTint = 0xFFFFFF; } /** @@ -185,9 +186,9 @@ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution; // create a nice shiny pattern! - // TODO this needs to be refreshed if texture changes.. - if (!this._canvasPattern) + if (this._textureID !== this._texture._updateID || this.cachedTint !== this.tint) { + this._textureID = this._texture._updateID; // cut an object from a spritesheet.. const tempCanvas = new core.CanvasRenderTarget(texture._frame.width, texture._frame.height, @@ -196,12 +197,7 @@ // Tint the tiling sprite if (this.tint !== 0xFFFFFF) { - if (this.cachedTint !== this.tint) - { - this.cachedTint = this.tint; - - this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); - } + this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); tempCanvas.context.drawImage(this.tintedTexture, 0, 0); } else @@ -209,6 +205,7 @@ tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } + this.cachedTint = this.tint; this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } diff --git a/package.json b/package.json index 668d451..1dfeee5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "resource-loader": "^2.0.9" }, "devDependencies": { + "@pixi/jsdoc-template": "^2.0.0", "babel-cli": "^6.18.0", "babel-plugin-static-fs": "^1.1.0", "babel-plugin-version-inline": "^1.0.0", @@ -75,7 +76,6 @@ "electron": "^1.4.15", "eslint": "^3.5.0", "floss": "^2.0.1", - "jaguarjs-jsdoc": "^1.0.1", "js-md5": "^0.4.1", "jsdoc": "3.4.3", "minimist": "^1.2.0", diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json index 8a3afee..2ee888c 100644 --- a/scripts/jsdoc.conf.json +++ b/scripts/jsdoc.conf.json @@ -51,6 +51,6 @@ "private" : false, "lenient" : true, "destination" : "./docs", - "template" : "./node_modules/jaguarjs-jsdoc" + "template" : "./node_modules/@pixi/jsdoc-template" } } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index f2d1c12..e63d2b0 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -127,6 +127,19 @@ */ this.currentRenderer = this.emptyRenderer; + /** + * Manages textures + * @member {PIXI.TextureManager} + */ + this.textureManager = null; + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = null; + this.initPlugins(); /** @@ -178,12 +191,6 @@ this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); // map some webGL blend and drawmodes.. this.drawModes = mapWebGLDrawModesToPixi(this.gl); @@ -236,6 +243,7 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.filterManager = new FilterManager(this); this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); @@ -683,6 +691,7 @@ handleContextRestored() { this.textureManager.removeAll(); + this.filterManager.destroy(true); this._initContext(); } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 7ed28ac..da00800 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -50,6 +50,10 @@ for (const i in this.uniformData) { this.uniforms[i] = this.uniformData[i].value; + if (this.uniformData[i].type) + { + this.uniformData[i].type = this.uniformData[i].type.toLowerCase(); + } } // this is where we store shader references.. diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index 851ad69..f955a9d 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,36 +40,13 @@ // this will map the filter coord so that a texture can be used based on the transform of a sprite export function calculateSpriteMatrix(outputMatrix, filterArea, textureSize, sprite) { - const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); const texture = sprite._texture.baseTexture; - - // TODO unwrap? - const mappedMatrix = outputMatrix.identity(); - - // scale.. - const ratio = textureSize.height / textureSize.width; - - mappedMatrix.translate(filterArea.x / textureSize.width, filterArea.y / textureSize.height); - - mappedMatrix.scale(1, ratio); - - const translateScaleX = (textureSize.width / texture.width); - const translateScaleY = (textureSize.height / texture.height); - - worldTransform.tx /= texture.width * translateScaleX; - - // this...? free beer for anyone who can explain why this makes sense! - worldTransform.ty /= texture.width * translateScaleX; - // worldTransform.ty /= texture.height * translateScaleY; + const mappedMatrix = outputMatrix.set(textureSize.width, 0, 0, textureSize.height, filterArea.x, filterArea.y); + const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); worldTransform.invert(); mappedMatrix.prepend(worldTransform); - - // apply inverse scale.. - mappedMatrix.scale(1, 1 / ratio); - - mappedMatrix.scale(translateScaleX, translateScaleY); - + mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index fdaa5c1..53f2ae1 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -49,6 +49,8 @@ this.pool = {}; this.filterData = null; + + this.managedFilters = []; } /** @@ -232,6 +234,8 @@ shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); } + this.managedFilters.push(filter); + // TODO - this only needs to be done once? renderer.bindVao(null); @@ -327,7 +331,9 @@ // TODO Cacheing layer.. for (const i in uniformData) { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + const type = uniformData[i].type; + + if (type === 'sampler2d' && uniforms[i] !== 0) { if (uniforms[i].baseTexture) { @@ -352,7 +358,7 @@ textureCount++; } - else if (uniformData[i].type === 'mat3') + else if (type === 'mat3') { // check if its PixiJS matrix.. if (uniforms[i].a !== undefined) @@ -364,7 +370,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'vec2') + else if (type === 'vec2') { // check if its a point.. if (uniforms[i].x !== undefined) @@ -380,7 +386,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'float') + else if (type === 'float') { if (shader.uniforms.data[i].value !== uniformData[i]) { @@ -486,11 +492,32 @@ /** * Destroys this Filter Manager. * + * @param {boolean} [contextLost=false] context was lost, do not free shaders + * */ - destroy() + destroy(contextLost = false) { + const renderer = this.renderer; + const filters = this.managedFilters; + + for (let i = 0; i < filters.length; i++) + { + if (!contextLost) + { + filters[i].glShaders[renderer.CONTEXT_UID].destroy(); + } + delete filters[i].glShaders[renderer.CONTEXT_UID]; + } + this.shaderCache = {}; - this.emptyPool(); + if (!contextLost) + { + this.emptyPool(); + } + else + { + this.pool = {}; + } } /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 0aeda37..dad38fb 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -143,6 +143,7 @@ { this._textureID = -1; this._textureTrimmedID = -1; + this.cachedTint = 0xFFFFFF; // so if _width is 0 then width was not set.. if (this._width) diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 85abccf..1a9223f 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -151,6 +151,7 @@ { let frameIndex = initialFrameIndex; const maxFrames = Spritesheet.BATCH_SIZE; + const sourceScale = this.baseTexture.sourceScale; while (frameIndex - initialFrameIndex < maxFrames && frameIndex < this._frameKeys.length) { @@ -164,26 +165,26 @@ const orig = new Rectangle( 0, 0, - this._frames[i].sourceSize.w / this.resolution, - this._frames[i].sourceSize.h / this.resolution + Math.floor(this._frames[i].sourceSize.w * sourceScale) / this.resolution, + Math.floor(this._frames[i].sourceSize.h * sourceScale) / this.resolution ); if (this._frames[i].rotated) { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.h / this.resolution, - rect.w / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution ); } else { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } @@ -191,10 +192,10 @@ if (this._frames[i].trimmed) { trim = new Rectangle( - this._frames[i].spriteSourceSize.x / this.resolution, - this._frames[i].spriteSourceSize.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(this._frames[i].spriteSourceSize.x * sourceScale) / this.resolution, + Math.floor(this._frames[i].spriteSourceSize.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 3a472be..75eb069 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -552,15 +552,22 @@ this.noFrame = false; - if (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height) + const { x, y, width, height } = frame; + const xNotFit = x + width > this.baseTexture.width; + const yNotFit = y + height > this.baseTexture.height; + + if (xNotFit || yNotFit) { + const relationship = xNotFit && yNotFit ? 'and' : 'or'; + const errorX = `X: ${x} + ${width} = ${x + width} > ${this.baseTexture.width}`; + const errorY = `Y: ${y} + ${height} = ${y + height} > ${this.baseTexture.height}`; + throw new Error('Texture Error: frame does not fit inside the base Texture dimensions: ' - + `X: ${frame.x} + ${frame.width} = ${frame.x + frame.width} > ${this.baseTexture.width} ` - + `Y: ${frame.y} + ${frame.height} = ${frame.y + frame.height} > ${this.baseTexture.height}`); + + `${errorX} ${relationship} ${errorY}`); } - // this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded; - this.valid = frame && frame.width && frame.height && this.baseTexture.hasLoaded; + // this.valid = width && height && this.baseTexture.source && this.baseTexture.hasLoaded; + this.valid = width && height && this.baseTexture.hasLoaded; if (!this.trim && !this.rotate) { diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index 1754e0e..c605fa8 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -271,6 +271,7 @@ { this._texture = this._textures[this.currentFrame]; this._textureID = -1; + this.cachedTint = 0xFFFFFF; if (this.onFrameChange) { diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 6282341..736b207 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -136,6 +136,7 @@ { this.uvTransform.texture = this._texture; } + this.cachedTint = 0xFFFFFF; } /** @@ -185,9 +186,9 @@ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution; // create a nice shiny pattern! - // TODO this needs to be refreshed if texture changes.. - if (!this._canvasPattern) + if (this._textureID !== this._texture._updateID || this.cachedTint !== this.tint) { + this._textureID = this._texture._updateID; // cut an object from a spritesheet.. const tempCanvas = new core.CanvasRenderTarget(texture._frame.width, texture._frame.height, @@ -196,12 +197,7 @@ // Tint the tiling sprite if (this.tint !== 0xFFFFFF) { - if (this.cachedTint !== this.tint) - { - this.cachedTint = this.tint; - - this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); - } + this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); tempCanvas.context.drawImage(this.tintedTexture, 0, 0); } else @@ -209,6 +205,7 @@ tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } + this.cachedTint = this.tint; this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js index 297e460..61ba5ee 100644 --- a/src/mesh/NineSlicePlane.js +++ b/src/mesh/NineSlicePlane.js @@ -363,7 +363,7 @@ this.updateHorizontalVertices(); this.updateVerticalVertices(); - this.dirty = true; + this.dirty++; this.multiplyUvs(); } diff --git a/package.json b/package.json index 668d451..1dfeee5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "resource-loader": "^2.0.9" }, "devDependencies": { + "@pixi/jsdoc-template": "^2.0.0", "babel-cli": "^6.18.0", "babel-plugin-static-fs": "^1.1.0", "babel-plugin-version-inline": "^1.0.0", @@ -75,7 +76,6 @@ "electron": "^1.4.15", "eslint": "^3.5.0", "floss": "^2.0.1", - "jaguarjs-jsdoc": "^1.0.1", "js-md5": "^0.4.1", "jsdoc": "3.4.3", "minimist": "^1.2.0", diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json index 8a3afee..2ee888c 100644 --- a/scripts/jsdoc.conf.json +++ b/scripts/jsdoc.conf.json @@ -51,6 +51,6 @@ "private" : false, "lenient" : true, "destination" : "./docs", - "template" : "./node_modules/jaguarjs-jsdoc" + "template" : "./node_modules/@pixi/jsdoc-template" } } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index f2d1c12..e63d2b0 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -127,6 +127,19 @@ */ this.currentRenderer = this.emptyRenderer; + /** + * Manages textures + * @member {PIXI.TextureManager} + */ + this.textureManager = null; + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = null; + this.initPlugins(); /** @@ -178,12 +191,6 @@ this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); // map some webGL blend and drawmodes.. this.drawModes = mapWebGLDrawModesToPixi(this.gl); @@ -236,6 +243,7 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.filterManager = new FilterManager(this); this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); @@ -683,6 +691,7 @@ handleContextRestored() { this.textureManager.removeAll(); + this.filterManager.destroy(true); this._initContext(); } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 7ed28ac..da00800 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -50,6 +50,10 @@ for (const i in this.uniformData) { this.uniforms[i] = this.uniformData[i].value; + if (this.uniformData[i].type) + { + this.uniformData[i].type = this.uniformData[i].type.toLowerCase(); + } } // this is where we store shader references.. diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index 851ad69..f955a9d 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,36 +40,13 @@ // this will map the filter coord so that a texture can be used based on the transform of a sprite export function calculateSpriteMatrix(outputMatrix, filterArea, textureSize, sprite) { - const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); const texture = sprite._texture.baseTexture; - - // TODO unwrap? - const mappedMatrix = outputMatrix.identity(); - - // scale.. - const ratio = textureSize.height / textureSize.width; - - mappedMatrix.translate(filterArea.x / textureSize.width, filterArea.y / textureSize.height); - - mappedMatrix.scale(1, ratio); - - const translateScaleX = (textureSize.width / texture.width); - const translateScaleY = (textureSize.height / texture.height); - - worldTransform.tx /= texture.width * translateScaleX; - - // this...? free beer for anyone who can explain why this makes sense! - worldTransform.ty /= texture.width * translateScaleX; - // worldTransform.ty /= texture.height * translateScaleY; + const mappedMatrix = outputMatrix.set(textureSize.width, 0, 0, textureSize.height, filterArea.x, filterArea.y); + const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); worldTransform.invert(); mappedMatrix.prepend(worldTransform); - - // apply inverse scale.. - mappedMatrix.scale(1, 1 / ratio); - - mappedMatrix.scale(translateScaleX, translateScaleY); - + mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index fdaa5c1..53f2ae1 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -49,6 +49,8 @@ this.pool = {}; this.filterData = null; + + this.managedFilters = []; } /** @@ -232,6 +234,8 @@ shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); } + this.managedFilters.push(filter); + // TODO - this only needs to be done once? renderer.bindVao(null); @@ -327,7 +331,9 @@ // TODO Cacheing layer.. for (const i in uniformData) { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + const type = uniformData[i].type; + + if (type === 'sampler2d' && uniforms[i] !== 0) { if (uniforms[i].baseTexture) { @@ -352,7 +358,7 @@ textureCount++; } - else if (uniformData[i].type === 'mat3') + else if (type === 'mat3') { // check if its PixiJS matrix.. if (uniforms[i].a !== undefined) @@ -364,7 +370,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'vec2') + else if (type === 'vec2') { // check if its a point.. if (uniforms[i].x !== undefined) @@ -380,7 +386,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'float') + else if (type === 'float') { if (shader.uniforms.data[i].value !== uniformData[i]) { @@ -486,11 +492,32 @@ /** * Destroys this Filter Manager. * + * @param {boolean} [contextLost=false] context was lost, do not free shaders + * */ - destroy() + destroy(contextLost = false) { + const renderer = this.renderer; + const filters = this.managedFilters; + + for (let i = 0; i < filters.length; i++) + { + if (!contextLost) + { + filters[i].glShaders[renderer.CONTEXT_UID].destroy(); + } + delete filters[i].glShaders[renderer.CONTEXT_UID]; + } + this.shaderCache = {}; - this.emptyPool(); + if (!contextLost) + { + this.emptyPool(); + } + else + { + this.pool = {}; + } } /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 0aeda37..dad38fb 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -143,6 +143,7 @@ { this._textureID = -1; this._textureTrimmedID = -1; + this.cachedTint = 0xFFFFFF; // so if _width is 0 then width was not set.. if (this._width) diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 85abccf..1a9223f 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -151,6 +151,7 @@ { let frameIndex = initialFrameIndex; const maxFrames = Spritesheet.BATCH_SIZE; + const sourceScale = this.baseTexture.sourceScale; while (frameIndex - initialFrameIndex < maxFrames && frameIndex < this._frameKeys.length) { @@ -164,26 +165,26 @@ const orig = new Rectangle( 0, 0, - this._frames[i].sourceSize.w / this.resolution, - this._frames[i].sourceSize.h / this.resolution + Math.floor(this._frames[i].sourceSize.w * sourceScale) / this.resolution, + Math.floor(this._frames[i].sourceSize.h * sourceScale) / this.resolution ); if (this._frames[i].rotated) { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.h / this.resolution, - rect.w / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution ); } else { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } @@ -191,10 +192,10 @@ if (this._frames[i].trimmed) { trim = new Rectangle( - this._frames[i].spriteSourceSize.x / this.resolution, - this._frames[i].spriteSourceSize.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(this._frames[i].spriteSourceSize.x * sourceScale) / this.resolution, + Math.floor(this._frames[i].spriteSourceSize.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 3a472be..75eb069 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -552,15 +552,22 @@ this.noFrame = false; - if (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height) + const { x, y, width, height } = frame; + const xNotFit = x + width > this.baseTexture.width; + const yNotFit = y + height > this.baseTexture.height; + + if (xNotFit || yNotFit) { + const relationship = xNotFit && yNotFit ? 'and' : 'or'; + const errorX = `X: ${x} + ${width} = ${x + width} > ${this.baseTexture.width}`; + const errorY = `Y: ${y} + ${height} = ${y + height} > ${this.baseTexture.height}`; + throw new Error('Texture Error: frame does not fit inside the base Texture dimensions: ' - + `X: ${frame.x} + ${frame.width} = ${frame.x + frame.width} > ${this.baseTexture.width} ` - + `Y: ${frame.y} + ${frame.height} = ${frame.y + frame.height} > ${this.baseTexture.height}`); + + `${errorX} ${relationship} ${errorY}`); } - // this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded; - this.valid = frame && frame.width && frame.height && this.baseTexture.hasLoaded; + // this.valid = width && height && this.baseTexture.source && this.baseTexture.hasLoaded; + this.valid = width && height && this.baseTexture.hasLoaded; if (!this.trim && !this.rotate) { diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index 1754e0e..c605fa8 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -271,6 +271,7 @@ { this._texture = this._textures[this.currentFrame]; this._textureID = -1; + this.cachedTint = 0xFFFFFF; if (this.onFrameChange) { diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 6282341..736b207 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -136,6 +136,7 @@ { this.uvTransform.texture = this._texture; } + this.cachedTint = 0xFFFFFF; } /** @@ -185,9 +186,9 @@ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution; // create a nice shiny pattern! - // TODO this needs to be refreshed if texture changes.. - if (!this._canvasPattern) + if (this._textureID !== this._texture._updateID || this.cachedTint !== this.tint) { + this._textureID = this._texture._updateID; // cut an object from a spritesheet.. const tempCanvas = new core.CanvasRenderTarget(texture._frame.width, texture._frame.height, @@ -196,12 +197,7 @@ // Tint the tiling sprite if (this.tint !== 0xFFFFFF) { - if (this.cachedTint !== this.tint) - { - this.cachedTint = this.tint; - - this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); - } + this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); tempCanvas.context.drawImage(this.tintedTexture, 0, 0); } else @@ -209,6 +205,7 @@ tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } + this.cachedTint = this.tint; this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js index 297e460..61ba5ee 100644 --- a/src/mesh/NineSlicePlane.js +++ b/src/mesh/NineSlicePlane.js @@ -363,7 +363,7 @@ this.updateHorizontalVertices(); this.updateVerticalVertices(); - this.dirty = true; + this.dirty++; this.multiplyUvs(); } diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js index 4f9b51c..4dd2398 100644 --- a/src/mesh/Plane.js +++ b/src/mesh/Plane.js @@ -94,7 +94,7 @@ this.uvs = new Float32Array(uvs); this.colors = new Float32Array(colors); this.indices = new Uint16Array(indices); - this.indexDirty = true; + this.indexDirty++; this.multiplyUvs(); } diff --git a/package.json b/package.json index 668d451..1dfeee5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "resource-loader": "^2.0.9" }, "devDependencies": { + "@pixi/jsdoc-template": "^2.0.0", "babel-cli": "^6.18.0", "babel-plugin-static-fs": "^1.1.0", "babel-plugin-version-inline": "^1.0.0", @@ -75,7 +76,6 @@ "electron": "^1.4.15", "eslint": "^3.5.0", "floss": "^2.0.1", - "jaguarjs-jsdoc": "^1.0.1", "js-md5": "^0.4.1", "jsdoc": "3.4.3", "minimist": "^1.2.0", diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json index 8a3afee..2ee888c 100644 --- a/scripts/jsdoc.conf.json +++ b/scripts/jsdoc.conf.json @@ -51,6 +51,6 @@ "private" : false, "lenient" : true, "destination" : "./docs", - "template" : "./node_modules/jaguarjs-jsdoc" + "template" : "./node_modules/@pixi/jsdoc-template" } } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index f2d1c12..e63d2b0 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -127,6 +127,19 @@ */ this.currentRenderer = this.emptyRenderer; + /** + * Manages textures + * @member {PIXI.TextureManager} + */ + this.textureManager = null; + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = null; + this.initPlugins(); /** @@ -178,12 +191,6 @@ this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); // map some webGL blend and drawmodes.. this.drawModes = mapWebGLDrawModesToPixi(this.gl); @@ -236,6 +243,7 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.filterManager = new FilterManager(this); this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); @@ -683,6 +691,7 @@ handleContextRestored() { this.textureManager.removeAll(); + this.filterManager.destroy(true); this._initContext(); } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 7ed28ac..da00800 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -50,6 +50,10 @@ for (const i in this.uniformData) { this.uniforms[i] = this.uniformData[i].value; + if (this.uniformData[i].type) + { + this.uniformData[i].type = this.uniformData[i].type.toLowerCase(); + } } // this is where we store shader references.. diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index 851ad69..f955a9d 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,36 +40,13 @@ // this will map the filter coord so that a texture can be used based on the transform of a sprite export function calculateSpriteMatrix(outputMatrix, filterArea, textureSize, sprite) { - const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); const texture = sprite._texture.baseTexture; - - // TODO unwrap? - const mappedMatrix = outputMatrix.identity(); - - // scale.. - const ratio = textureSize.height / textureSize.width; - - mappedMatrix.translate(filterArea.x / textureSize.width, filterArea.y / textureSize.height); - - mappedMatrix.scale(1, ratio); - - const translateScaleX = (textureSize.width / texture.width); - const translateScaleY = (textureSize.height / texture.height); - - worldTransform.tx /= texture.width * translateScaleX; - - // this...? free beer for anyone who can explain why this makes sense! - worldTransform.ty /= texture.width * translateScaleX; - // worldTransform.ty /= texture.height * translateScaleY; + const mappedMatrix = outputMatrix.set(textureSize.width, 0, 0, textureSize.height, filterArea.x, filterArea.y); + const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); worldTransform.invert(); mappedMatrix.prepend(worldTransform); - - // apply inverse scale.. - mappedMatrix.scale(1, 1 / ratio); - - mappedMatrix.scale(translateScaleX, translateScaleY); - + mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index fdaa5c1..53f2ae1 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -49,6 +49,8 @@ this.pool = {}; this.filterData = null; + + this.managedFilters = []; } /** @@ -232,6 +234,8 @@ shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); } + this.managedFilters.push(filter); + // TODO - this only needs to be done once? renderer.bindVao(null); @@ -327,7 +331,9 @@ // TODO Cacheing layer.. for (const i in uniformData) { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + const type = uniformData[i].type; + + if (type === 'sampler2d' && uniforms[i] !== 0) { if (uniforms[i].baseTexture) { @@ -352,7 +358,7 @@ textureCount++; } - else if (uniformData[i].type === 'mat3') + else if (type === 'mat3') { // check if its PixiJS matrix.. if (uniforms[i].a !== undefined) @@ -364,7 +370,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'vec2') + else if (type === 'vec2') { // check if its a point.. if (uniforms[i].x !== undefined) @@ -380,7 +386,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'float') + else if (type === 'float') { if (shader.uniforms.data[i].value !== uniformData[i]) { @@ -486,11 +492,32 @@ /** * Destroys this Filter Manager. * + * @param {boolean} [contextLost=false] context was lost, do not free shaders + * */ - destroy() + destroy(contextLost = false) { + const renderer = this.renderer; + const filters = this.managedFilters; + + for (let i = 0; i < filters.length; i++) + { + if (!contextLost) + { + filters[i].glShaders[renderer.CONTEXT_UID].destroy(); + } + delete filters[i].glShaders[renderer.CONTEXT_UID]; + } + this.shaderCache = {}; - this.emptyPool(); + if (!contextLost) + { + this.emptyPool(); + } + else + { + this.pool = {}; + } } /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 0aeda37..dad38fb 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -143,6 +143,7 @@ { this._textureID = -1; this._textureTrimmedID = -1; + this.cachedTint = 0xFFFFFF; // so if _width is 0 then width was not set.. if (this._width) diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 85abccf..1a9223f 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -151,6 +151,7 @@ { let frameIndex = initialFrameIndex; const maxFrames = Spritesheet.BATCH_SIZE; + const sourceScale = this.baseTexture.sourceScale; while (frameIndex - initialFrameIndex < maxFrames && frameIndex < this._frameKeys.length) { @@ -164,26 +165,26 @@ const orig = new Rectangle( 0, 0, - this._frames[i].sourceSize.w / this.resolution, - this._frames[i].sourceSize.h / this.resolution + Math.floor(this._frames[i].sourceSize.w * sourceScale) / this.resolution, + Math.floor(this._frames[i].sourceSize.h * sourceScale) / this.resolution ); if (this._frames[i].rotated) { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.h / this.resolution, - rect.w / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution ); } else { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } @@ -191,10 +192,10 @@ if (this._frames[i].trimmed) { trim = new Rectangle( - this._frames[i].spriteSourceSize.x / this.resolution, - this._frames[i].spriteSourceSize.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(this._frames[i].spriteSourceSize.x * sourceScale) / this.resolution, + Math.floor(this._frames[i].spriteSourceSize.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 3a472be..75eb069 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -552,15 +552,22 @@ this.noFrame = false; - if (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height) + const { x, y, width, height } = frame; + const xNotFit = x + width > this.baseTexture.width; + const yNotFit = y + height > this.baseTexture.height; + + if (xNotFit || yNotFit) { + const relationship = xNotFit && yNotFit ? 'and' : 'or'; + const errorX = `X: ${x} + ${width} = ${x + width} > ${this.baseTexture.width}`; + const errorY = `Y: ${y} + ${height} = ${y + height} > ${this.baseTexture.height}`; + throw new Error('Texture Error: frame does not fit inside the base Texture dimensions: ' - + `X: ${frame.x} + ${frame.width} = ${frame.x + frame.width} > ${this.baseTexture.width} ` - + `Y: ${frame.y} + ${frame.height} = ${frame.y + frame.height} > ${this.baseTexture.height}`); + + `${errorX} ${relationship} ${errorY}`); } - // this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded; - this.valid = frame && frame.width && frame.height && this.baseTexture.hasLoaded; + // this.valid = width && height && this.baseTexture.source && this.baseTexture.hasLoaded; + this.valid = width && height && this.baseTexture.hasLoaded; if (!this.trim && !this.rotate) { diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index 1754e0e..c605fa8 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -271,6 +271,7 @@ { this._texture = this._textures[this.currentFrame]; this._textureID = -1; + this.cachedTint = 0xFFFFFF; if (this.onFrameChange) { diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 6282341..736b207 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -136,6 +136,7 @@ { this.uvTransform.texture = this._texture; } + this.cachedTint = 0xFFFFFF; } /** @@ -185,9 +186,9 @@ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution; // create a nice shiny pattern! - // TODO this needs to be refreshed if texture changes.. - if (!this._canvasPattern) + if (this._textureID !== this._texture._updateID || this.cachedTint !== this.tint) { + this._textureID = this._texture._updateID; // cut an object from a spritesheet.. const tempCanvas = new core.CanvasRenderTarget(texture._frame.width, texture._frame.height, @@ -196,12 +197,7 @@ // Tint the tiling sprite if (this.tint !== 0xFFFFFF) { - if (this.cachedTint !== this.tint) - { - this.cachedTint = this.tint; - - this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); - } + this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); tempCanvas.context.drawImage(this.tintedTexture, 0, 0); } else @@ -209,6 +205,7 @@ tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } + this.cachedTint = this.tint; this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js index 297e460..61ba5ee 100644 --- a/src/mesh/NineSlicePlane.js +++ b/src/mesh/NineSlicePlane.js @@ -363,7 +363,7 @@ this.updateHorizontalVertices(); this.updateVerticalVertices(); - this.dirty = true; + this.dirty++; this.multiplyUvs(); } diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js index 4f9b51c..4dd2398 100644 --- a/src/mesh/Plane.js +++ b/src/mesh/Plane.js @@ -94,7 +94,7 @@ this.uvs = new Float32Array(uvs); this.colors = new Float32Array(colors); this.indices = new Uint16Array(indices); - this.indexDirty = true; + this.indexDirty++; this.multiplyUvs(); } diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index 1c725d2..233ee3b 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -28,16 +28,19 @@ export default class ParticleContainer extends core.Container { /** - * @param {number} [maxSize=15000] - The maximum number of particles that can be renderer by the container. + * @param {number} [maxSize=1500] - The maximum number of particles that can be rendered by the container. + * Affects size of allocated buffers. * @param {object} [properties] - The properties of children that should be uploaded to the gpu and applied. * @param {boolean} [properties.scale=false] - When true, scale be uploaded and applied. * @param {boolean} [properties.position=true] - When true, position be uploaded and applied. * @param {boolean} [properties.rotation=false] - When true, rotation be uploaded and applied. * @param {boolean} [properties.uvs=false] - When true, uvs be uploaded and applied. - * @param {boolean} [properties.alpha=false] - When true, alpha be uploaded and applied. - * @param {number} [batchSize=15000] - Number of particles per batch. + * @param {boolean} [properties.tint=false] - When true, alpha and tint be uploaded and applied. + * @param {number} [batchSize=16384] - Number of particles per batch. If less than maxSize, it uses maxSize instead. + * @param {boolean} [autoResize=true] If true, container allocates more batches in case + * there are more than `maxSize` particles. */ - constructor(maxSize = 1500, properties, batchSize = 16384) + constructor(maxSize = 1500, properties, batchSize = 16384, autoResize = false) { super(); @@ -105,6 +108,13 @@ this.blendMode = core.BLEND_MODES.NORMAL; /** + * If true, container allocates more batches in case there are more than `maxSize` particles. + * @member {boolean} + * @default false + */ + this.autoResize = autoResize; + + /** * Used for canvas renderering. If true then the elements will be positioned at the * nearest pixel. This provides a nice speed boost. * @@ -149,7 +159,8 @@ this._properties[1] = 'position' in properties ? !!properties.position : this._properties[1]; this._properties[2] = 'rotation' in properties ? !!properties.rotation : this._properties[2]; this._properties[3] = 'uvs' in properties ? !!properties.uvs : this._properties[3]; - this._properties[4] = 'alpha' in properties ? !!properties.alpha : this._properties[4]; + this._properties[4] = 'alpha' in properties || 'tint' in properties + ? !!properties.alpha || !!properties.tint : this._properties[4]; } } diff --git a/package.json b/package.json index 668d451..1dfeee5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "resource-loader": "^2.0.9" }, "devDependencies": { + "@pixi/jsdoc-template": "^2.0.0", "babel-cli": "^6.18.0", "babel-plugin-static-fs": "^1.1.0", "babel-plugin-version-inline": "^1.0.0", @@ -75,7 +76,6 @@ "electron": "^1.4.15", "eslint": "^3.5.0", "floss": "^2.0.1", - "jaguarjs-jsdoc": "^1.0.1", "js-md5": "^0.4.1", "jsdoc": "3.4.3", "minimist": "^1.2.0", diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json index 8a3afee..2ee888c 100644 --- a/scripts/jsdoc.conf.json +++ b/scripts/jsdoc.conf.json @@ -51,6 +51,6 @@ "private" : false, "lenient" : true, "destination" : "./docs", - "template" : "./node_modules/jaguarjs-jsdoc" + "template" : "./node_modules/@pixi/jsdoc-template" } } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index f2d1c12..e63d2b0 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -127,6 +127,19 @@ */ this.currentRenderer = this.emptyRenderer; + /** + * Manages textures + * @member {PIXI.TextureManager} + */ + this.textureManager = null; + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = null; + this.initPlugins(); /** @@ -178,12 +191,6 @@ this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); // map some webGL blend and drawmodes.. this.drawModes = mapWebGLDrawModesToPixi(this.gl); @@ -236,6 +243,7 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.filterManager = new FilterManager(this); this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); @@ -683,6 +691,7 @@ handleContextRestored() { this.textureManager.removeAll(); + this.filterManager.destroy(true); this._initContext(); } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 7ed28ac..da00800 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -50,6 +50,10 @@ for (const i in this.uniformData) { this.uniforms[i] = this.uniformData[i].value; + if (this.uniformData[i].type) + { + this.uniformData[i].type = this.uniformData[i].type.toLowerCase(); + } } // this is where we store shader references.. diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index 851ad69..f955a9d 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,36 +40,13 @@ // this will map the filter coord so that a texture can be used based on the transform of a sprite export function calculateSpriteMatrix(outputMatrix, filterArea, textureSize, sprite) { - const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); const texture = sprite._texture.baseTexture; - - // TODO unwrap? - const mappedMatrix = outputMatrix.identity(); - - // scale.. - const ratio = textureSize.height / textureSize.width; - - mappedMatrix.translate(filterArea.x / textureSize.width, filterArea.y / textureSize.height); - - mappedMatrix.scale(1, ratio); - - const translateScaleX = (textureSize.width / texture.width); - const translateScaleY = (textureSize.height / texture.height); - - worldTransform.tx /= texture.width * translateScaleX; - - // this...? free beer for anyone who can explain why this makes sense! - worldTransform.ty /= texture.width * translateScaleX; - // worldTransform.ty /= texture.height * translateScaleY; + const mappedMatrix = outputMatrix.set(textureSize.width, 0, 0, textureSize.height, filterArea.x, filterArea.y); + const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); worldTransform.invert(); mappedMatrix.prepend(worldTransform); - - // apply inverse scale.. - mappedMatrix.scale(1, 1 / ratio); - - mappedMatrix.scale(translateScaleX, translateScaleY); - + mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index fdaa5c1..53f2ae1 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -49,6 +49,8 @@ this.pool = {}; this.filterData = null; + + this.managedFilters = []; } /** @@ -232,6 +234,8 @@ shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); } + this.managedFilters.push(filter); + // TODO - this only needs to be done once? renderer.bindVao(null); @@ -327,7 +331,9 @@ // TODO Cacheing layer.. for (const i in uniformData) { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + const type = uniformData[i].type; + + if (type === 'sampler2d' && uniforms[i] !== 0) { if (uniforms[i].baseTexture) { @@ -352,7 +358,7 @@ textureCount++; } - else if (uniformData[i].type === 'mat3') + else if (type === 'mat3') { // check if its PixiJS matrix.. if (uniforms[i].a !== undefined) @@ -364,7 +370,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'vec2') + else if (type === 'vec2') { // check if its a point.. if (uniforms[i].x !== undefined) @@ -380,7 +386,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'float') + else if (type === 'float') { if (shader.uniforms.data[i].value !== uniformData[i]) { @@ -486,11 +492,32 @@ /** * Destroys this Filter Manager. * + * @param {boolean} [contextLost=false] context was lost, do not free shaders + * */ - destroy() + destroy(contextLost = false) { + const renderer = this.renderer; + const filters = this.managedFilters; + + for (let i = 0; i < filters.length; i++) + { + if (!contextLost) + { + filters[i].glShaders[renderer.CONTEXT_UID].destroy(); + } + delete filters[i].glShaders[renderer.CONTEXT_UID]; + } + this.shaderCache = {}; - this.emptyPool(); + if (!contextLost) + { + this.emptyPool(); + } + else + { + this.pool = {}; + } } /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 0aeda37..dad38fb 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -143,6 +143,7 @@ { this._textureID = -1; this._textureTrimmedID = -1; + this.cachedTint = 0xFFFFFF; // so if _width is 0 then width was not set.. if (this._width) diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 85abccf..1a9223f 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -151,6 +151,7 @@ { let frameIndex = initialFrameIndex; const maxFrames = Spritesheet.BATCH_SIZE; + const sourceScale = this.baseTexture.sourceScale; while (frameIndex - initialFrameIndex < maxFrames && frameIndex < this._frameKeys.length) { @@ -164,26 +165,26 @@ const orig = new Rectangle( 0, 0, - this._frames[i].sourceSize.w / this.resolution, - this._frames[i].sourceSize.h / this.resolution + Math.floor(this._frames[i].sourceSize.w * sourceScale) / this.resolution, + Math.floor(this._frames[i].sourceSize.h * sourceScale) / this.resolution ); if (this._frames[i].rotated) { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.h / this.resolution, - rect.w / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution ); } else { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } @@ -191,10 +192,10 @@ if (this._frames[i].trimmed) { trim = new Rectangle( - this._frames[i].spriteSourceSize.x / this.resolution, - this._frames[i].spriteSourceSize.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(this._frames[i].spriteSourceSize.x * sourceScale) / this.resolution, + Math.floor(this._frames[i].spriteSourceSize.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 3a472be..75eb069 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -552,15 +552,22 @@ this.noFrame = false; - if (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height) + const { x, y, width, height } = frame; + const xNotFit = x + width > this.baseTexture.width; + const yNotFit = y + height > this.baseTexture.height; + + if (xNotFit || yNotFit) { + const relationship = xNotFit && yNotFit ? 'and' : 'or'; + const errorX = `X: ${x} + ${width} = ${x + width} > ${this.baseTexture.width}`; + const errorY = `Y: ${y} + ${height} = ${y + height} > ${this.baseTexture.height}`; + throw new Error('Texture Error: frame does not fit inside the base Texture dimensions: ' - + `X: ${frame.x} + ${frame.width} = ${frame.x + frame.width} > ${this.baseTexture.width} ` - + `Y: ${frame.y} + ${frame.height} = ${frame.y + frame.height} > ${this.baseTexture.height}`); + + `${errorX} ${relationship} ${errorY}`); } - // this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded; - this.valid = frame && frame.width && frame.height && this.baseTexture.hasLoaded; + // this.valid = width && height && this.baseTexture.source && this.baseTexture.hasLoaded; + this.valid = width && height && this.baseTexture.hasLoaded; if (!this.trim && !this.rotate) { diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index 1754e0e..c605fa8 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -271,6 +271,7 @@ { this._texture = this._textures[this.currentFrame]; this._textureID = -1; + this.cachedTint = 0xFFFFFF; if (this.onFrameChange) { diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 6282341..736b207 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -136,6 +136,7 @@ { this.uvTransform.texture = this._texture; } + this.cachedTint = 0xFFFFFF; } /** @@ -185,9 +186,9 @@ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution; // create a nice shiny pattern! - // TODO this needs to be refreshed if texture changes.. - if (!this._canvasPattern) + if (this._textureID !== this._texture._updateID || this.cachedTint !== this.tint) { + this._textureID = this._texture._updateID; // cut an object from a spritesheet.. const tempCanvas = new core.CanvasRenderTarget(texture._frame.width, texture._frame.height, @@ -196,12 +197,7 @@ // Tint the tiling sprite if (this.tint !== 0xFFFFFF) { - if (this.cachedTint !== this.tint) - { - this.cachedTint = this.tint; - - this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); - } + this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); tempCanvas.context.drawImage(this.tintedTexture, 0, 0); } else @@ -209,6 +205,7 @@ tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } + this.cachedTint = this.tint; this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js index 297e460..61ba5ee 100644 --- a/src/mesh/NineSlicePlane.js +++ b/src/mesh/NineSlicePlane.js @@ -363,7 +363,7 @@ this.updateHorizontalVertices(); this.updateVerticalVertices(); - this.dirty = true; + this.dirty++; this.multiplyUvs(); } diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js index 4f9b51c..4dd2398 100644 --- a/src/mesh/Plane.js +++ b/src/mesh/Plane.js @@ -94,7 +94,7 @@ this.uvs = new Float32Array(uvs); this.colors = new Float32Array(colors); this.indices = new Uint16Array(indices); - this.indexDirty = true; + this.indexDirty++; this.multiplyUvs(); } diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index 1c725d2..233ee3b 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -28,16 +28,19 @@ export default class ParticleContainer extends core.Container { /** - * @param {number} [maxSize=15000] - The maximum number of particles that can be renderer by the container. + * @param {number} [maxSize=1500] - The maximum number of particles that can be rendered by the container. + * Affects size of allocated buffers. * @param {object} [properties] - The properties of children that should be uploaded to the gpu and applied. * @param {boolean} [properties.scale=false] - When true, scale be uploaded and applied. * @param {boolean} [properties.position=true] - When true, position be uploaded and applied. * @param {boolean} [properties.rotation=false] - When true, rotation be uploaded and applied. * @param {boolean} [properties.uvs=false] - When true, uvs be uploaded and applied. - * @param {boolean} [properties.alpha=false] - When true, alpha be uploaded and applied. - * @param {number} [batchSize=15000] - Number of particles per batch. + * @param {boolean} [properties.tint=false] - When true, alpha and tint be uploaded and applied. + * @param {number} [batchSize=16384] - Number of particles per batch. If less than maxSize, it uses maxSize instead. + * @param {boolean} [autoResize=true] If true, container allocates more batches in case + * there are more than `maxSize` particles. */ - constructor(maxSize = 1500, properties, batchSize = 16384) + constructor(maxSize = 1500, properties, batchSize = 16384, autoResize = false) { super(); @@ -105,6 +108,13 @@ this.blendMode = core.BLEND_MODES.NORMAL; /** + * If true, container allocates more batches in case there are more than `maxSize` particles. + * @member {boolean} + * @default false + */ + this.autoResize = autoResize; + + /** * Used for canvas renderering. If true then the elements will be positioned at the * nearest pixel. This provides a nice speed boost. * @@ -149,7 +159,8 @@ this._properties[1] = 'position' in properties ? !!properties.position : this._properties[1]; this._properties[2] = 'rotation' in properties ? !!properties.rotation : this._properties[2]; this._properties[3] = 'uvs' in properties ? !!properties.uvs : this._properties[3]; - this._properties[4] = 'alpha' in properties ? !!properties.alpha : this._properties[4]; + this._properties[4] = 'alpha' in properties || 'tint' in properties + ? !!properties.alpha || !!properties.tint : this._properties[4]; } } diff --git a/src/particles/webgl/ParticleBuffer.js b/src/particles/webgl/ParticleBuffer.js index bd8e243..0d41f66 100644 --- a/src/particles/webgl/ParticleBuffer.js +++ b/src/particles/webgl/ParticleBuffer.js @@ -38,20 +38,6 @@ this.gl = gl; /** - * Size of a single vertex. - * - * @member {number} - */ - this.vertSize = 2; - - /** - * Size of a single vertex in bytes. - * - * @member {number} - */ - this.vertByteSize = this.vertSize * 4; - - /** * The number of particles the buffer can hold * * @member {number} @@ -82,6 +68,7 @@ attribute: property.attribute, size: property.size, uploadFunction: property.uploadFunction, + unsignedByte: property.unsignedByte, offset: property.offset, }; @@ -98,10 +85,12 @@ this.staticStride = 0; this.staticBuffer = null; this.staticData = null; + this.staticDataUint32 = null; this.dynamicStride = 0; this.dynamicBuffer = null; this.dynamicData = null; + this.dynamicDataUint32 = null; this.initBuffers(); } @@ -135,8 +124,11 @@ this.dynamicStride += property.size; } - this.dynamicData = new Float32Array(this.size * this.dynamicStride * 4); - this.dynamicBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.dynamicData, gl.STREAM_DRAW); + const dynBuffer = new ArrayBuffer(this.size * this.dynamicStride * 4 * 4); + + this.dynamicData = new Float32Array(dynBuffer); + this.dynamicDataUint32 = new Uint32Array(dynBuffer); + this.dynamicBuffer = glCore.GLBuffer.createVertexBuffer(gl, dynBuffer, gl.STREAM_DRAW); // static // let staticOffset = 0; @@ -152,8 +144,11 @@ this.staticStride += property.size; } - this.staticData = new Float32Array(this.size * this.staticStride * 4); - this.staticBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.staticData, gl.STATIC_DRAW); + const statBuffer = new ArrayBuffer(this.size * this.staticStride * 4 * 4); + + this.staticData = new Float32Array(statBuffer); + this.staticDataUint32 = new Uint32Array(statBuffer); + this.staticBuffer = glCore.GLBuffer.createVertexBuffer(gl, statBuffer, gl.STATIC_DRAW); this.vao = new glCore.VertexArrayObject(gl) .addIndex(this.indexBuffer); @@ -162,28 +157,56 @@ { const property = this.dynamicProperties[i]; - this.vao.addAttribute( - this.dynamicBuffer, - property.attribute, - gl.FLOAT, - false, - this.dynamicStride * 4, - property.offset * 4 - ); + if (property.unsignedByte) + { + this.vao.addAttribute( + this.dynamicBuffer, + property.attribute, + gl.UNSIGNED_BYTE, + true, + this.dynamicStride * 4, + property.offset * 4 + ); + } + else + { + this.vao.addAttribute( + this.dynamicBuffer, + property.attribute, + gl.FLOAT, + false, + this.dynamicStride * 4, + property.offset * 4 + ); + } } for (let i = 0; i < this.staticProperties.length; ++i) { const property = this.staticProperties[i]; - this.vao.addAttribute( - this.staticBuffer, - property.attribute, - gl.FLOAT, - false, - this.staticStride * 4, - property.offset * 4 - ); + if (property.unsignedByte) + { + this.vao.addAttribute( + this.staticBuffer, + property.attribute, + gl.UNSIGNED_BYTE, + true, + this.staticStride * 4, + property.offset * 4 + ); + } + else + { + this.vao.addAttribute( + this.staticBuffer, + property.attribute, + gl.FLOAT, + false, + this.staticStride * 4, + property.offset * 4 + ); + } } } @@ -200,7 +223,9 @@ { const property = this.dynamicProperties[i]; - property.uploadFunction(children, startIndex, amount, this.dynamicData, this.dynamicStride, property.offset); + property.uploadFunction(children, startIndex, amount, + property.unsignedByte ? this.dynamicDataUint32 : this.dynamicData, + this.dynamicStride, property.offset); } this.dynamicBuffer.upload(); @@ -219,7 +244,9 @@ { const property = this.staticProperties[i]; - property.uploadFunction(children, startIndex, amount, this.staticData, this.staticStride, property.offset); + property.uploadFunction(children, startIndex, amount, + property.unsignedByte ? this.staticDataUint32 : this.staticData, + this.staticStride, property.offset); } this.staticBuffer.upload(); diff --git a/package.json b/package.json index 668d451..1dfeee5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "resource-loader": "^2.0.9" }, "devDependencies": { + "@pixi/jsdoc-template": "^2.0.0", "babel-cli": "^6.18.0", "babel-plugin-static-fs": "^1.1.0", "babel-plugin-version-inline": "^1.0.0", @@ -75,7 +76,6 @@ "electron": "^1.4.15", "eslint": "^3.5.0", "floss": "^2.0.1", - "jaguarjs-jsdoc": "^1.0.1", "js-md5": "^0.4.1", "jsdoc": "3.4.3", "minimist": "^1.2.0", diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json index 8a3afee..2ee888c 100644 --- a/scripts/jsdoc.conf.json +++ b/scripts/jsdoc.conf.json @@ -51,6 +51,6 @@ "private" : false, "lenient" : true, "destination" : "./docs", - "template" : "./node_modules/jaguarjs-jsdoc" + "template" : "./node_modules/@pixi/jsdoc-template" } } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index f2d1c12..e63d2b0 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -127,6 +127,19 @@ */ this.currentRenderer = this.emptyRenderer; + /** + * Manages textures + * @member {PIXI.TextureManager} + */ + this.textureManager = null; + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = null; + this.initPlugins(); /** @@ -178,12 +191,6 @@ this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); // map some webGL blend and drawmodes.. this.drawModes = mapWebGLDrawModesToPixi(this.gl); @@ -236,6 +243,7 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.filterManager = new FilterManager(this); this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); @@ -683,6 +691,7 @@ handleContextRestored() { this.textureManager.removeAll(); + this.filterManager.destroy(true); this._initContext(); } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 7ed28ac..da00800 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -50,6 +50,10 @@ for (const i in this.uniformData) { this.uniforms[i] = this.uniformData[i].value; + if (this.uniformData[i].type) + { + this.uniformData[i].type = this.uniformData[i].type.toLowerCase(); + } } // this is where we store shader references.. diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index 851ad69..f955a9d 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,36 +40,13 @@ // this will map the filter coord so that a texture can be used based on the transform of a sprite export function calculateSpriteMatrix(outputMatrix, filterArea, textureSize, sprite) { - const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); const texture = sprite._texture.baseTexture; - - // TODO unwrap? - const mappedMatrix = outputMatrix.identity(); - - // scale.. - const ratio = textureSize.height / textureSize.width; - - mappedMatrix.translate(filterArea.x / textureSize.width, filterArea.y / textureSize.height); - - mappedMatrix.scale(1, ratio); - - const translateScaleX = (textureSize.width / texture.width); - const translateScaleY = (textureSize.height / texture.height); - - worldTransform.tx /= texture.width * translateScaleX; - - // this...? free beer for anyone who can explain why this makes sense! - worldTransform.ty /= texture.width * translateScaleX; - // worldTransform.ty /= texture.height * translateScaleY; + const mappedMatrix = outputMatrix.set(textureSize.width, 0, 0, textureSize.height, filterArea.x, filterArea.y); + const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); worldTransform.invert(); mappedMatrix.prepend(worldTransform); - - // apply inverse scale.. - mappedMatrix.scale(1, 1 / ratio); - - mappedMatrix.scale(translateScaleX, translateScaleY); - + mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index fdaa5c1..53f2ae1 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -49,6 +49,8 @@ this.pool = {}; this.filterData = null; + + this.managedFilters = []; } /** @@ -232,6 +234,8 @@ shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); } + this.managedFilters.push(filter); + // TODO - this only needs to be done once? renderer.bindVao(null); @@ -327,7 +331,9 @@ // TODO Cacheing layer.. for (const i in uniformData) { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + const type = uniformData[i].type; + + if (type === 'sampler2d' && uniforms[i] !== 0) { if (uniforms[i].baseTexture) { @@ -352,7 +358,7 @@ textureCount++; } - else if (uniformData[i].type === 'mat3') + else if (type === 'mat3') { // check if its PixiJS matrix.. if (uniforms[i].a !== undefined) @@ -364,7 +370,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'vec2') + else if (type === 'vec2') { // check if its a point.. if (uniforms[i].x !== undefined) @@ -380,7 +386,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'float') + else if (type === 'float') { if (shader.uniforms.data[i].value !== uniformData[i]) { @@ -486,11 +492,32 @@ /** * Destroys this Filter Manager. * + * @param {boolean} [contextLost=false] context was lost, do not free shaders + * */ - destroy() + destroy(contextLost = false) { + const renderer = this.renderer; + const filters = this.managedFilters; + + for (let i = 0; i < filters.length; i++) + { + if (!contextLost) + { + filters[i].glShaders[renderer.CONTEXT_UID].destroy(); + } + delete filters[i].glShaders[renderer.CONTEXT_UID]; + } + this.shaderCache = {}; - this.emptyPool(); + if (!contextLost) + { + this.emptyPool(); + } + else + { + this.pool = {}; + } } /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 0aeda37..dad38fb 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -143,6 +143,7 @@ { this._textureID = -1; this._textureTrimmedID = -1; + this.cachedTint = 0xFFFFFF; // so if _width is 0 then width was not set.. if (this._width) diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 85abccf..1a9223f 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -151,6 +151,7 @@ { let frameIndex = initialFrameIndex; const maxFrames = Spritesheet.BATCH_SIZE; + const sourceScale = this.baseTexture.sourceScale; while (frameIndex - initialFrameIndex < maxFrames && frameIndex < this._frameKeys.length) { @@ -164,26 +165,26 @@ const orig = new Rectangle( 0, 0, - this._frames[i].sourceSize.w / this.resolution, - this._frames[i].sourceSize.h / this.resolution + Math.floor(this._frames[i].sourceSize.w * sourceScale) / this.resolution, + Math.floor(this._frames[i].sourceSize.h * sourceScale) / this.resolution ); if (this._frames[i].rotated) { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.h / this.resolution, - rect.w / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution ); } else { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } @@ -191,10 +192,10 @@ if (this._frames[i].trimmed) { trim = new Rectangle( - this._frames[i].spriteSourceSize.x / this.resolution, - this._frames[i].spriteSourceSize.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(this._frames[i].spriteSourceSize.x * sourceScale) / this.resolution, + Math.floor(this._frames[i].spriteSourceSize.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 3a472be..75eb069 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -552,15 +552,22 @@ this.noFrame = false; - if (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height) + const { x, y, width, height } = frame; + const xNotFit = x + width > this.baseTexture.width; + const yNotFit = y + height > this.baseTexture.height; + + if (xNotFit || yNotFit) { + const relationship = xNotFit && yNotFit ? 'and' : 'or'; + const errorX = `X: ${x} + ${width} = ${x + width} > ${this.baseTexture.width}`; + const errorY = `Y: ${y} + ${height} = ${y + height} > ${this.baseTexture.height}`; + throw new Error('Texture Error: frame does not fit inside the base Texture dimensions: ' - + `X: ${frame.x} + ${frame.width} = ${frame.x + frame.width} > ${this.baseTexture.width} ` - + `Y: ${frame.y} + ${frame.height} = ${frame.y + frame.height} > ${this.baseTexture.height}`); + + `${errorX} ${relationship} ${errorY}`); } - // this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded; - this.valid = frame && frame.width && frame.height && this.baseTexture.hasLoaded; + // this.valid = width && height && this.baseTexture.source && this.baseTexture.hasLoaded; + this.valid = width && height && this.baseTexture.hasLoaded; if (!this.trim && !this.rotate) { diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index 1754e0e..c605fa8 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -271,6 +271,7 @@ { this._texture = this._textures[this.currentFrame]; this._textureID = -1; + this.cachedTint = 0xFFFFFF; if (this.onFrameChange) { diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 6282341..736b207 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -136,6 +136,7 @@ { this.uvTransform.texture = this._texture; } + this.cachedTint = 0xFFFFFF; } /** @@ -185,9 +186,9 @@ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution; // create a nice shiny pattern! - // TODO this needs to be refreshed if texture changes.. - if (!this._canvasPattern) + if (this._textureID !== this._texture._updateID || this.cachedTint !== this.tint) { + this._textureID = this._texture._updateID; // cut an object from a spritesheet.. const tempCanvas = new core.CanvasRenderTarget(texture._frame.width, texture._frame.height, @@ -196,12 +197,7 @@ // Tint the tiling sprite if (this.tint !== 0xFFFFFF) { - if (this.cachedTint !== this.tint) - { - this.cachedTint = this.tint; - - this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); - } + this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); tempCanvas.context.drawImage(this.tintedTexture, 0, 0); } else @@ -209,6 +205,7 @@ tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } + this.cachedTint = this.tint; this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js index 297e460..61ba5ee 100644 --- a/src/mesh/NineSlicePlane.js +++ b/src/mesh/NineSlicePlane.js @@ -363,7 +363,7 @@ this.updateHorizontalVertices(); this.updateVerticalVertices(); - this.dirty = true; + this.dirty++; this.multiplyUvs(); } diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js index 4f9b51c..4dd2398 100644 --- a/src/mesh/Plane.js +++ b/src/mesh/Plane.js @@ -94,7 +94,7 @@ this.uvs = new Float32Array(uvs); this.colors = new Float32Array(colors); this.indices = new Uint16Array(indices); - this.indexDirty = true; + this.indexDirty++; this.multiplyUvs(); } diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index 1c725d2..233ee3b 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -28,16 +28,19 @@ export default class ParticleContainer extends core.Container { /** - * @param {number} [maxSize=15000] - The maximum number of particles that can be renderer by the container. + * @param {number} [maxSize=1500] - The maximum number of particles that can be rendered by the container. + * Affects size of allocated buffers. * @param {object} [properties] - The properties of children that should be uploaded to the gpu and applied. * @param {boolean} [properties.scale=false] - When true, scale be uploaded and applied. * @param {boolean} [properties.position=true] - When true, position be uploaded and applied. * @param {boolean} [properties.rotation=false] - When true, rotation be uploaded and applied. * @param {boolean} [properties.uvs=false] - When true, uvs be uploaded and applied. - * @param {boolean} [properties.alpha=false] - When true, alpha be uploaded and applied. - * @param {number} [batchSize=15000] - Number of particles per batch. + * @param {boolean} [properties.tint=false] - When true, alpha and tint be uploaded and applied. + * @param {number} [batchSize=16384] - Number of particles per batch. If less than maxSize, it uses maxSize instead. + * @param {boolean} [autoResize=true] If true, container allocates more batches in case + * there are more than `maxSize` particles. */ - constructor(maxSize = 1500, properties, batchSize = 16384) + constructor(maxSize = 1500, properties, batchSize = 16384, autoResize = false) { super(); @@ -105,6 +108,13 @@ this.blendMode = core.BLEND_MODES.NORMAL; /** + * If true, container allocates more batches in case there are more than `maxSize` particles. + * @member {boolean} + * @default false + */ + this.autoResize = autoResize; + + /** * Used for canvas renderering. If true then the elements will be positioned at the * nearest pixel. This provides a nice speed boost. * @@ -149,7 +159,8 @@ this._properties[1] = 'position' in properties ? !!properties.position : this._properties[1]; this._properties[2] = 'rotation' in properties ? !!properties.rotation : this._properties[2]; this._properties[3] = 'uvs' in properties ? !!properties.uvs : this._properties[3]; - this._properties[4] = 'alpha' in properties ? !!properties.alpha : this._properties[4]; + this._properties[4] = 'alpha' in properties || 'tint' in properties + ? !!properties.alpha || !!properties.tint : this._properties[4]; } } diff --git a/src/particles/webgl/ParticleBuffer.js b/src/particles/webgl/ParticleBuffer.js index bd8e243..0d41f66 100644 --- a/src/particles/webgl/ParticleBuffer.js +++ b/src/particles/webgl/ParticleBuffer.js @@ -38,20 +38,6 @@ this.gl = gl; /** - * Size of a single vertex. - * - * @member {number} - */ - this.vertSize = 2; - - /** - * Size of a single vertex in bytes. - * - * @member {number} - */ - this.vertByteSize = this.vertSize * 4; - - /** * The number of particles the buffer can hold * * @member {number} @@ -82,6 +68,7 @@ attribute: property.attribute, size: property.size, uploadFunction: property.uploadFunction, + unsignedByte: property.unsignedByte, offset: property.offset, }; @@ -98,10 +85,12 @@ this.staticStride = 0; this.staticBuffer = null; this.staticData = null; + this.staticDataUint32 = null; this.dynamicStride = 0; this.dynamicBuffer = null; this.dynamicData = null; + this.dynamicDataUint32 = null; this.initBuffers(); } @@ -135,8 +124,11 @@ this.dynamicStride += property.size; } - this.dynamicData = new Float32Array(this.size * this.dynamicStride * 4); - this.dynamicBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.dynamicData, gl.STREAM_DRAW); + const dynBuffer = new ArrayBuffer(this.size * this.dynamicStride * 4 * 4); + + this.dynamicData = new Float32Array(dynBuffer); + this.dynamicDataUint32 = new Uint32Array(dynBuffer); + this.dynamicBuffer = glCore.GLBuffer.createVertexBuffer(gl, dynBuffer, gl.STREAM_DRAW); // static // let staticOffset = 0; @@ -152,8 +144,11 @@ this.staticStride += property.size; } - this.staticData = new Float32Array(this.size * this.staticStride * 4); - this.staticBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.staticData, gl.STATIC_DRAW); + const statBuffer = new ArrayBuffer(this.size * this.staticStride * 4 * 4); + + this.staticData = new Float32Array(statBuffer); + this.staticDataUint32 = new Uint32Array(statBuffer); + this.staticBuffer = glCore.GLBuffer.createVertexBuffer(gl, statBuffer, gl.STATIC_DRAW); this.vao = new glCore.VertexArrayObject(gl) .addIndex(this.indexBuffer); @@ -162,28 +157,56 @@ { const property = this.dynamicProperties[i]; - this.vao.addAttribute( - this.dynamicBuffer, - property.attribute, - gl.FLOAT, - false, - this.dynamicStride * 4, - property.offset * 4 - ); + if (property.unsignedByte) + { + this.vao.addAttribute( + this.dynamicBuffer, + property.attribute, + gl.UNSIGNED_BYTE, + true, + this.dynamicStride * 4, + property.offset * 4 + ); + } + else + { + this.vao.addAttribute( + this.dynamicBuffer, + property.attribute, + gl.FLOAT, + false, + this.dynamicStride * 4, + property.offset * 4 + ); + } } for (let i = 0; i < this.staticProperties.length; ++i) { const property = this.staticProperties[i]; - this.vao.addAttribute( - this.staticBuffer, - property.attribute, - gl.FLOAT, - false, - this.staticStride * 4, - property.offset * 4 - ); + if (property.unsignedByte) + { + this.vao.addAttribute( + this.staticBuffer, + property.attribute, + gl.UNSIGNED_BYTE, + true, + this.staticStride * 4, + property.offset * 4 + ); + } + else + { + this.vao.addAttribute( + this.staticBuffer, + property.attribute, + gl.FLOAT, + false, + this.staticStride * 4, + property.offset * 4 + ); + } } } @@ -200,7 +223,9 @@ { const property = this.dynamicProperties[i]; - property.uploadFunction(children, startIndex, amount, this.dynamicData, this.dynamicStride, property.offset); + property.uploadFunction(children, startIndex, amount, + property.unsignedByte ? this.dynamicDataUint32 : this.dynamicData, + this.dynamicStride, property.offset); } this.dynamicBuffer.upload(); @@ -219,7 +244,9 @@ { const property = this.staticProperties[i]; - property.uploadFunction(children, startIndex, amount, this.staticData, this.staticStride, property.offset); + property.uploadFunction(children, startIndex, amount, + property.unsignedByte ? this.staticDataUint32 : this.staticData, + this.staticStride, property.offset); } this.staticBuffer.upload(); diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js index 1b39985..1ae869e 100644 --- a/src/particles/webgl/ParticleRenderer.js +++ b/src/particles/webgl/ParticleRenderer.js @@ -1,6 +1,7 @@ import * as core from '../../core'; import ParticleShader from './ParticleShader'; import ParticleBuffer from './ParticleBuffer'; +import { premultiplyTint } from '../../core/utils'; /** * @author Mat Groves @@ -94,11 +95,12 @@ uploadFunction: this.uploadUvs, offset: 0, }, - // alphaData + // tintData { attribute: this.shader.attributes.aColor, size: 1, - uploadFunction: this.uploadAlpha, + unsignedByte: true, + uploadFunction: this.uploadTint, offset: 0, }, ]; @@ -171,6 +173,15 @@ amount = batchSize; } + if (j >= buffers.length) + { + if (!container.autoResize) + { + break; + } + buffers.push(this._generateOneMoreBuffer(container)); + } + const buffer = buffers[j]; // we always upload the dynamic @@ -212,6 +223,22 @@ } /** + * Creates one more particle buffer, because container has autoResize feature + * + * @param {PIXI.ParticleContainer} container - The container to render using this ParticleRenderer + * @return {PIXI.ParticleBuffer} generated buffer + * @private + */ + _generateOneMoreBuffer(container) + { + const gl = this.renderer.gl; + const batchSize = container._batchSize; + const dynamicPropertyFlags = container._properties; + + return new ParticleBuffer(gl, this.properties, dynamicPropertyFlags, batchSize); + } + + /** * Uploads the verticies. * * @param {PIXI.DisplayObject[]} children - the array of display objects to render @@ -387,16 +414,21 @@ * @param {number} stride - Stride to use for iteration. * @param {number} offset - Offset to start at. */ - uploadAlpha(children, startIndex, amount, array, stride, offset) + uploadTint(children, startIndex, amount, array, stride, offset) { - for (let i = 0; i < amount; i++) + for (let i = 0; i < amount; ++i) { - const spriteAlpha = children[startIndex + i].alpha; + const sprite = children[startIndex + i]; + const premultiplied = sprite._texture.baseTexture.premultipliedAlpha; + const alpha = sprite.alpha; + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && premultiplied ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); - array[offset] = spriteAlpha; - array[offset + stride] = spriteAlpha; - array[offset + (stride * 2)] = spriteAlpha; - array[offset + (stride * 3)] = spriteAlpha; + array[offset] = argb; + array[offset + stride] = argb; + array[offset + (stride * 2)] = argb; + array[offset + (stride * 3)] = argb; offset += stride * 4; } diff --git a/package.json b/package.json index 668d451..1dfeee5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "resource-loader": "^2.0.9" }, "devDependencies": { + "@pixi/jsdoc-template": "^2.0.0", "babel-cli": "^6.18.0", "babel-plugin-static-fs": "^1.1.0", "babel-plugin-version-inline": "^1.0.0", @@ -75,7 +76,6 @@ "electron": "^1.4.15", "eslint": "^3.5.0", "floss": "^2.0.1", - "jaguarjs-jsdoc": "^1.0.1", "js-md5": "^0.4.1", "jsdoc": "3.4.3", "minimist": "^1.2.0", diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json index 8a3afee..2ee888c 100644 --- a/scripts/jsdoc.conf.json +++ b/scripts/jsdoc.conf.json @@ -51,6 +51,6 @@ "private" : false, "lenient" : true, "destination" : "./docs", - "template" : "./node_modules/jaguarjs-jsdoc" + "template" : "./node_modules/@pixi/jsdoc-template" } } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index f2d1c12..e63d2b0 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -127,6 +127,19 @@ */ this.currentRenderer = this.emptyRenderer; + /** + * Manages textures + * @member {PIXI.TextureManager} + */ + this.textureManager = null; + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = null; + this.initPlugins(); /** @@ -178,12 +191,6 @@ this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); // map some webGL blend and drawmodes.. this.drawModes = mapWebGLDrawModesToPixi(this.gl); @@ -236,6 +243,7 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.filterManager = new FilterManager(this); this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); @@ -683,6 +691,7 @@ handleContextRestored() { this.textureManager.removeAll(); + this.filterManager.destroy(true); this._initContext(); } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 7ed28ac..da00800 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -50,6 +50,10 @@ for (const i in this.uniformData) { this.uniforms[i] = this.uniformData[i].value; + if (this.uniformData[i].type) + { + this.uniformData[i].type = this.uniformData[i].type.toLowerCase(); + } } // this is where we store shader references.. diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index 851ad69..f955a9d 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,36 +40,13 @@ // this will map the filter coord so that a texture can be used based on the transform of a sprite export function calculateSpriteMatrix(outputMatrix, filterArea, textureSize, sprite) { - const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); const texture = sprite._texture.baseTexture; - - // TODO unwrap? - const mappedMatrix = outputMatrix.identity(); - - // scale.. - const ratio = textureSize.height / textureSize.width; - - mappedMatrix.translate(filterArea.x / textureSize.width, filterArea.y / textureSize.height); - - mappedMatrix.scale(1, ratio); - - const translateScaleX = (textureSize.width / texture.width); - const translateScaleY = (textureSize.height / texture.height); - - worldTransform.tx /= texture.width * translateScaleX; - - // this...? free beer for anyone who can explain why this makes sense! - worldTransform.ty /= texture.width * translateScaleX; - // worldTransform.ty /= texture.height * translateScaleY; + const mappedMatrix = outputMatrix.set(textureSize.width, 0, 0, textureSize.height, filterArea.x, filterArea.y); + const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); worldTransform.invert(); mappedMatrix.prepend(worldTransform); - - // apply inverse scale.. - mappedMatrix.scale(1, 1 / ratio); - - mappedMatrix.scale(translateScaleX, translateScaleY); - + mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index fdaa5c1..53f2ae1 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -49,6 +49,8 @@ this.pool = {}; this.filterData = null; + + this.managedFilters = []; } /** @@ -232,6 +234,8 @@ shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); } + this.managedFilters.push(filter); + // TODO - this only needs to be done once? renderer.bindVao(null); @@ -327,7 +331,9 @@ // TODO Cacheing layer.. for (const i in uniformData) { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + const type = uniformData[i].type; + + if (type === 'sampler2d' && uniforms[i] !== 0) { if (uniforms[i].baseTexture) { @@ -352,7 +358,7 @@ textureCount++; } - else if (uniformData[i].type === 'mat3') + else if (type === 'mat3') { // check if its PixiJS matrix.. if (uniforms[i].a !== undefined) @@ -364,7 +370,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'vec2') + else if (type === 'vec2') { // check if its a point.. if (uniforms[i].x !== undefined) @@ -380,7 +386,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'float') + else if (type === 'float') { if (shader.uniforms.data[i].value !== uniformData[i]) { @@ -486,11 +492,32 @@ /** * Destroys this Filter Manager. * + * @param {boolean} [contextLost=false] context was lost, do not free shaders + * */ - destroy() + destroy(contextLost = false) { + const renderer = this.renderer; + const filters = this.managedFilters; + + for (let i = 0; i < filters.length; i++) + { + if (!contextLost) + { + filters[i].glShaders[renderer.CONTEXT_UID].destroy(); + } + delete filters[i].glShaders[renderer.CONTEXT_UID]; + } + this.shaderCache = {}; - this.emptyPool(); + if (!contextLost) + { + this.emptyPool(); + } + else + { + this.pool = {}; + } } /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 0aeda37..dad38fb 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -143,6 +143,7 @@ { this._textureID = -1; this._textureTrimmedID = -1; + this.cachedTint = 0xFFFFFF; // so if _width is 0 then width was not set.. if (this._width) diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 85abccf..1a9223f 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -151,6 +151,7 @@ { let frameIndex = initialFrameIndex; const maxFrames = Spritesheet.BATCH_SIZE; + const sourceScale = this.baseTexture.sourceScale; while (frameIndex - initialFrameIndex < maxFrames && frameIndex < this._frameKeys.length) { @@ -164,26 +165,26 @@ const orig = new Rectangle( 0, 0, - this._frames[i].sourceSize.w / this.resolution, - this._frames[i].sourceSize.h / this.resolution + Math.floor(this._frames[i].sourceSize.w * sourceScale) / this.resolution, + Math.floor(this._frames[i].sourceSize.h * sourceScale) / this.resolution ); if (this._frames[i].rotated) { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.h / this.resolution, - rect.w / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution ); } else { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } @@ -191,10 +192,10 @@ if (this._frames[i].trimmed) { trim = new Rectangle( - this._frames[i].spriteSourceSize.x / this.resolution, - this._frames[i].spriteSourceSize.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(this._frames[i].spriteSourceSize.x * sourceScale) / this.resolution, + Math.floor(this._frames[i].spriteSourceSize.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 3a472be..75eb069 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -552,15 +552,22 @@ this.noFrame = false; - if (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height) + const { x, y, width, height } = frame; + const xNotFit = x + width > this.baseTexture.width; + const yNotFit = y + height > this.baseTexture.height; + + if (xNotFit || yNotFit) { + const relationship = xNotFit && yNotFit ? 'and' : 'or'; + const errorX = `X: ${x} + ${width} = ${x + width} > ${this.baseTexture.width}`; + const errorY = `Y: ${y} + ${height} = ${y + height} > ${this.baseTexture.height}`; + throw new Error('Texture Error: frame does not fit inside the base Texture dimensions: ' - + `X: ${frame.x} + ${frame.width} = ${frame.x + frame.width} > ${this.baseTexture.width} ` - + `Y: ${frame.y} + ${frame.height} = ${frame.y + frame.height} > ${this.baseTexture.height}`); + + `${errorX} ${relationship} ${errorY}`); } - // this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded; - this.valid = frame && frame.width && frame.height && this.baseTexture.hasLoaded; + // this.valid = width && height && this.baseTexture.source && this.baseTexture.hasLoaded; + this.valid = width && height && this.baseTexture.hasLoaded; if (!this.trim && !this.rotate) { diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index 1754e0e..c605fa8 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -271,6 +271,7 @@ { this._texture = this._textures[this.currentFrame]; this._textureID = -1; + this.cachedTint = 0xFFFFFF; if (this.onFrameChange) { diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 6282341..736b207 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -136,6 +136,7 @@ { this.uvTransform.texture = this._texture; } + this.cachedTint = 0xFFFFFF; } /** @@ -185,9 +186,9 @@ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution; // create a nice shiny pattern! - // TODO this needs to be refreshed if texture changes.. - if (!this._canvasPattern) + if (this._textureID !== this._texture._updateID || this.cachedTint !== this.tint) { + this._textureID = this._texture._updateID; // cut an object from a spritesheet.. const tempCanvas = new core.CanvasRenderTarget(texture._frame.width, texture._frame.height, @@ -196,12 +197,7 @@ // Tint the tiling sprite if (this.tint !== 0xFFFFFF) { - if (this.cachedTint !== this.tint) - { - this.cachedTint = this.tint; - - this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); - } + this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); tempCanvas.context.drawImage(this.tintedTexture, 0, 0); } else @@ -209,6 +205,7 @@ tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } + this.cachedTint = this.tint; this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js index 297e460..61ba5ee 100644 --- a/src/mesh/NineSlicePlane.js +++ b/src/mesh/NineSlicePlane.js @@ -363,7 +363,7 @@ this.updateHorizontalVertices(); this.updateVerticalVertices(); - this.dirty = true; + this.dirty++; this.multiplyUvs(); } diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js index 4f9b51c..4dd2398 100644 --- a/src/mesh/Plane.js +++ b/src/mesh/Plane.js @@ -94,7 +94,7 @@ this.uvs = new Float32Array(uvs); this.colors = new Float32Array(colors); this.indices = new Uint16Array(indices); - this.indexDirty = true; + this.indexDirty++; this.multiplyUvs(); } diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index 1c725d2..233ee3b 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -28,16 +28,19 @@ export default class ParticleContainer extends core.Container { /** - * @param {number} [maxSize=15000] - The maximum number of particles that can be renderer by the container. + * @param {number} [maxSize=1500] - The maximum number of particles that can be rendered by the container. + * Affects size of allocated buffers. * @param {object} [properties] - The properties of children that should be uploaded to the gpu and applied. * @param {boolean} [properties.scale=false] - When true, scale be uploaded and applied. * @param {boolean} [properties.position=true] - When true, position be uploaded and applied. * @param {boolean} [properties.rotation=false] - When true, rotation be uploaded and applied. * @param {boolean} [properties.uvs=false] - When true, uvs be uploaded and applied. - * @param {boolean} [properties.alpha=false] - When true, alpha be uploaded and applied. - * @param {number} [batchSize=15000] - Number of particles per batch. + * @param {boolean} [properties.tint=false] - When true, alpha and tint be uploaded and applied. + * @param {number} [batchSize=16384] - Number of particles per batch. If less than maxSize, it uses maxSize instead. + * @param {boolean} [autoResize=true] If true, container allocates more batches in case + * there are more than `maxSize` particles. */ - constructor(maxSize = 1500, properties, batchSize = 16384) + constructor(maxSize = 1500, properties, batchSize = 16384, autoResize = false) { super(); @@ -105,6 +108,13 @@ this.blendMode = core.BLEND_MODES.NORMAL; /** + * If true, container allocates more batches in case there are more than `maxSize` particles. + * @member {boolean} + * @default false + */ + this.autoResize = autoResize; + + /** * Used for canvas renderering. If true then the elements will be positioned at the * nearest pixel. This provides a nice speed boost. * @@ -149,7 +159,8 @@ this._properties[1] = 'position' in properties ? !!properties.position : this._properties[1]; this._properties[2] = 'rotation' in properties ? !!properties.rotation : this._properties[2]; this._properties[3] = 'uvs' in properties ? !!properties.uvs : this._properties[3]; - this._properties[4] = 'alpha' in properties ? !!properties.alpha : this._properties[4]; + this._properties[4] = 'alpha' in properties || 'tint' in properties + ? !!properties.alpha || !!properties.tint : this._properties[4]; } } diff --git a/src/particles/webgl/ParticleBuffer.js b/src/particles/webgl/ParticleBuffer.js index bd8e243..0d41f66 100644 --- a/src/particles/webgl/ParticleBuffer.js +++ b/src/particles/webgl/ParticleBuffer.js @@ -38,20 +38,6 @@ this.gl = gl; /** - * Size of a single vertex. - * - * @member {number} - */ - this.vertSize = 2; - - /** - * Size of a single vertex in bytes. - * - * @member {number} - */ - this.vertByteSize = this.vertSize * 4; - - /** * The number of particles the buffer can hold * * @member {number} @@ -82,6 +68,7 @@ attribute: property.attribute, size: property.size, uploadFunction: property.uploadFunction, + unsignedByte: property.unsignedByte, offset: property.offset, }; @@ -98,10 +85,12 @@ this.staticStride = 0; this.staticBuffer = null; this.staticData = null; + this.staticDataUint32 = null; this.dynamicStride = 0; this.dynamicBuffer = null; this.dynamicData = null; + this.dynamicDataUint32 = null; this.initBuffers(); } @@ -135,8 +124,11 @@ this.dynamicStride += property.size; } - this.dynamicData = new Float32Array(this.size * this.dynamicStride * 4); - this.dynamicBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.dynamicData, gl.STREAM_DRAW); + const dynBuffer = new ArrayBuffer(this.size * this.dynamicStride * 4 * 4); + + this.dynamicData = new Float32Array(dynBuffer); + this.dynamicDataUint32 = new Uint32Array(dynBuffer); + this.dynamicBuffer = glCore.GLBuffer.createVertexBuffer(gl, dynBuffer, gl.STREAM_DRAW); // static // let staticOffset = 0; @@ -152,8 +144,11 @@ this.staticStride += property.size; } - this.staticData = new Float32Array(this.size * this.staticStride * 4); - this.staticBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.staticData, gl.STATIC_DRAW); + const statBuffer = new ArrayBuffer(this.size * this.staticStride * 4 * 4); + + this.staticData = new Float32Array(statBuffer); + this.staticDataUint32 = new Uint32Array(statBuffer); + this.staticBuffer = glCore.GLBuffer.createVertexBuffer(gl, statBuffer, gl.STATIC_DRAW); this.vao = new glCore.VertexArrayObject(gl) .addIndex(this.indexBuffer); @@ -162,28 +157,56 @@ { const property = this.dynamicProperties[i]; - this.vao.addAttribute( - this.dynamicBuffer, - property.attribute, - gl.FLOAT, - false, - this.dynamicStride * 4, - property.offset * 4 - ); + if (property.unsignedByte) + { + this.vao.addAttribute( + this.dynamicBuffer, + property.attribute, + gl.UNSIGNED_BYTE, + true, + this.dynamicStride * 4, + property.offset * 4 + ); + } + else + { + this.vao.addAttribute( + this.dynamicBuffer, + property.attribute, + gl.FLOAT, + false, + this.dynamicStride * 4, + property.offset * 4 + ); + } } for (let i = 0; i < this.staticProperties.length; ++i) { const property = this.staticProperties[i]; - this.vao.addAttribute( - this.staticBuffer, - property.attribute, - gl.FLOAT, - false, - this.staticStride * 4, - property.offset * 4 - ); + if (property.unsignedByte) + { + this.vao.addAttribute( + this.staticBuffer, + property.attribute, + gl.UNSIGNED_BYTE, + true, + this.staticStride * 4, + property.offset * 4 + ); + } + else + { + this.vao.addAttribute( + this.staticBuffer, + property.attribute, + gl.FLOAT, + false, + this.staticStride * 4, + property.offset * 4 + ); + } } } @@ -200,7 +223,9 @@ { const property = this.dynamicProperties[i]; - property.uploadFunction(children, startIndex, amount, this.dynamicData, this.dynamicStride, property.offset); + property.uploadFunction(children, startIndex, amount, + property.unsignedByte ? this.dynamicDataUint32 : this.dynamicData, + this.dynamicStride, property.offset); } this.dynamicBuffer.upload(); @@ -219,7 +244,9 @@ { const property = this.staticProperties[i]; - property.uploadFunction(children, startIndex, amount, this.staticData, this.staticStride, property.offset); + property.uploadFunction(children, startIndex, amount, + property.unsignedByte ? this.staticDataUint32 : this.staticData, + this.staticStride, property.offset); } this.staticBuffer.upload(); diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js index 1b39985..1ae869e 100644 --- a/src/particles/webgl/ParticleRenderer.js +++ b/src/particles/webgl/ParticleRenderer.js @@ -1,6 +1,7 @@ import * as core from '../../core'; import ParticleShader from './ParticleShader'; import ParticleBuffer from './ParticleBuffer'; +import { premultiplyTint } from '../../core/utils'; /** * @author Mat Groves @@ -94,11 +95,12 @@ uploadFunction: this.uploadUvs, offset: 0, }, - // alphaData + // tintData { attribute: this.shader.attributes.aColor, size: 1, - uploadFunction: this.uploadAlpha, + unsignedByte: true, + uploadFunction: this.uploadTint, offset: 0, }, ]; @@ -171,6 +173,15 @@ amount = batchSize; } + if (j >= buffers.length) + { + if (!container.autoResize) + { + break; + } + buffers.push(this._generateOneMoreBuffer(container)); + } + const buffer = buffers[j]; // we always upload the dynamic @@ -212,6 +223,22 @@ } /** + * Creates one more particle buffer, because container has autoResize feature + * + * @param {PIXI.ParticleContainer} container - The container to render using this ParticleRenderer + * @return {PIXI.ParticleBuffer} generated buffer + * @private + */ + _generateOneMoreBuffer(container) + { + const gl = this.renderer.gl; + const batchSize = container._batchSize; + const dynamicPropertyFlags = container._properties; + + return new ParticleBuffer(gl, this.properties, dynamicPropertyFlags, batchSize); + } + + /** * Uploads the verticies. * * @param {PIXI.DisplayObject[]} children - the array of display objects to render @@ -387,16 +414,21 @@ * @param {number} stride - Stride to use for iteration. * @param {number} offset - Offset to start at. */ - uploadAlpha(children, startIndex, amount, array, stride, offset) + uploadTint(children, startIndex, amount, array, stride, offset) { - for (let i = 0; i < amount; i++) + for (let i = 0; i < amount; ++i) { - const spriteAlpha = children[startIndex + i].alpha; + const sprite = children[startIndex + i]; + const premultiplied = sprite._texture.baseTexture.premultipliedAlpha; + const alpha = sprite.alpha; + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && premultiplied ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); - array[offset] = spriteAlpha; - array[offset + stride] = spriteAlpha; - array[offset + (stride * 2)] = spriteAlpha; - array[offset + (stride * 3)] = spriteAlpha; + array[offset] = argb; + array[offset + stride] = argb; + array[offset + (stride * 2)] = argb; + array[offset + (stride * 3)] = argb; offset += stride * 4; } diff --git a/src/particles/webgl/ParticleShader.js b/src/particles/webgl/ParticleShader.js index 4361623..b3584fe 100644 --- a/src/particles/webgl/ParticleShader.js +++ b/src/particles/webgl/ParticleShader.js @@ -18,16 +18,17 @@ [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', + 'attribute vec4 aColor;', 'attribute vec2 aPositionCoord;', 'attribute vec2 aScale;', 'attribute float aRotation;', 'uniform mat3 projectionMatrix;', + 'uniform vec4 uColor;', 'varying vec2 vTextureCoord;', - 'varying float vColor;', + 'varying vec4 vColor;', 'void main(void){', ' vec2 v = aVertexPosition;', @@ -39,19 +40,18 @@ ' gl_Position = vec4((projectionMatrix * vec3(v, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', - ' vColor = aColor;', + ' vColor = aColor * uColor;', '}', ].join('\n'), // hello [ 'varying vec2 vTextureCoord;', - 'varying float vColor;', + 'varying vec4 vColor;', 'uniform sampler2D uSampler;', - 'uniform vec4 uColor;', 'void main(void){', - ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * uColor;', + ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor;', ' if (color.a == 0.0) discard;', ' gl_FragColor = color;', '}', diff --git a/package.json b/package.json index 668d451..1dfeee5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "resource-loader": "^2.0.9" }, "devDependencies": { + "@pixi/jsdoc-template": "^2.0.0", "babel-cli": "^6.18.0", "babel-plugin-static-fs": "^1.1.0", "babel-plugin-version-inline": "^1.0.0", @@ -75,7 +76,6 @@ "electron": "^1.4.15", "eslint": "^3.5.0", "floss": "^2.0.1", - "jaguarjs-jsdoc": "^1.0.1", "js-md5": "^0.4.1", "jsdoc": "3.4.3", "minimist": "^1.2.0", diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json index 8a3afee..2ee888c 100644 --- a/scripts/jsdoc.conf.json +++ b/scripts/jsdoc.conf.json @@ -51,6 +51,6 @@ "private" : false, "lenient" : true, "destination" : "./docs", - "template" : "./node_modules/jaguarjs-jsdoc" + "template" : "./node_modules/@pixi/jsdoc-template" } } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index f2d1c12..e63d2b0 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -127,6 +127,19 @@ */ this.currentRenderer = this.emptyRenderer; + /** + * Manages textures + * @member {PIXI.TextureManager} + */ + this.textureManager = null; + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = null; + this.initPlugins(); /** @@ -178,12 +191,6 @@ this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); // map some webGL blend and drawmodes.. this.drawModes = mapWebGLDrawModesToPixi(this.gl); @@ -236,6 +243,7 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.filterManager = new FilterManager(this); this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); @@ -683,6 +691,7 @@ handleContextRestored() { this.textureManager.removeAll(); + this.filterManager.destroy(true); this._initContext(); } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 7ed28ac..da00800 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -50,6 +50,10 @@ for (const i in this.uniformData) { this.uniforms[i] = this.uniformData[i].value; + if (this.uniformData[i].type) + { + this.uniformData[i].type = this.uniformData[i].type.toLowerCase(); + } } // this is where we store shader references.. diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index 851ad69..f955a9d 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,36 +40,13 @@ // this will map the filter coord so that a texture can be used based on the transform of a sprite export function calculateSpriteMatrix(outputMatrix, filterArea, textureSize, sprite) { - const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); const texture = sprite._texture.baseTexture; - - // TODO unwrap? - const mappedMatrix = outputMatrix.identity(); - - // scale.. - const ratio = textureSize.height / textureSize.width; - - mappedMatrix.translate(filterArea.x / textureSize.width, filterArea.y / textureSize.height); - - mappedMatrix.scale(1, ratio); - - const translateScaleX = (textureSize.width / texture.width); - const translateScaleY = (textureSize.height / texture.height); - - worldTransform.tx /= texture.width * translateScaleX; - - // this...? free beer for anyone who can explain why this makes sense! - worldTransform.ty /= texture.width * translateScaleX; - // worldTransform.ty /= texture.height * translateScaleY; + const mappedMatrix = outputMatrix.set(textureSize.width, 0, 0, textureSize.height, filterArea.x, filterArea.y); + const worldTransform = sprite.worldTransform.copy(Matrix.TEMP_MATRIX); worldTransform.invert(); mappedMatrix.prepend(worldTransform); - - // apply inverse scale.. - mappedMatrix.scale(1, 1 / ratio); - - mappedMatrix.scale(translateScaleX, translateScaleY); - + mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index fdaa5c1..53f2ae1 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -49,6 +49,8 @@ this.pool = {}; this.filterData = null; + + this.managedFilters = []; } /** @@ -232,6 +234,8 @@ shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); } + this.managedFilters.push(filter); + // TODO - this only needs to be done once? renderer.bindVao(null); @@ -327,7 +331,9 @@ // TODO Cacheing layer.. for (const i in uniformData) { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + const type = uniformData[i].type; + + if (type === 'sampler2d' && uniforms[i] !== 0) { if (uniforms[i].baseTexture) { @@ -352,7 +358,7 @@ textureCount++; } - else if (uniformData[i].type === 'mat3') + else if (type === 'mat3') { // check if its PixiJS matrix.. if (uniforms[i].a !== undefined) @@ -364,7 +370,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'vec2') + else if (type === 'vec2') { // check if its a point.. if (uniforms[i].x !== undefined) @@ -380,7 +386,7 @@ shader.uniforms[i] = uniforms[i]; } } - else if (uniformData[i].type === 'float') + else if (type === 'float') { if (shader.uniforms.data[i].value !== uniformData[i]) { @@ -486,11 +492,32 @@ /** * Destroys this Filter Manager. * + * @param {boolean} [contextLost=false] context was lost, do not free shaders + * */ - destroy() + destroy(contextLost = false) { + const renderer = this.renderer; + const filters = this.managedFilters; + + for (let i = 0; i < filters.length; i++) + { + if (!contextLost) + { + filters[i].glShaders[renderer.CONTEXT_UID].destroy(); + } + delete filters[i].glShaders[renderer.CONTEXT_UID]; + } + this.shaderCache = {}; - this.emptyPool(); + if (!contextLost) + { + this.emptyPool(); + } + else + { + this.pool = {}; + } } /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 0aeda37..dad38fb 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -143,6 +143,7 @@ { this._textureID = -1; this._textureTrimmedID = -1; + this.cachedTint = 0xFFFFFF; // so if _width is 0 then width was not set.. if (this._width) diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 85abccf..1a9223f 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -151,6 +151,7 @@ { let frameIndex = initialFrameIndex; const maxFrames = Spritesheet.BATCH_SIZE; + const sourceScale = this.baseTexture.sourceScale; while (frameIndex - initialFrameIndex < maxFrames && frameIndex < this._frameKeys.length) { @@ -164,26 +165,26 @@ const orig = new Rectangle( 0, 0, - this._frames[i].sourceSize.w / this.resolution, - this._frames[i].sourceSize.h / this.resolution + Math.floor(this._frames[i].sourceSize.w * sourceScale) / this.resolution, + Math.floor(this._frames[i].sourceSize.h * sourceScale) / this.resolution ); if (this._frames[i].rotated) { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.h / this.resolution, - rect.w / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution ); } else { frame = new Rectangle( - rect.x / this.resolution, - rect.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(rect.x * sourceScale) / this.resolution, + Math.floor(rect.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } @@ -191,10 +192,10 @@ if (this._frames[i].trimmed) { trim = new Rectangle( - this._frames[i].spriteSourceSize.x / this.resolution, - this._frames[i].spriteSourceSize.y / this.resolution, - rect.w / this.resolution, - rect.h / this.resolution + Math.floor(this._frames[i].spriteSourceSize.x * sourceScale) / this.resolution, + Math.floor(this._frames[i].spriteSourceSize.y * sourceScale) / this.resolution, + Math.floor(rect.w * sourceScale) / this.resolution, + Math.floor(rect.h * sourceScale) / this.resolution ); } diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 3a472be..75eb069 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -552,15 +552,22 @@ this.noFrame = false; - if (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height) + const { x, y, width, height } = frame; + const xNotFit = x + width > this.baseTexture.width; + const yNotFit = y + height > this.baseTexture.height; + + if (xNotFit || yNotFit) { + const relationship = xNotFit && yNotFit ? 'and' : 'or'; + const errorX = `X: ${x} + ${width} = ${x + width} > ${this.baseTexture.width}`; + const errorY = `Y: ${y} + ${height} = ${y + height} > ${this.baseTexture.height}`; + throw new Error('Texture Error: frame does not fit inside the base Texture dimensions: ' - + `X: ${frame.x} + ${frame.width} = ${frame.x + frame.width} > ${this.baseTexture.width} ` - + `Y: ${frame.y} + ${frame.height} = ${frame.y + frame.height} > ${this.baseTexture.height}`); + + `${errorX} ${relationship} ${errorY}`); } - // this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded; - this.valid = frame && frame.width && frame.height && this.baseTexture.hasLoaded; + // this.valid = width && height && this.baseTexture.source && this.baseTexture.hasLoaded; + this.valid = width && height && this.baseTexture.hasLoaded; if (!this.trim && !this.rotate) { diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index 1754e0e..c605fa8 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -271,6 +271,7 @@ { this._texture = this._textures[this.currentFrame]; this._textureID = -1; + this.cachedTint = 0xFFFFFF; if (this.onFrameChange) { diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 6282341..736b207 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -136,6 +136,7 @@ { this.uvTransform.texture = this._texture; } + this.cachedTint = 0xFFFFFF; } /** @@ -185,9 +186,9 @@ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution; // create a nice shiny pattern! - // TODO this needs to be refreshed if texture changes.. - if (!this._canvasPattern) + if (this._textureID !== this._texture._updateID || this.cachedTint !== this.tint) { + this._textureID = this._texture._updateID; // cut an object from a spritesheet.. const tempCanvas = new core.CanvasRenderTarget(texture._frame.width, texture._frame.height, @@ -196,12 +197,7 @@ // Tint the tiling sprite if (this.tint !== 0xFFFFFF) { - if (this.cachedTint !== this.tint) - { - this.cachedTint = this.tint; - - this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); - } + this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); tempCanvas.context.drawImage(this.tintedTexture, 0, 0); } else @@ -209,6 +205,7 @@ tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } + this.cachedTint = this.tint; this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js index 297e460..61ba5ee 100644 --- a/src/mesh/NineSlicePlane.js +++ b/src/mesh/NineSlicePlane.js @@ -363,7 +363,7 @@ this.updateHorizontalVertices(); this.updateVerticalVertices(); - this.dirty = true; + this.dirty++; this.multiplyUvs(); } diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js index 4f9b51c..4dd2398 100644 --- a/src/mesh/Plane.js +++ b/src/mesh/Plane.js @@ -94,7 +94,7 @@ this.uvs = new Float32Array(uvs); this.colors = new Float32Array(colors); this.indices = new Uint16Array(indices); - this.indexDirty = true; + this.indexDirty++; this.multiplyUvs(); } diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index 1c725d2..233ee3b 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -28,16 +28,19 @@ export default class ParticleContainer extends core.Container { /** - * @param {number} [maxSize=15000] - The maximum number of particles that can be renderer by the container. + * @param {number} [maxSize=1500] - The maximum number of particles that can be rendered by the container. + * Affects size of allocated buffers. * @param {object} [properties] - The properties of children that should be uploaded to the gpu and applied. * @param {boolean} [properties.scale=false] - When true, scale be uploaded and applied. * @param {boolean} [properties.position=true] - When true, position be uploaded and applied. * @param {boolean} [properties.rotation=false] - When true, rotation be uploaded and applied. * @param {boolean} [properties.uvs=false] - When true, uvs be uploaded and applied. - * @param {boolean} [properties.alpha=false] - When true, alpha be uploaded and applied. - * @param {number} [batchSize=15000] - Number of particles per batch. + * @param {boolean} [properties.tint=false] - When true, alpha and tint be uploaded and applied. + * @param {number} [batchSize=16384] - Number of particles per batch. If less than maxSize, it uses maxSize instead. + * @param {boolean} [autoResize=true] If true, container allocates more batches in case + * there are more than `maxSize` particles. */ - constructor(maxSize = 1500, properties, batchSize = 16384) + constructor(maxSize = 1500, properties, batchSize = 16384, autoResize = false) { super(); @@ -105,6 +108,13 @@ this.blendMode = core.BLEND_MODES.NORMAL; /** + * If true, container allocates more batches in case there are more than `maxSize` particles. + * @member {boolean} + * @default false + */ + this.autoResize = autoResize; + + /** * Used for canvas renderering. If true then the elements will be positioned at the * nearest pixel. This provides a nice speed boost. * @@ -149,7 +159,8 @@ this._properties[1] = 'position' in properties ? !!properties.position : this._properties[1]; this._properties[2] = 'rotation' in properties ? !!properties.rotation : this._properties[2]; this._properties[3] = 'uvs' in properties ? !!properties.uvs : this._properties[3]; - this._properties[4] = 'alpha' in properties ? !!properties.alpha : this._properties[4]; + this._properties[4] = 'alpha' in properties || 'tint' in properties + ? !!properties.alpha || !!properties.tint : this._properties[4]; } } diff --git a/src/particles/webgl/ParticleBuffer.js b/src/particles/webgl/ParticleBuffer.js index bd8e243..0d41f66 100644 --- a/src/particles/webgl/ParticleBuffer.js +++ b/src/particles/webgl/ParticleBuffer.js @@ -38,20 +38,6 @@ this.gl = gl; /** - * Size of a single vertex. - * - * @member {number} - */ - this.vertSize = 2; - - /** - * Size of a single vertex in bytes. - * - * @member {number} - */ - this.vertByteSize = this.vertSize * 4; - - /** * The number of particles the buffer can hold * * @member {number} @@ -82,6 +68,7 @@ attribute: property.attribute, size: property.size, uploadFunction: property.uploadFunction, + unsignedByte: property.unsignedByte, offset: property.offset, }; @@ -98,10 +85,12 @@ this.staticStride = 0; this.staticBuffer = null; this.staticData = null; + this.staticDataUint32 = null; this.dynamicStride = 0; this.dynamicBuffer = null; this.dynamicData = null; + this.dynamicDataUint32 = null; this.initBuffers(); } @@ -135,8 +124,11 @@ this.dynamicStride += property.size; } - this.dynamicData = new Float32Array(this.size * this.dynamicStride * 4); - this.dynamicBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.dynamicData, gl.STREAM_DRAW); + const dynBuffer = new ArrayBuffer(this.size * this.dynamicStride * 4 * 4); + + this.dynamicData = new Float32Array(dynBuffer); + this.dynamicDataUint32 = new Uint32Array(dynBuffer); + this.dynamicBuffer = glCore.GLBuffer.createVertexBuffer(gl, dynBuffer, gl.STREAM_DRAW); // static // let staticOffset = 0; @@ -152,8 +144,11 @@ this.staticStride += property.size; } - this.staticData = new Float32Array(this.size * this.staticStride * 4); - this.staticBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.staticData, gl.STATIC_DRAW); + const statBuffer = new ArrayBuffer(this.size * this.staticStride * 4 * 4); + + this.staticData = new Float32Array(statBuffer); + this.staticDataUint32 = new Uint32Array(statBuffer); + this.staticBuffer = glCore.GLBuffer.createVertexBuffer(gl, statBuffer, gl.STATIC_DRAW); this.vao = new glCore.VertexArrayObject(gl) .addIndex(this.indexBuffer); @@ -162,28 +157,56 @@ { const property = this.dynamicProperties[i]; - this.vao.addAttribute( - this.dynamicBuffer, - property.attribute, - gl.FLOAT, - false, - this.dynamicStride * 4, - property.offset * 4 - ); + if (property.unsignedByte) + { + this.vao.addAttribute( + this.dynamicBuffer, + property.attribute, + gl.UNSIGNED_BYTE, + true, + this.dynamicStride * 4, + property.offset * 4 + ); + } + else + { + this.vao.addAttribute( + this.dynamicBuffer, + property.attribute, + gl.FLOAT, + false, + this.dynamicStride * 4, + property.offset * 4 + ); + } } for (let i = 0; i < this.staticProperties.length; ++i) { const property = this.staticProperties[i]; - this.vao.addAttribute( - this.staticBuffer, - property.attribute, - gl.FLOAT, - false, - this.staticStride * 4, - property.offset * 4 - ); + if (property.unsignedByte) + { + this.vao.addAttribute( + this.staticBuffer, + property.attribute, + gl.UNSIGNED_BYTE, + true, + this.staticStride * 4, + property.offset * 4 + ); + } + else + { + this.vao.addAttribute( + this.staticBuffer, + property.attribute, + gl.FLOAT, + false, + this.staticStride * 4, + property.offset * 4 + ); + } } } @@ -200,7 +223,9 @@ { const property = this.dynamicProperties[i]; - property.uploadFunction(children, startIndex, amount, this.dynamicData, this.dynamicStride, property.offset); + property.uploadFunction(children, startIndex, amount, + property.unsignedByte ? this.dynamicDataUint32 : this.dynamicData, + this.dynamicStride, property.offset); } this.dynamicBuffer.upload(); @@ -219,7 +244,9 @@ { const property = this.staticProperties[i]; - property.uploadFunction(children, startIndex, amount, this.staticData, this.staticStride, property.offset); + property.uploadFunction(children, startIndex, amount, + property.unsignedByte ? this.staticDataUint32 : this.staticData, + this.staticStride, property.offset); } this.staticBuffer.upload(); diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js index 1b39985..1ae869e 100644 --- a/src/particles/webgl/ParticleRenderer.js +++ b/src/particles/webgl/ParticleRenderer.js @@ -1,6 +1,7 @@ import * as core from '../../core'; import ParticleShader from './ParticleShader'; import ParticleBuffer from './ParticleBuffer'; +import { premultiplyTint } from '../../core/utils'; /** * @author Mat Groves @@ -94,11 +95,12 @@ uploadFunction: this.uploadUvs, offset: 0, }, - // alphaData + // tintData { attribute: this.shader.attributes.aColor, size: 1, - uploadFunction: this.uploadAlpha, + unsignedByte: true, + uploadFunction: this.uploadTint, offset: 0, }, ]; @@ -171,6 +173,15 @@ amount = batchSize; } + if (j >= buffers.length) + { + if (!container.autoResize) + { + break; + } + buffers.push(this._generateOneMoreBuffer(container)); + } + const buffer = buffers[j]; // we always upload the dynamic @@ -212,6 +223,22 @@ } /** + * Creates one more particle buffer, because container has autoResize feature + * + * @param {PIXI.ParticleContainer} container - The container to render using this ParticleRenderer + * @return {PIXI.ParticleBuffer} generated buffer + * @private + */ + _generateOneMoreBuffer(container) + { + const gl = this.renderer.gl; + const batchSize = container._batchSize; + const dynamicPropertyFlags = container._properties; + + return new ParticleBuffer(gl, this.properties, dynamicPropertyFlags, batchSize); + } + + /** * Uploads the verticies. * * @param {PIXI.DisplayObject[]} children - the array of display objects to render @@ -387,16 +414,21 @@ * @param {number} stride - Stride to use for iteration. * @param {number} offset - Offset to start at. */ - uploadAlpha(children, startIndex, amount, array, stride, offset) + uploadTint(children, startIndex, amount, array, stride, offset) { - for (let i = 0; i < amount; i++) + for (let i = 0; i < amount; ++i) { - const spriteAlpha = children[startIndex + i].alpha; + const sprite = children[startIndex + i]; + const premultiplied = sprite._texture.baseTexture.premultipliedAlpha; + const alpha = sprite.alpha; + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && premultiplied ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); - array[offset] = spriteAlpha; - array[offset + stride] = spriteAlpha; - array[offset + (stride * 2)] = spriteAlpha; - array[offset + (stride * 3)] = spriteAlpha; + array[offset] = argb; + array[offset + stride] = argb; + array[offset + (stride * 2)] = argb; + array[offset + (stride * 3)] = argb; offset += stride * 4; } diff --git a/src/particles/webgl/ParticleShader.js b/src/particles/webgl/ParticleShader.js index 4361623..b3584fe 100644 --- a/src/particles/webgl/ParticleShader.js +++ b/src/particles/webgl/ParticleShader.js @@ -18,16 +18,17 @@ [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', + 'attribute vec4 aColor;', 'attribute vec2 aPositionCoord;', 'attribute vec2 aScale;', 'attribute float aRotation;', 'uniform mat3 projectionMatrix;', + 'uniform vec4 uColor;', 'varying vec2 vTextureCoord;', - 'varying float vColor;', + 'varying vec4 vColor;', 'void main(void){', ' vec2 v = aVertexPosition;', @@ -39,19 +40,18 @@ ' gl_Position = vec4((projectionMatrix * vec3(v, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', - ' vColor = aColor;', + ' vColor = aColor * uColor;', '}', ].join('\n'), // hello [ 'varying vec2 vTextureCoord;', - 'varying float vColor;', + 'varying vec4 vColor;', 'uniform sampler2D uSampler;', - 'uniform vec4 uColor;', 'void main(void){', - ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * uColor;', + ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor;', ' if (color.a == 0.0) discard;', ' gl_FragColor = color;', '}', diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js index 7a3d6ac..dccde04 100644 --- a/test/core/Spritesheet.js +++ b/test/core/Spritesheet.js @@ -12,12 +12,14 @@ spritesheet.parse(function (textures) { const id = 'goldmine_10_5.png'; + const width = Math.floor(spritesheet.data.frames[id].frame.w * spritesheet.baseTexture.sourceScale); + const height = Math.floor(spritesheet.data.frames[id].frame.h * spritesheet.baseTexture.sourceScale); expect(Object.keys(textures).length).to.equal(1); expect(Object.keys(spritesheet.textures).length).to.equal(1); expect(textures[id]).to.be.an.instanceof(PIXI.Texture); - expect(textures[id].width).to.equal(spritesheet.data.frames[id].frame.w / spritesheet.resolution); - expect(textures[id].height).to.equal(spritesheet.data.frames[id].frame.h / spritesheet.resolution); + expect(textures[id].width).to.equal(width / spritesheet.resolution); + expect(textures[id].height).to.equal(height / spritesheet.resolution); expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0); spritesheet.destroy(true); expect(spritesheet.textures).to.be.null; @@ -68,6 +70,19 @@ }; }); + it('should create instance with BaseTexture source scale', function (done) + { + const data = require(path.resolve(this.resources, 'building1.json')); // eslint-disable-line global-require + const baseTexture = new PIXI.BaseTexture.fromImage(data.meta.image, undefined, undefined, 1.5); + const spritesheet = new PIXI.Spritesheet(baseTexture, data); + + expect(data).to.be.an.object; + expect(data.meta.image).to.equal('building1.png'); + expect(spritesheet.resolution).to.equal(0.5); + + this.validate(spritesheet, done); + }); + it('should create instance with filename resolution', function (done) { const uri = path.resolve(this.resources, 'building1@2x.json');