diff --git a/src/core/index.js b/src/core/index.js index 3265dfb..1a92622 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -33,6 +33,7 @@ export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Spritesheet } from './textures/Spritesheet'; export { default as Texture } from './textures/Texture'; +export { default as TextureMatrix } from './textures/TextureMatrix'; export { default as BaseTexture } from './textures/BaseTexture'; export { default as RenderTexture } from './textures/RenderTexture'; export { default as BaseRenderTexture } from './textures/BaseRenderTexture'; diff --git a/src/core/index.js b/src/core/index.js index 3265dfb..1a92622 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -33,6 +33,7 @@ export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Spritesheet } from './textures/Spritesheet'; export { default as Texture } from './textures/Texture'; +export { default as TextureMatrix } from './textures/TextureMatrix'; export { default as BaseTexture } from './textures/BaseTexture'; export { default as RenderTexture } from './textures/RenderTexture'; export { default as BaseRenderTexture } from './textures/BaseRenderTexture'; diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index f955a9d..2d08dbc 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,13 +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 texture = sprite._texture.baseTexture; + const orig = sprite._texture.orig; 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); - mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); + mappedMatrix.scale(1.0 / orig.width, 1.0 / orig.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/index.js b/src/core/index.js index 3265dfb..1a92622 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -33,6 +33,7 @@ export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Spritesheet } from './textures/Spritesheet'; export { default as Texture } from './textures/Texture'; +export { default as TextureMatrix } from './textures/TextureMatrix'; export { default as BaseTexture } from './textures/BaseTexture'; export { default as RenderTexture } from './textures/RenderTexture'; export { default as BaseRenderTexture } from './textures/BaseRenderTexture'; diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index f955a9d..2d08dbc 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,13 +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 texture = sprite._texture.baseTexture; + const orig = sprite._texture.orig; 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); - mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); + mappedMatrix.scale(1.0 / orig.width, 1.0 / orig.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js b/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js index 1e86c68..e5fd49c 100644 --- a/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js +++ b/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js @@ -2,6 +2,7 @@ import { Matrix } from '../../../../math'; import { readFileSync } from 'fs'; import { join } from 'path'; +import { default as TextureMatrix } from '../../../../textures/TextureMatrix'; /** * The SpriteMaskFilter class @@ -40,10 +41,25 @@ apply(filterManager, input, output) { const maskSprite = this.maskSprite; + const tex = this.maskSprite.texture; - this.uniforms.mask = maskSprite._texture; - this.uniforms.otherMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, maskSprite); + if (!tex.valid) + { + return; + } + if (!tex.transform) + { + // margin = 0.0, let it bleed a bit, shader code becomes easier + // assuming that atlas textures were made with 1-pixel padding + tex.transform = new TextureMatrix(tex, 0.0); + } + tex.transform.update(); + + this.uniforms.mask = tex; + this.uniforms.otherMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, maskSprite) + .prepend(tex.transform.mapCoord); this.uniforms.alpha = maskSprite.worldAlpha; + this.uniforms.maskClamp = tex.transform.uClampFrame; filterManager.applyFilter(this, input, output); } diff --git a/src/core/index.js b/src/core/index.js index 3265dfb..1a92622 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -33,6 +33,7 @@ export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Spritesheet } from './textures/Spritesheet'; export { default as Texture } from './textures/Texture'; +export { default as TextureMatrix } from './textures/TextureMatrix'; export { default as BaseTexture } from './textures/BaseTexture'; export { default as RenderTexture } from './textures/RenderTexture'; export { default as BaseRenderTexture } from './textures/BaseRenderTexture'; diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index f955a9d..2d08dbc 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,13 +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 texture = sprite._texture.baseTexture; + const orig = sprite._texture.orig; 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); - mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); + mappedMatrix.scale(1.0 / orig.width, 1.0 / orig.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js b/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js index 1e86c68..e5fd49c 100644 --- a/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js +++ b/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js @@ -2,6 +2,7 @@ import { Matrix } from '../../../../math'; import { readFileSync } from 'fs'; import { join } from 'path'; +import { default as TextureMatrix } from '../../../../textures/TextureMatrix'; /** * The SpriteMaskFilter class @@ -40,10 +41,25 @@ apply(filterManager, input, output) { const maskSprite = this.maskSprite; + const tex = this.maskSprite.texture; - this.uniforms.mask = maskSprite._texture; - this.uniforms.otherMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, maskSprite); + if (!tex.valid) + { + return; + } + if (!tex.transform) + { + // margin = 0.0, let it bleed a bit, shader code becomes easier + // assuming that atlas textures were made with 1-pixel padding + tex.transform = new TextureMatrix(tex, 0.0); + } + tex.transform.update(); + + this.uniforms.mask = tex; + this.uniforms.otherMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, maskSprite) + .prepend(tex.transform.mapCoord); this.uniforms.alpha = maskSprite.worldAlpha; + this.uniforms.maskClamp = tex.transform.uClampFrame; filterManager.applyFilter(this, input, output); } diff --git a/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag b/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag index 4a419a1..0e4aef8 100644 --- a/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag +++ b/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag @@ -2,16 +2,18 @@ varying vec2 vTextureCoord; uniform sampler2D uSampler; -uniform float alpha; uniform sampler2D mask; +uniform float alpha; +uniform vec4 maskClamp; void main(void) { - // check clip! this will stop the mask bleeding out from the edges - vec2 text = abs( vMaskCoord - 0.5 ); - text = step(0.5, text); + float clip = step(3.5, + step(maskClamp.x, vMaskCoord.x) + + step(maskClamp.y, vMaskCoord.y) + + step(vMaskCoord.x, maskClamp.z) + + step(vMaskCoord.y, maskClamp.w)); - float clip = 1.0 - max(text.y, text.x); vec4 original = texture2D(uSampler, vTextureCoord); vec4 masky = texture2D(mask, vMaskCoord); diff --git a/src/core/index.js b/src/core/index.js index 3265dfb..1a92622 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -33,6 +33,7 @@ export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Spritesheet } from './textures/Spritesheet'; export { default as Texture } from './textures/Texture'; +export { default as TextureMatrix } from './textures/TextureMatrix'; export { default as BaseTexture } from './textures/BaseTexture'; export { default as RenderTexture } from './textures/RenderTexture'; export { default as BaseRenderTexture } from './textures/BaseRenderTexture'; diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index f955a9d..2d08dbc 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,13 +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 texture = sprite._texture.baseTexture; + const orig = sprite._texture.orig; 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); - mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); + mappedMatrix.scale(1.0 / orig.width, 1.0 / orig.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js b/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js index 1e86c68..e5fd49c 100644 --- a/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js +++ b/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js @@ -2,6 +2,7 @@ import { Matrix } from '../../../../math'; import { readFileSync } from 'fs'; import { join } from 'path'; +import { default as TextureMatrix } from '../../../../textures/TextureMatrix'; /** * The SpriteMaskFilter class @@ -40,10 +41,25 @@ apply(filterManager, input, output) { const maskSprite = this.maskSprite; + const tex = this.maskSprite.texture; - this.uniforms.mask = maskSprite._texture; - this.uniforms.otherMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, maskSprite); + if (!tex.valid) + { + return; + } + if (!tex.transform) + { + // margin = 0.0, let it bleed a bit, shader code becomes easier + // assuming that atlas textures were made with 1-pixel padding + tex.transform = new TextureMatrix(tex, 0.0); + } + tex.transform.update(); + + this.uniforms.mask = tex; + this.uniforms.otherMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, maskSprite) + .prepend(tex.transform.mapCoord); this.uniforms.alpha = maskSprite.worldAlpha; + this.uniforms.maskClamp = tex.transform.uClampFrame; filterManager.applyFilter(this, input, output); } diff --git a/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag b/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag index 4a419a1..0e4aef8 100644 --- a/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag +++ b/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag @@ -2,16 +2,18 @@ varying vec2 vTextureCoord; uniform sampler2D uSampler; -uniform float alpha; uniform sampler2D mask; +uniform float alpha; +uniform vec4 maskClamp; void main(void) { - // check clip! this will stop the mask bleeding out from the edges - vec2 text = abs( vMaskCoord - 0.5 ); - text = step(0.5, text); + float clip = step(3.5, + step(maskClamp.x, vMaskCoord.x) + + step(maskClamp.y, vMaskCoord.y) + + step(vMaskCoord.x, maskClamp.z) + + step(vMaskCoord.y, maskClamp.w)); - float clip = 1.0 - max(text.y, text.x); vec4 original = texture2D(uSampler, vTextureCoord); vec4 masky = texture2D(mask, vMaskCoord); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 75eb069..c63af99 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -80,6 +80,7 @@ /** * This is the trimmed area of original texture, before it was put in atlas + * Please call `_updateUvs()` after you change coordinates of `trim` manually. * * @member {PIXI.Rectangle} */ @@ -153,8 +154,10 @@ this._updateID = 0; /** - * Extra field for extra plugins. May contain clamp settings and some matrices - * @type {Object} + * Contains data for uvs. May contain clamp settings and some matrices. + * Its a bit heavy, so by default that object is not created. + * @type {PIXI.TextureMatrix} + * @default null */ this.transform = null; @@ -266,9 +269,7 @@ } /** - * Updates the internal WebGL UV cache. - * - * @protected + * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture. */ _updateUvs() { @@ -538,6 +539,7 @@ /** * The frame specifies the region of the base texture that this texture uses. + * Please call `_updateUvs()` after you change coordinates of `frame` manually. * * @member {PIXI.Rectangle} */ diff --git a/src/core/index.js b/src/core/index.js index 3265dfb..1a92622 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -33,6 +33,7 @@ export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Spritesheet } from './textures/Spritesheet'; export { default as Texture } from './textures/Texture'; +export { default as TextureMatrix } from './textures/TextureMatrix'; export { default as BaseTexture } from './textures/BaseTexture'; export { default as RenderTexture } from './textures/RenderTexture'; export { default as BaseRenderTexture } from './textures/BaseRenderTexture'; diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index f955a9d..2d08dbc 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,13 +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 texture = sprite._texture.baseTexture; + const orig = sprite._texture.orig; 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); - mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); + mappedMatrix.scale(1.0 / orig.width, 1.0 / orig.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js b/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js index 1e86c68..e5fd49c 100644 --- a/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js +++ b/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js @@ -2,6 +2,7 @@ import { Matrix } from '../../../../math'; import { readFileSync } from 'fs'; import { join } from 'path'; +import { default as TextureMatrix } from '../../../../textures/TextureMatrix'; /** * The SpriteMaskFilter class @@ -40,10 +41,25 @@ apply(filterManager, input, output) { const maskSprite = this.maskSprite; + const tex = this.maskSprite.texture; - this.uniforms.mask = maskSprite._texture; - this.uniforms.otherMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, maskSprite); + if (!tex.valid) + { + return; + } + if (!tex.transform) + { + // margin = 0.0, let it bleed a bit, shader code becomes easier + // assuming that atlas textures were made with 1-pixel padding + tex.transform = new TextureMatrix(tex, 0.0); + } + tex.transform.update(); + + this.uniforms.mask = tex; + this.uniforms.otherMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, maskSprite) + .prepend(tex.transform.mapCoord); this.uniforms.alpha = maskSprite.worldAlpha; + this.uniforms.maskClamp = tex.transform.uClampFrame; filterManager.applyFilter(this, input, output); } diff --git a/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag b/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag index 4a419a1..0e4aef8 100644 --- a/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag +++ b/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag @@ -2,16 +2,18 @@ varying vec2 vTextureCoord; uniform sampler2D uSampler; -uniform float alpha; uniform sampler2D mask; +uniform float alpha; +uniform vec4 maskClamp; void main(void) { - // check clip! this will stop the mask bleeding out from the edges - vec2 text = abs( vMaskCoord - 0.5 ); - text = step(0.5, text); + float clip = step(3.5, + step(maskClamp.x, vMaskCoord.x) + + step(maskClamp.y, vMaskCoord.y) + + step(vMaskCoord.x, maskClamp.z) + + step(vMaskCoord.y, maskClamp.w)); - float clip = 1.0 - max(text.y, text.x); vec4 original = texture2D(uSampler, vTextureCoord); vec4 masky = texture2D(mask, vMaskCoord); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 75eb069..c63af99 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -80,6 +80,7 @@ /** * This is the trimmed area of original texture, before it was put in atlas + * Please call `_updateUvs()` after you change coordinates of `trim` manually. * * @member {PIXI.Rectangle} */ @@ -153,8 +154,10 @@ this._updateID = 0; /** - * Extra field for extra plugins. May contain clamp settings and some matrices - * @type {Object} + * Contains data for uvs. May contain clamp settings and some matrices. + * Its a bit heavy, so by default that object is not created. + * @type {PIXI.TextureMatrix} + * @default null */ this.transform = null; @@ -266,9 +269,7 @@ } /** - * Updates the internal WebGL UV cache. - * - * @protected + * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture. */ _updateUvs() { @@ -538,6 +539,7 @@ /** * The frame specifies the region of the base texture that this texture uses. + * Please call `_updateUvs()` after you change coordinates of `frame` manually. * * @member {PIXI.Rectangle} */ diff --git a/src/core/textures/TextureMatrix.js b/src/core/textures/TextureMatrix.js new file mode 100644 index 0000000..f0b9c12 --- /dev/null +++ b/src/core/textures/TextureMatrix.js @@ -0,0 +1,149 @@ +import { default as Matrix } from '../math/Matrix'; + +const tempMat = new Matrix(); + +/** + * Class controls uv transform and frame clamp for texture + * Can be used in Texture "transform" field, or separately, you can use different clamp settings on the same texture. + * If you want to add support for texture region of certain feature or filter, that's what you're looking for. + * + * @see PIXI.Texture + * @see PIXI.mesh.Mesh + * @see PIXI.extras.TilingSprite + * @class + * @memberof PIXI + */ +export default class TextureMatrix +{ + /** + * + * @param {PIXI.Texture} texture observed texture + * @param {number} [clampMargin] Changes frame clamping, 0.5 by default. Use -0.5 for extra border. + * @constructor + */ + constructor(texture, clampMargin) + { + this._texture = texture; + + this.mapCoord = new Matrix(); + + this.uClampFrame = new Float32Array(4); + + this.uClampOffset = new Float32Array(2); + + this._lastTextureID = -1; + + /** + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders + * + * @default 0 + * @member {number} + */ + this.clampOffset = 0; + + /** + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to -0.5 to add a pixel to the edge, recommended for transparent trimmed textures in atlas + * + * @default 0.5 + * @member {number} + */ + this.clampMargin = (typeof clampMargin === 'undefined') ? 0.5 : clampMargin; + } + + /** + * texture property + * @member {PIXI.Texture} + */ + get texture() + { + return this._texture; + } + + set texture(value) // eslint-disable-line require-jsdoc + { + this._texture = value; + this._lastTextureID = -1; + } + + /** + * Multiplies uvs array to transform + * @param {Float32Array} uvs mesh uvs + * @param {Float32Array} [out=uvs] output + * @returns {Float32Array} output + */ + multiplyUvs(uvs, out) + { + if (out === undefined) + { + out = uvs; + } + + const mat = this.mapCoord; + + for (let i = 0; i < uvs.length; i += 2) + { + const x = uvs[i]; + const y = uvs[i + 1]; + + out[i] = (x * mat.a) + (y * mat.c) + mat.tx; + out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; + } + + return out; + } + + /** + * updates matrices if texture was changed + * @param {boolean} forceUpdate if true, matrices will be updated any case + * @returns {boolean} whether or not it was updated + */ + update(forceUpdate) + { + const tex = this._texture; + + if (!tex || !tex.valid) + { + return false; + } + + if (!forceUpdate + && this._lastTextureID === tex._updateID) + { + return false; + } + + this._lastTextureID = tex._updateID; + + const uvs = tex._uvs; + + this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); + + const orig = tex.orig; + const trim = tex.trim; + + if (trim) + { + tempMat.set(orig.width / trim.width, 0, 0, orig.height / trim.height, + -trim.x / trim.width, -trim.y / trim.height); + this.mapCoord.append(tempMat); + } + + const texBase = tex.baseTexture; + const frame = this.uClampFrame; + const margin = this.clampMargin / texBase.resolution; + const offset = this.clampOffset; + + frame[0] = (tex._frame.x + margin + offset) / texBase.width; + frame[1] = (tex._frame.y + margin + offset) / texBase.height; + frame[2] = (tex._frame.x + tex._frame.width - margin + offset) / texBase.width; + frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; + this.uClampOffset[0] = offset / texBase.realWidth; + this.uClampOffset[1] = offset / texBase.realHeight; + + return true; + } +} diff --git a/src/core/index.js b/src/core/index.js index 3265dfb..1a92622 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -33,6 +33,7 @@ export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Spritesheet } from './textures/Spritesheet'; export { default as Texture } from './textures/Texture'; +export { default as TextureMatrix } from './textures/TextureMatrix'; export { default as BaseTexture } from './textures/BaseTexture'; export { default as RenderTexture } from './textures/RenderTexture'; export { default as BaseRenderTexture } from './textures/BaseRenderTexture'; diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index f955a9d..2d08dbc 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,13 +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 texture = sprite._texture.baseTexture; + const orig = sprite._texture.orig; 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); - mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); + mappedMatrix.scale(1.0 / orig.width, 1.0 / orig.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js b/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js index 1e86c68..e5fd49c 100644 --- a/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js +++ b/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js @@ -2,6 +2,7 @@ import { Matrix } from '../../../../math'; import { readFileSync } from 'fs'; import { join } from 'path'; +import { default as TextureMatrix } from '../../../../textures/TextureMatrix'; /** * The SpriteMaskFilter class @@ -40,10 +41,25 @@ apply(filterManager, input, output) { const maskSprite = this.maskSprite; + const tex = this.maskSprite.texture; - this.uniforms.mask = maskSprite._texture; - this.uniforms.otherMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, maskSprite); + if (!tex.valid) + { + return; + } + if (!tex.transform) + { + // margin = 0.0, let it bleed a bit, shader code becomes easier + // assuming that atlas textures were made with 1-pixel padding + tex.transform = new TextureMatrix(tex, 0.0); + } + tex.transform.update(); + + this.uniforms.mask = tex; + this.uniforms.otherMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, maskSprite) + .prepend(tex.transform.mapCoord); this.uniforms.alpha = maskSprite.worldAlpha; + this.uniforms.maskClamp = tex.transform.uClampFrame; filterManager.applyFilter(this, input, output); } diff --git a/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag b/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag index 4a419a1..0e4aef8 100644 --- a/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag +++ b/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag @@ -2,16 +2,18 @@ varying vec2 vTextureCoord; uniform sampler2D uSampler; -uniform float alpha; uniform sampler2D mask; +uniform float alpha; +uniform vec4 maskClamp; void main(void) { - // check clip! this will stop the mask bleeding out from the edges - vec2 text = abs( vMaskCoord - 0.5 ); - text = step(0.5, text); + float clip = step(3.5, + step(maskClamp.x, vMaskCoord.x) + + step(maskClamp.y, vMaskCoord.y) + + step(vMaskCoord.x, maskClamp.z) + + step(vMaskCoord.y, maskClamp.w)); - float clip = 1.0 - max(text.y, text.x); vec4 original = texture2D(uSampler, vTextureCoord); vec4 masky = texture2D(mask, vMaskCoord); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 75eb069..c63af99 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -80,6 +80,7 @@ /** * This is the trimmed area of original texture, before it was put in atlas + * Please call `_updateUvs()` after you change coordinates of `trim` manually. * * @member {PIXI.Rectangle} */ @@ -153,8 +154,10 @@ this._updateID = 0; /** - * Extra field for extra plugins. May contain clamp settings and some matrices - * @type {Object} + * Contains data for uvs. May contain clamp settings and some matrices. + * Its a bit heavy, so by default that object is not created. + * @type {PIXI.TextureMatrix} + * @default null */ this.transform = null; @@ -266,9 +269,7 @@ } /** - * Updates the internal WebGL UV cache. - * - * @protected + * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture. */ _updateUvs() { @@ -538,6 +539,7 @@ /** * The frame specifies the region of the base texture that this texture uses. + * Please call `_updateUvs()` after you change coordinates of `frame` manually. * * @member {PIXI.Rectangle} */ diff --git a/src/core/textures/TextureMatrix.js b/src/core/textures/TextureMatrix.js new file mode 100644 index 0000000..f0b9c12 --- /dev/null +++ b/src/core/textures/TextureMatrix.js @@ -0,0 +1,149 @@ +import { default as Matrix } from '../math/Matrix'; + +const tempMat = new Matrix(); + +/** + * Class controls uv transform and frame clamp for texture + * Can be used in Texture "transform" field, or separately, you can use different clamp settings on the same texture. + * If you want to add support for texture region of certain feature or filter, that's what you're looking for. + * + * @see PIXI.Texture + * @see PIXI.mesh.Mesh + * @see PIXI.extras.TilingSprite + * @class + * @memberof PIXI + */ +export default class TextureMatrix +{ + /** + * + * @param {PIXI.Texture} texture observed texture + * @param {number} [clampMargin] Changes frame clamping, 0.5 by default. Use -0.5 for extra border. + * @constructor + */ + constructor(texture, clampMargin) + { + this._texture = texture; + + this.mapCoord = new Matrix(); + + this.uClampFrame = new Float32Array(4); + + this.uClampOffset = new Float32Array(2); + + this._lastTextureID = -1; + + /** + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders + * + * @default 0 + * @member {number} + */ + this.clampOffset = 0; + + /** + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to -0.5 to add a pixel to the edge, recommended for transparent trimmed textures in atlas + * + * @default 0.5 + * @member {number} + */ + this.clampMargin = (typeof clampMargin === 'undefined') ? 0.5 : clampMargin; + } + + /** + * texture property + * @member {PIXI.Texture} + */ + get texture() + { + return this._texture; + } + + set texture(value) // eslint-disable-line require-jsdoc + { + this._texture = value; + this._lastTextureID = -1; + } + + /** + * Multiplies uvs array to transform + * @param {Float32Array} uvs mesh uvs + * @param {Float32Array} [out=uvs] output + * @returns {Float32Array} output + */ + multiplyUvs(uvs, out) + { + if (out === undefined) + { + out = uvs; + } + + const mat = this.mapCoord; + + for (let i = 0; i < uvs.length; i += 2) + { + const x = uvs[i]; + const y = uvs[i + 1]; + + out[i] = (x * mat.a) + (y * mat.c) + mat.tx; + out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; + } + + return out; + } + + /** + * updates matrices if texture was changed + * @param {boolean} forceUpdate if true, matrices will be updated any case + * @returns {boolean} whether or not it was updated + */ + update(forceUpdate) + { + const tex = this._texture; + + if (!tex || !tex.valid) + { + return false; + } + + if (!forceUpdate + && this._lastTextureID === tex._updateID) + { + return false; + } + + this._lastTextureID = tex._updateID; + + const uvs = tex._uvs; + + this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); + + const orig = tex.orig; + const trim = tex.trim; + + if (trim) + { + tempMat.set(orig.width / trim.width, 0, 0, orig.height / trim.height, + -trim.x / trim.width, -trim.y / trim.height); + this.mapCoord.append(tempMat); + } + + const texBase = tex.baseTexture; + const frame = this.uClampFrame; + const margin = this.clampMargin / texBase.resolution; + const offset = this.clampOffset; + + frame[0] = (tex._frame.x + margin + offset) / texBase.width; + frame[1] = (tex._frame.y + margin + offset) / texBase.height; + frame[2] = (tex._frame.x + tex._frame.width - margin + offset) / texBase.width; + frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; + this.uClampOffset[0] = offset / texBase.realWidth; + this.uClampOffset[1] = offset / texBase.realHeight; + + return true; + } +} diff --git a/src/deprecation.js b/src/deprecation.js index 209910a..dab6e70 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -586,6 +586,28 @@ }); } + if (extras) + { + Object.defineProperties(extras, { + /** + * @class + * @name TextureTransform + * @memberof PIXI.extras + * @see PIXI.TextureMatrix + * @deprecated since version 4.6.0 + */ + TextureTransform: { + get() + { + warn('The TextureTransform class has been renamed to TextureMatrix, ' + + 'please use PIXI.TextureMatrix from now on.'); + + return core.TextureMatrix; + }, + }, + }); + } + core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { warn('generateTexture has moved to the renderer, please use renderer.generateTexture(displayObject)'); diff --git a/src/core/index.js b/src/core/index.js index 3265dfb..1a92622 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -33,6 +33,7 @@ export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Spritesheet } from './textures/Spritesheet'; export { default as Texture } from './textures/Texture'; +export { default as TextureMatrix } from './textures/TextureMatrix'; export { default as BaseTexture } from './textures/BaseTexture'; export { default as RenderTexture } from './textures/RenderTexture'; export { default as BaseRenderTexture } from './textures/BaseRenderTexture'; diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index f955a9d..2d08dbc 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,13 +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 texture = sprite._texture.baseTexture; + const orig = sprite._texture.orig; 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); - mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); + mappedMatrix.scale(1.0 / orig.width, 1.0 / orig.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js b/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js index 1e86c68..e5fd49c 100644 --- a/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js +++ b/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js @@ -2,6 +2,7 @@ import { Matrix } from '../../../../math'; import { readFileSync } from 'fs'; import { join } from 'path'; +import { default as TextureMatrix } from '../../../../textures/TextureMatrix'; /** * The SpriteMaskFilter class @@ -40,10 +41,25 @@ apply(filterManager, input, output) { const maskSprite = this.maskSprite; + const tex = this.maskSprite.texture; - this.uniforms.mask = maskSprite._texture; - this.uniforms.otherMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, maskSprite); + if (!tex.valid) + { + return; + } + if (!tex.transform) + { + // margin = 0.0, let it bleed a bit, shader code becomes easier + // assuming that atlas textures were made with 1-pixel padding + tex.transform = new TextureMatrix(tex, 0.0); + } + tex.transform.update(); + + this.uniforms.mask = tex; + this.uniforms.otherMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, maskSprite) + .prepend(tex.transform.mapCoord); this.uniforms.alpha = maskSprite.worldAlpha; + this.uniforms.maskClamp = tex.transform.uClampFrame; filterManager.applyFilter(this, input, output); } diff --git a/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag b/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag index 4a419a1..0e4aef8 100644 --- a/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag +++ b/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag @@ -2,16 +2,18 @@ varying vec2 vTextureCoord; uniform sampler2D uSampler; -uniform float alpha; uniform sampler2D mask; +uniform float alpha; +uniform vec4 maskClamp; void main(void) { - // check clip! this will stop the mask bleeding out from the edges - vec2 text = abs( vMaskCoord - 0.5 ); - text = step(0.5, text); + float clip = step(3.5, + step(maskClamp.x, vMaskCoord.x) + + step(maskClamp.y, vMaskCoord.y) + + step(vMaskCoord.x, maskClamp.z) + + step(vMaskCoord.y, maskClamp.w)); - float clip = 1.0 - max(text.y, text.x); vec4 original = texture2D(uSampler, vTextureCoord); vec4 masky = texture2D(mask, vMaskCoord); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 75eb069..c63af99 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -80,6 +80,7 @@ /** * This is the trimmed area of original texture, before it was put in atlas + * Please call `_updateUvs()` after you change coordinates of `trim` manually. * * @member {PIXI.Rectangle} */ @@ -153,8 +154,10 @@ this._updateID = 0; /** - * Extra field for extra plugins. May contain clamp settings and some matrices - * @type {Object} + * Contains data for uvs. May contain clamp settings and some matrices. + * Its a bit heavy, so by default that object is not created. + * @type {PIXI.TextureMatrix} + * @default null */ this.transform = null; @@ -266,9 +269,7 @@ } /** - * Updates the internal WebGL UV cache. - * - * @protected + * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture. */ _updateUvs() { @@ -538,6 +539,7 @@ /** * The frame specifies the region of the base texture that this texture uses. + * Please call `_updateUvs()` after you change coordinates of `frame` manually. * * @member {PIXI.Rectangle} */ diff --git a/src/core/textures/TextureMatrix.js b/src/core/textures/TextureMatrix.js new file mode 100644 index 0000000..f0b9c12 --- /dev/null +++ b/src/core/textures/TextureMatrix.js @@ -0,0 +1,149 @@ +import { default as Matrix } from '../math/Matrix'; + +const tempMat = new Matrix(); + +/** + * Class controls uv transform and frame clamp for texture + * Can be used in Texture "transform" field, or separately, you can use different clamp settings on the same texture. + * If you want to add support for texture region of certain feature or filter, that's what you're looking for. + * + * @see PIXI.Texture + * @see PIXI.mesh.Mesh + * @see PIXI.extras.TilingSprite + * @class + * @memberof PIXI + */ +export default class TextureMatrix +{ + /** + * + * @param {PIXI.Texture} texture observed texture + * @param {number} [clampMargin] Changes frame clamping, 0.5 by default. Use -0.5 for extra border. + * @constructor + */ + constructor(texture, clampMargin) + { + this._texture = texture; + + this.mapCoord = new Matrix(); + + this.uClampFrame = new Float32Array(4); + + this.uClampOffset = new Float32Array(2); + + this._lastTextureID = -1; + + /** + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders + * + * @default 0 + * @member {number} + */ + this.clampOffset = 0; + + /** + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to -0.5 to add a pixel to the edge, recommended for transparent trimmed textures in atlas + * + * @default 0.5 + * @member {number} + */ + this.clampMargin = (typeof clampMargin === 'undefined') ? 0.5 : clampMargin; + } + + /** + * texture property + * @member {PIXI.Texture} + */ + get texture() + { + return this._texture; + } + + set texture(value) // eslint-disable-line require-jsdoc + { + this._texture = value; + this._lastTextureID = -1; + } + + /** + * Multiplies uvs array to transform + * @param {Float32Array} uvs mesh uvs + * @param {Float32Array} [out=uvs] output + * @returns {Float32Array} output + */ + multiplyUvs(uvs, out) + { + if (out === undefined) + { + out = uvs; + } + + const mat = this.mapCoord; + + for (let i = 0; i < uvs.length; i += 2) + { + const x = uvs[i]; + const y = uvs[i + 1]; + + out[i] = (x * mat.a) + (y * mat.c) + mat.tx; + out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; + } + + return out; + } + + /** + * updates matrices if texture was changed + * @param {boolean} forceUpdate if true, matrices will be updated any case + * @returns {boolean} whether or not it was updated + */ + update(forceUpdate) + { + const tex = this._texture; + + if (!tex || !tex.valid) + { + return false; + } + + if (!forceUpdate + && this._lastTextureID === tex._updateID) + { + return false; + } + + this._lastTextureID = tex._updateID; + + const uvs = tex._uvs; + + this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); + + const orig = tex.orig; + const trim = tex.trim; + + if (trim) + { + tempMat.set(orig.width / trim.width, 0, 0, orig.height / trim.height, + -trim.x / trim.width, -trim.y / trim.height); + this.mapCoord.append(tempMat); + } + + const texBase = tex.baseTexture; + const frame = this.uClampFrame; + const margin = this.clampMargin / texBase.resolution; + const offset = this.clampOffset; + + frame[0] = (tex._frame.x + margin + offset) / texBase.width; + frame[1] = (tex._frame.y + margin + offset) / texBase.height; + frame[2] = (tex._frame.x + tex._frame.width - margin + offset) / texBase.width; + frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; + this.uClampOffset[0] = offset / texBase.realWidth; + this.uClampOffset[1] = offset / texBase.realHeight; + + return true; + } +} diff --git a/src/deprecation.js b/src/deprecation.js index 209910a..dab6e70 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -586,6 +586,28 @@ }); } + if (extras) + { + Object.defineProperties(extras, { + /** + * @class + * @name TextureTransform + * @memberof PIXI.extras + * @see PIXI.TextureMatrix + * @deprecated since version 4.6.0 + */ + TextureTransform: { + get() + { + warn('The TextureTransform class has been renamed to TextureMatrix, ' + + 'please use PIXI.TextureMatrix from now on.'); + + return core.TextureMatrix; + }, + }, + }); + } + core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { warn('generateTexture has moved to the renderer, please use renderer.generateTexture(displayObject)'); diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js deleted file mode 100644 index 16536d2..0000000 --- a/src/extras/TextureTransform.js +++ /dev/null @@ -1,144 +0,0 @@ -import { default as Matrix } from '../core/math/Matrix'; - -const tempMat = new Matrix(); - -/** - * class controls uv transform and frame clamp for texture - * - * @class - * @memberof PIXI.extras - */ -export default class TextureTransform -{ - /** - * - * @param {PIXI.Texture} texture observed texture - * @param {number} [clampMargin] Changes frame clamping, 0.5 by default. Use -0.5 for extra border. - * @constructor - */ - constructor(texture, clampMargin) - { - this._texture = texture; - - this.mapCoord = new Matrix(); - - this.uClampFrame = new Float32Array(4); - - this.uClampOffset = new Float32Array(2); - - this._lastTextureID = -1; - - /** - * Changes frame clamping - * Works with TilingSprite and Mesh - * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders - * - * @default 0 - * @member {number} - */ - this.clampOffset = 0; - - /** - * Changes frame clamping - * Works with TilingSprite and Mesh - * Change to -0.5 to add a pixel to the edge, recommended for transparent trimmed textures in atlas - * - * @default 0.5 - * @member {number} - */ - this.clampMargin = (typeof clampMargin === 'undefined') ? 0.5 : clampMargin; - } - - /** - * texture property - * @member {PIXI.Texture} - */ - get texture() - { - return this._texture; - } - - set texture(value) // eslint-disable-line require-jsdoc - { - this._texture = value; - this._lastTextureID = -1; - } - - /** - * Multiplies uvs array to transform - * @param {Float32Array} uvs mesh uvs - * @param {Float32Array} [out=uvs] output - * @returns {Float32Array} output - */ - multiplyUvs(uvs, out) - { - if (out === undefined) - { - out = uvs; - } - - const mat = this.mapCoord; - - for (let i = 0; i < uvs.length; i += 2) - { - const x = uvs[i]; - const y = uvs[i + 1]; - - out[i] = (x * mat.a) + (y * mat.c) + mat.tx; - out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; - } - - return out; - } - - /** - * updates matrices if texture was changed - * @param {boolean} forceUpdate if true, matrices will be updated any case - * @returns {boolean} whether or not it was updated - */ - update(forceUpdate) - { - const tex = this._texture; - - if (!tex || !tex.valid) - { - return false; - } - - if (!forceUpdate - && this._lastTextureID === tex._updateID) - { - return false; - } - - this._lastTextureID = tex._updateID; - - const uvs = tex._uvs; - - this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); - - const orig = tex.orig; - const trim = tex.trim; - - if (trim) - { - tempMat.set(orig.width / trim.width, 0, 0, orig.height / trim.height, - -trim.x / trim.width, -trim.y / trim.height); - this.mapCoord.append(tempMat); - } - - const texBase = tex.baseTexture; - const frame = this.uClampFrame; - const margin = this.clampMargin / texBase.resolution; - const offset = this.clampOffset; - - frame[0] = (tex._frame.x + margin + offset) / texBase.width; - frame[1] = (tex._frame.y + margin + offset) / texBase.height; - frame[2] = (tex._frame.x + tex._frame.width - margin + offset) / texBase.width; - frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; - this.uClampOffset[0] = offset / texBase.realWidth; - this.uClampOffset[1] = offset / texBase.realHeight; - - return true; - } -} diff --git a/src/core/index.js b/src/core/index.js index 3265dfb..1a92622 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -33,6 +33,7 @@ export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Spritesheet } from './textures/Spritesheet'; export { default as Texture } from './textures/Texture'; +export { default as TextureMatrix } from './textures/TextureMatrix'; export { default as BaseTexture } from './textures/BaseTexture'; export { default as RenderTexture } from './textures/RenderTexture'; export { default as BaseRenderTexture } from './textures/BaseRenderTexture'; diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index f955a9d..2d08dbc 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,13 +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 texture = sprite._texture.baseTexture; + const orig = sprite._texture.orig; 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); - mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); + mappedMatrix.scale(1.0 / orig.width, 1.0 / orig.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js b/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js index 1e86c68..e5fd49c 100644 --- a/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js +++ b/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js @@ -2,6 +2,7 @@ import { Matrix } from '../../../../math'; import { readFileSync } from 'fs'; import { join } from 'path'; +import { default as TextureMatrix } from '../../../../textures/TextureMatrix'; /** * The SpriteMaskFilter class @@ -40,10 +41,25 @@ apply(filterManager, input, output) { const maskSprite = this.maskSprite; + const tex = this.maskSprite.texture; - this.uniforms.mask = maskSprite._texture; - this.uniforms.otherMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, maskSprite); + if (!tex.valid) + { + return; + } + if (!tex.transform) + { + // margin = 0.0, let it bleed a bit, shader code becomes easier + // assuming that atlas textures were made with 1-pixel padding + tex.transform = new TextureMatrix(tex, 0.0); + } + tex.transform.update(); + + this.uniforms.mask = tex; + this.uniforms.otherMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, maskSprite) + .prepend(tex.transform.mapCoord); this.uniforms.alpha = maskSprite.worldAlpha; + this.uniforms.maskClamp = tex.transform.uClampFrame; filterManager.applyFilter(this, input, output); } diff --git a/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag b/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag index 4a419a1..0e4aef8 100644 --- a/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag +++ b/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag @@ -2,16 +2,18 @@ varying vec2 vTextureCoord; uniform sampler2D uSampler; -uniform float alpha; uniform sampler2D mask; +uniform float alpha; +uniform vec4 maskClamp; void main(void) { - // check clip! this will stop the mask bleeding out from the edges - vec2 text = abs( vMaskCoord - 0.5 ); - text = step(0.5, text); + float clip = step(3.5, + step(maskClamp.x, vMaskCoord.x) + + step(maskClamp.y, vMaskCoord.y) + + step(vMaskCoord.x, maskClamp.z) + + step(vMaskCoord.y, maskClamp.w)); - float clip = 1.0 - max(text.y, text.x); vec4 original = texture2D(uSampler, vTextureCoord); vec4 masky = texture2D(mask, vMaskCoord); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 75eb069..c63af99 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -80,6 +80,7 @@ /** * This is the trimmed area of original texture, before it was put in atlas + * Please call `_updateUvs()` after you change coordinates of `trim` manually. * * @member {PIXI.Rectangle} */ @@ -153,8 +154,10 @@ this._updateID = 0; /** - * Extra field for extra plugins. May contain clamp settings and some matrices - * @type {Object} + * Contains data for uvs. May contain clamp settings and some matrices. + * Its a bit heavy, so by default that object is not created. + * @type {PIXI.TextureMatrix} + * @default null */ this.transform = null; @@ -266,9 +269,7 @@ } /** - * Updates the internal WebGL UV cache. - * - * @protected + * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture. */ _updateUvs() { @@ -538,6 +539,7 @@ /** * The frame specifies the region of the base texture that this texture uses. + * Please call `_updateUvs()` after you change coordinates of `frame` manually. * * @member {PIXI.Rectangle} */ diff --git a/src/core/textures/TextureMatrix.js b/src/core/textures/TextureMatrix.js new file mode 100644 index 0000000..f0b9c12 --- /dev/null +++ b/src/core/textures/TextureMatrix.js @@ -0,0 +1,149 @@ +import { default as Matrix } from '../math/Matrix'; + +const tempMat = new Matrix(); + +/** + * Class controls uv transform and frame clamp for texture + * Can be used in Texture "transform" field, or separately, you can use different clamp settings on the same texture. + * If you want to add support for texture region of certain feature or filter, that's what you're looking for. + * + * @see PIXI.Texture + * @see PIXI.mesh.Mesh + * @see PIXI.extras.TilingSprite + * @class + * @memberof PIXI + */ +export default class TextureMatrix +{ + /** + * + * @param {PIXI.Texture} texture observed texture + * @param {number} [clampMargin] Changes frame clamping, 0.5 by default. Use -0.5 for extra border. + * @constructor + */ + constructor(texture, clampMargin) + { + this._texture = texture; + + this.mapCoord = new Matrix(); + + this.uClampFrame = new Float32Array(4); + + this.uClampOffset = new Float32Array(2); + + this._lastTextureID = -1; + + /** + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders + * + * @default 0 + * @member {number} + */ + this.clampOffset = 0; + + /** + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to -0.5 to add a pixel to the edge, recommended for transparent trimmed textures in atlas + * + * @default 0.5 + * @member {number} + */ + this.clampMargin = (typeof clampMargin === 'undefined') ? 0.5 : clampMargin; + } + + /** + * texture property + * @member {PIXI.Texture} + */ + get texture() + { + return this._texture; + } + + set texture(value) // eslint-disable-line require-jsdoc + { + this._texture = value; + this._lastTextureID = -1; + } + + /** + * Multiplies uvs array to transform + * @param {Float32Array} uvs mesh uvs + * @param {Float32Array} [out=uvs] output + * @returns {Float32Array} output + */ + multiplyUvs(uvs, out) + { + if (out === undefined) + { + out = uvs; + } + + const mat = this.mapCoord; + + for (let i = 0; i < uvs.length; i += 2) + { + const x = uvs[i]; + const y = uvs[i + 1]; + + out[i] = (x * mat.a) + (y * mat.c) + mat.tx; + out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; + } + + return out; + } + + /** + * updates matrices if texture was changed + * @param {boolean} forceUpdate if true, matrices will be updated any case + * @returns {boolean} whether or not it was updated + */ + update(forceUpdate) + { + const tex = this._texture; + + if (!tex || !tex.valid) + { + return false; + } + + if (!forceUpdate + && this._lastTextureID === tex._updateID) + { + return false; + } + + this._lastTextureID = tex._updateID; + + const uvs = tex._uvs; + + this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); + + const orig = tex.orig; + const trim = tex.trim; + + if (trim) + { + tempMat.set(orig.width / trim.width, 0, 0, orig.height / trim.height, + -trim.x / trim.width, -trim.y / trim.height); + this.mapCoord.append(tempMat); + } + + const texBase = tex.baseTexture; + const frame = this.uClampFrame; + const margin = this.clampMargin / texBase.resolution; + const offset = this.clampOffset; + + frame[0] = (tex._frame.x + margin + offset) / texBase.width; + frame[1] = (tex._frame.y + margin + offset) / texBase.height; + frame[2] = (tex._frame.x + tex._frame.width - margin + offset) / texBase.width; + frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; + this.uClampOffset[0] = offset / texBase.realWidth; + this.uClampOffset[1] = offset / texBase.realHeight; + + return true; + } +} diff --git a/src/deprecation.js b/src/deprecation.js index 209910a..dab6e70 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -586,6 +586,28 @@ }); } + if (extras) + { + Object.defineProperties(extras, { + /** + * @class + * @name TextureTransform + * @memberof PIXI.extras + * @see PIXI.TextureMatrix + * @deprecated since version 4.6.0 + */ + TextureTransform: { + get() + { + warn('The TextureTransform class has been renamed to TextureMatrix, ' + + 'please use PIXI.TextureMatrix from now on.'); + + return core.TextureMatrix; + }, + }, + }); + } + core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { warn('generateTexture has moved to the renderer, please use renderer.generateTexture(displayObject)'); diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js deleted file mode 100644 index 16536d2..0000000 --- a/src/extras/TextureTransform.js +++ /dev/null @@ -1,144 +0,0 @@ -import { default as Matrix } from '../core/math/Matrix'; - -const tempMat = new Matrix(); - -/** - * class controls uv transform and frame clamp for texture - * - * @class - * @memberof PIXI.extras - */ -export default class TextureTransform -{ - /** - * - * @param {PIXI.Texture} texture observed texture - * @param {number} [clampMargin] Changes frame clamping, 0.5 by default. Use -0.5 for extra border. - * @constructor - */ - constructor(texture, clampMargin) - { - this._texture = texture; - - this.mapCoord = new Matrix(); - - this.uClampFrame = new Float32Array(4); - - this.uClampOffset = new Float32Array(2); - - this._lastTextureID = -1; - - /** - * Changes frame clamping - * Works with TilingSprite and Mesh - * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders - * - * @default 0 - * @member {number} - */ - this.clampOffset = 0; - - /** - * Changes frame clamping - * Works with TilingSprite and Mesh - * Change to -0.5 to add a pixel to the edge, recommended for transparent trimmed textures in atlas - * - * @default 0.5 - * @member {number} - */ - this.clampMargin = (typeof clampMargin === 'undefined') ? 0.5 : clampMargin; - } - - /** - * texture property - * @member {PIXI.Texture} - */ - get texture() - { - return this._texture; - } - - set texture(value) // eslint-disable-line require-jsdoc - { - this._texture = value; - this._lastTextureID = -1; - } - - /** - * Multiplies uvs array to transform - * @param {Float32Array} uvs mesh uvs - * @param {Float32Array} [out=uvs] output - * @returns {Float32Array} output - */ - multiplyUvs(uvs, out) - { - if (out === undefined) - { - out = uvs; - } - - const mat = this.mapCoord; - - for (let i = 0; i < uvs.length; i += 2) - { - const x = uvs[i]; - const y = uvs[i + 1]; - - out[i] = (x * mat.a) + (y * mat.c) + mat.tx; - out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; - } - - return out; - } - - /** - * updates matrices if texture was changed - * @param {boolean} forceUpdate if true, matrices will be updated any case - * @returns {boolean} whether or not it was updated - */ - update(forceUpdate) - { - const tex = this._texture; - - if (!tex || !tex.valid) - { - return false; - } - - if (!forceUpdate - && this._lastTextureID === tex._updateID) - { - return false; - } - - this._lastTextureID = tex._updateID; - - const uvs = tex._uvs; - - this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); - - const orig = tex.orig; - const trim = tex.trim; - - if (trim) - { - tempMat.set(orig.width / trim.width, 0, 0, orig.height / trim.height, - -trim.x / trim.width, -trim.y / trim.height); - this.mapCoord.append(tempMat); - } - - const texBase = tex.baseTexture; - const frame = this.uClampFrame; - const margin = this.clampMargin / texBase.resolution; - const offset = this.clampOffset; - - frame[0] = (tex._frame.x + margin + offset) / texBase.width; - frame[1] = (tex._frame.y + margin + offset) / texBase.height; - frame[2] = (tex._frame.x + tex._frame.width - margin + offset) / texBase.width; - frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; - this.uClampOffset[0] = offset / texBase.realWidth; - this.uClampOffset[1] = offset / texBase.realHeight; - - return true; - } -} diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 736b207..84c0841 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -1,6 +1,5 @@ import * as core from '../core'; import CanvasTinter from '../core/sprites/canvas/CanvasTinter'; -import { default as TextureTransform } from './TextureTransform'; const tempPoint = new core.Point(); @@ -58,9 +57,9 @@ /** * transform that is applied to UV to get the texture coords * - * @member {PIXI.extras.TextureTransform} + * @member {PIXI.TextureMatrix} */ - this.uvTransform = texture.transform || new TextureTransform(texture); + this.uvTransform = texture.transform || new core.TextureMatrix(texture); /** * Plugin that is responsible for rendering this element. diff --git a/src/core/index.js b/src/core/index.js index 3265dfb..1a92622 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -33,6 +33,7 @@ export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Spritesheet } from './textures/Spritesheet'; export { default as Texture } from './textures/Texture'; +export { default as TextureMatrix } from './textures/TextureMatrix'; export { default as BaseTexture } from './textures/BaseTexture'; export { default as RenderTexture } from './textures/RenderTexture'; export { default as BaseRenderTexture } from './textures/BaseRenderTexture'; diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index f955a9d..2d08dbc 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,13 +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 texture = sprite._texture.baseTexture; + const orig = sprite._texture.orig; 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); - mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); + mappedMatrix.scale(1.0 / orig.width, 1.0 / orig.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js b/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js index 1e86c68..e5fd49c 100644 --- a/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js +++ b/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js @@ -2,6 +2,7 @@ import { Matrix } from '../../../../math'; import { readFileSync } from 'fs'; import { join } from 'path'; +import { default as TextureMatrix } from '../../../../textures/TextureMatrix'; /** * The SpriteMaskFilter class @@ -40,10 +41,25 @@ apply(filterManager, input, output) { const maskSprite = this.maskSprite; + const tex = this.maskSprite.texture; - this.uniforms.mask = maskSprite._texture; - this.uniforms.otherMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, maskSprite); + if (!tex.valid) + { + return; + } + if (!tex.transform) + { + // margin = 0.0, let it bleed a bit, shader code becomes easier + // assuming that atlas textures were made with 1-pixel padding + tex.transform = new TextureMatrix(tex, 0.0); + } + tex.transform.update(); + + this.uniforms.mask = tex; + this.uniforms.otherMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, maskSprite) + .prepend(tex.transform.mapCoord); this.uniforms.alpha = maskSprite.worldAlpha; + this.uniforms.maskClamp = tex.transform.uClampFrame; filterManager.applyFilter(this, input, output); } diff --git a/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag b/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag index 4a419a1..0e4aef8 100644 --- a/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag +++ b/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag @@ -2,16 +2,18 @@ varying vec2 vTextureCoord; uniform sampler2D uSampler; -uniform float alpha; uniform sampler2D mask; +uniform float alpha; +uniform vec4 maskClamp; void main(void) { - // check clip! this will stop the mask bleeding out from the edges - vec2 text = abs( vMaskCoord - 0.5 ); - text = step(0.5, text); + float clip = step(3.5, + step(maskClamp.x, vMaskCoord.x) + + step(maskClamp.y, vMaskCoord.y) + + step(vMaskCoord.x, maskClamp.z) + + step(vMaskCoord.y, maskClamp.w)); - float clip = 1.0 - max(text.y, text.x); vec4 original = texture2D(uSampler, vTextureCoord); vec4 masky = texture2D(mask, vMaskCoord); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 75eb069..c63af99 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -80,6 +80,7 @@ /** * This is the trimmed area of original texture, before it was put in atlas + * Please call `_updateUvs()` after you change coordinates of `trim` manually. * * @member {PIXI.Rectangle} */ @@ -153,8 +154,10 @@ this._updateID = 0; /** - * Extra field for extra plugins. May contain clamp settings and some matrices - * @type {Object} + * Contains data for uvs. May contain clamp settings and some matrices. + * Its a bit heavy, so by default that object is not created. + * @type {PIXI.TextureMatrix} + * @default null */ this.transform = null; @@ -266,9 +269,7 @@ } /** - * Updates the internal WebGL UV cache. - * - * @protected + * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture. */ _updateUvs() { @@ -538,6 +539,7 @@ /** * The frame specifies the region of the base texture that this texture uses. + * Please call `_updateUvs()` after you change coordinates of `frame` manually. * * @member {PIXI.Rectangle} */ diff --git a/src/core/textures/TextureMatrix.js b/src/core/textures/TextureMatrix.js new file mode 100644 index 0000000..f0b9c12 --- /dev/null +++ b/src/core/textures/TextureMatrix.js @@ -0,0 +1,149 @@ +import { default as Matrix } from '../math/Matrix'; + +const tempMat = new Matrix(); + +/** + * Class controls uv transform and frame clamp for texture + * Can be used in Texture "transform" field, or separately, you can use different clamp settings on the same texture. + * If you want to add support for texture region of certain feature or filter, that's what you're looking for. + * + * @see PIXI.Texture + * @see PIXI.mesh.Mesh + * @see PIXI.extras.TilingSprite + * @class + * @memberof PIXI + */ +export default class TextureMatrix +{ + /** + * + * @param {PIXI.Texture} texture observed texture + * @param {number} [clampMargin] Changes frame clamping, 0.5 by default. Use -0.5 for extra border. + * @constructor + */ + constructor(texture, clampMargin) + { + this._texture = texture; + + this.mapCoord = new Matrix(); + + this.uClampFrame = new Float32Array(4); + + this.uClampOffset = new Float32Array(2); + + this._lastTextureID = -1; + + /** + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders + * + * @default 0 + * @member {number} + */ + this.clampOffset = 0; + + /** + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to -0.5 to add a pixel to the edge, recommended for transparent trimmed textures in atlas + * + * @default 0.5 + * @member {number} + */ + this.clampMargin = (typeof clampMargin === 'undefined') ? 0.5 : clampMargin; + } + + /** + * texture property + * @member {PIXI.Texture} + */ + get texture() + { + return this._texture; + } + + set texture(value) // eslint-disable-line require-jsdoc + { + this._texture = value; + this._lastTextureID = -1; + } + + /** + * Multiplies uvs array to transform + * @param {Float32Array} uvs mesh uvs + * @param {Float32Array} [out=uvs] output + * @returns {Float32Array} output + */ + multiplyUvs(uvs, out) + { + if (out === undefined) + { + out = uvs; + } + + const mat = this.mapCoord; + + for (let i = 0; i < uvs.length; i += 2) + { + const x = uvs[i]; + const y = uvs[i + 1]; + + out[i] = (x * mat.a) + (y * mat.c) + mat.tx; + out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; + } + + return out; + } + + /** + * updates matrices if texture was changed + * @param {boolean} forceUpdate if true, matrices will be updated any case + * @returns {boolean} whether or not it was updated + */ + update(forceUpdate) + { + const tex = this._texture; + + if (!tex || !tex.valid) + { + return false; + } + + if (!forceUpdate + && this._lastTextureID === tex._updateID) + { + return false; + } + + this._lastTextureID = tex._updateID; + + const uvs = tex._uvs; + + this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); + + const orig = tex.orig; + const trim = tex.trim; + + if (trim) + { + tempMat.set(orig.width / trim.width, 0, 0, orig.height / trim.height, + -trim.x / trim.width, -trim.y / trim.height); + this.mapCoord.append(tempMat); + } + + const texBase = tex.baseTexture; + const frame = this.uClampFrame; + const margin = this.clampMargin / texBase.resolution; + const offset = this.clampOffset; + + frame[0] = (tex._frame.x + margin + offset) / texBase.width; + frame[1] = (tex._frame.y + margin + offset) / texBase.height; + frame[2] = (tex._frame.x + tex._frame.width - margin + offset) / texBase.width; + frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; + this.uClampOffset[0] = offset / texBase.realWidth; + this.uClampOffset[1] = offset / texBase.realHeight; + + return true; + } +} diff --git a/src/deprecation.js b/src/deprecation.js index 209910a..dab6e70 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -586,6 +586,28 @@ }); } + if (extras) + { + Object.defineProperties(extras, { + /** + * @class + * @name TextureTransform + * @memberof PIXI.extras + * @see PIXI.TextureMatrix + * @deprecated since version 4.6.0 + */ + TextureTransform: { + get() + { + warn('The TextureTransform class has been renamed to TextureMatrix, ' + + 'please use PIXI.TextureMatrix from now on.'); + + return core.TextureMatrix; + }, + }, + }); + } + core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { warn('generateTexture has moved to the renderer, please use renderer.generateTexture(displayObject)'); diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js deleted file mode 100644 index 16536d2..0000000 --- a/src/extras/TextureTransform.js +++ /dev/null @@ -1,144 +0,0 @@ -import { default as Matrix } from '../core/math/Matrix'; - -const tempMat = new Matrix(); - -/** - * class controls uv transform and frame clamp for texture - * - * @class - * @memberof PIXI.extras - */ -export default class TextureTransform -{ - /** - * - * @param {PIXI.Texture} texture observed texture - * @param {number} [clampMargin] Changes frame clamping, 0.5 by default. Use -0.5 for extra border. - * @constructor - */ - constructor(texture, clampMargin) - { - this._texture = texture; - - this.mapCoord = new Matrix(); - - this.uClampFrame = new Float32Array(4); - - this.uClampOffset = new Float32Array(2); - - this._lastTextureID = -1; - - /** - * Changes frame clamping - * Works with TilingSprite and Mesh - * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders - * - * @default 0 - * @member {number} - */ - this.clampOffset = 0; - - /** - * Changes frame clamping - * Works with TilingSprite and Mesh - * Change to -0.5 to add a pixel to the edge, recommended for transparent trimmed textures in atlas - * - * @default 0.5 - * @member {number} - */ - this.clampMargin = (typeof clampMargin === 'undefined') ? 0.5 : clampMargin; - } - - /** - * texture property - * @member {PIXI.Texture} - */ - get texture() - { - return this._texture; - } - - set texture(value) // eslint-disable-line require-jsdoc - { - this._texture = value; - this._lastTextureID = -1; - } - - /** - * Multiplies uvs array to transform - * @param {Float32Array} uvs mesh uvs - * @param {Float32Array} [out=uvs] output - * @returns {Float32Array} output - */ - multiplyUvs(uvs, out) - { - if (out === undefined) - { - out = uvs; - } - - const mat = this.mapCoord; - - for (let i = 0; i < uvs.length; i += 2) - { - const x = uvs[i]; - const y = uvs[i + 1]; - - out[i] = (x * mat.a) + (y * mat.c) + mat.tx; - out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; - } - - return out; - } - - /** - * updates matrices if texture was changed - * @param {boolean} forceUpdate if true, matrices will be updated any case - * @returns {boolean} whether or not it was updated - */ - update(forceUpdate) - { - const tex = this._texture; - - if (!tex || !tex.valid) - { - return false; - } - - if (!forceUpdate - && this._lastTextureID === tex._updateID) - { - return false; - } - - this._lastTextureID = tex._updateID; - - const uvs = tex._uvs; - - this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); - - const orig = tex.orig; - const trim = tex.trim; - - if (trim) - { - tempMat.set(orig.width / trim.width, 0, 0, orig.height / trim.height, - -trim.x / trim.width, -trim.y / trim.height); - this.mapCoord.append(tempMat); - } - - const texBase = tex.baseTexture; - const frame = this.uClampFrame; - const margin = this.clampMargin / texBase.resolution; - const offset = this.clampOffset; - - frame[0] = (tex._frame.x + margin + offset) / texBase.width; - frame[1] = (tex._frame.y + margin + offset) / texBase.height; - frame[2] = (tex._frame.x + tex._frame.width - margin + offset) / texBase.width; - frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; - this.uClampOffset[0] = offset / texBase.realWidth; - this.uClampOffset[1] = offset / texBase.realHeight; - - return true; - } -} diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 736b207..84c0841 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -1,6 +1,5 @@ import * as core from '../core'; import CanvasTinter from '../core/sprites/canvas/CanvasTinter'; -import { default as TextureTransform } from './TextureTransform'; const tempPoint = new core.Point(); @@ -58,9 +57,9 @@ /** * transform that is applied to UV to get the texture coords * - * @member {PIXI.extras.TextureTransform} + * @member {PIXI.TextureMatrix} */ - this.uvTransform = texture.transform || new TextureTransform(texture); + this.uvTransform = texture.transform || new core.TextureMatrix(texture); /** * Plugin that is responsible for rendering this element. diff --git a/src/extras/index.js b/src/extras/index.js index 5b98cb1..a34d7ec 100644 --- a/src/extras/index.js +++ b/src/extras/index.js @@ -3,7 +3,6 @@ * @namespace PIXI.extras */ export { default as AnimatedSprite } from './AnimatedSprite'; -export { default as TextureTransform } from './TextureTransform'; export { default as TilingSprite } from './TilingSprite'; export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer'; export { default as BitmapText } from './BitmapText'; diff --git a/src/core/index.js b/src/core/index.js index 3265dfb..1a92622 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -33,6 +33,7 @@ export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Spritesheet } from './textures/Spritesheet'; export { default as Texture } from './textures/Texture'; +export { default as TextureMatrix } from './textures/TextureMatrix'; export { default as BaseTexture } from './textures/BaseTexture'; export { default as RenderTexture } from './textures/RenderTexture'; export { default as BaseRenderTexture } from './textures/BaseRenderTexture'; diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js index f955a9d..2d08dbc 100644 --- a/src/core/renderers/webgl/filters/filterTransforms.js +++ b/src/core/renderers/webgl/filters/filterTransforms.js @@ -40,13 +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 texture = sprite._texture.baseTexture; + const orig = sprite._texture.orig; 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); - mappedMatrix.scale(1.0 / texture.width, 1.0 / texture.height); + mappedMatrix.scale(1.0 / orig.width, 1.0 / orig.height); mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); return mappedMatrix; diff --git a/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js b/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js index 1e86c68..e5fd49c 100644 --- a/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js +++ b/src/core/renderers/webgl/filters/spriteMask/SpriteMaskFilter.js @@ -2,6 +2,7 @@ import { Matrix } from '../../../../math'; import { readFileSync } from 'fs'; import { join } from 'path'; +import { default as TextureMatrix } from '../../../../textures/TextureMatrix'; /** * The SpriteMaskFilter class @@ -40,10 +41,25 @@ apply(filterManager, input, output) { const maskSprite = this.maskSprite; + const tex = this.maskSprite.texture; - this.uniforms.mask = maskSprite._texture; - this.uniforms.otherMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, maskSprite); + if (!tex.valid) + { + return; + } + if (!tex.transform) + { + // margin = 0.0, let it bleed a bit, shader code becomes easier + // assuming that atlas textures were made with 1-pixel padding + tex.transform = new TextureMatrix(tex, 0.0); + } + tex.transform.update(); + + this.uniforms.mask = tex; + this.uniforms.otherMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, maskSprite) + .prepend(tex.transform.mapCoord); this.uniforms.alpha = maskSprite.worldAlpha; + this.uniforms.maskClamp = tex.transform.uClampFrame; filterManager.applyFilter(this, input, output); } diff --git a/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag b/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag index 4a419a1..0e4aef8 100644 --- a/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag +++ b/src/core/renderers/webgl/filters/spriteMask/spriteMaskFilter.frag @@ -2,16 +2,18 @@ varying vec2 vTextureCoord; uniform sampler2D uSampler; -uniform float alpha; uniform sampler2D mask; +uniform float alpha; +uniform vec4 maskClamp; void main(void) { - // check clip! this will stop the mask bleeding out from the edges - vec2 text = abs( vMaskCoord - 0.5 ); - text = step(0.5, text); + float clip = step(3.5, + step(maskClamp.x, vMaskCoord.x) + + step(maskClamp.y, vMaskCoord.y) + + step(vMaskCoord.x, maskClamp.z) + + step(vMaskCoord.y, maskClamp.w)); - float clip = 1.0 - max(text.y, text.x); vec4 original = texture2D(uSampler, vTextureCoord); vec4 masky = texture2D(mask, vMaskCoord); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 75eb069..c63af99 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -80,6 +80,7 @@ /** * This is the trimmed area of original texture, before it was put in atlas + * Please call `_updateUvs()` after you change coordinates of `trim` manually. * * @member {PIXI.Rectangle} */ @@ -153,8 +154,10 @@ this._updateID = 0; /** - * Extra field for extra plugins. May contain clamp settings and some matrices - * @type {Object} + * Contains data for uvs. May contain clamp settings and some matrices. + * Its a bit heavy, so by default that object is not created. + * @type {PIXI.TextureMatrix} + * @default null */ this.transform = null; @@ -266,9 +269,7 @@ } /** - * Updates the internal WebGL UV cache. - * - * @protected + * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture. */ _updateUvs() { @@ -538,6 +539,7 @@ /** * The frame specifies the region of the base texture that this texture uses. + * Please call `_updateUvs()` after you change coordinates of `frame` manually. * * @member {PIXI.Rectangle} */ diff --git a/src/core/textures/TextureMatrix.js b/src/core/textures/TextureMatrix.js new file mode 100644 index 0000000..f0b9c12 --- /dev/null +++ b/src/core/textures/TextureMatrix.js @@ -0,0 +1,149 @@ +import { default as Matrix } from '../math/Matrix'; + +const tempMat = new Matrix(); + +/** + * Class controls uv transform and frame clamp for texture + * Can be used in Texture "transform" field, or separately, you can use different clamp settings on the same texture. + * If you want to add support for texture region of certain feature or filter, that's what you're looking for. + * + * @see PIXI.Texture + * @see PIXI.mesh.Mesh + * @see PIXI.extras.TilingSprite + * @class + * @memberof PIXI + */ +export default class TextureMatrix +{ + /** + * + * @param {PIXI.Texture} texture observed texture + * @param {number} [clampMargin] Changes frame clamping, 0.5 by default. Use -0.5 for extra border. + * @constructor + */ + constructor(texture, clampMargin) + { + this._texture = texture; + + this.mapCoord = new Matrix(); + + this.uClampFrame = new Float32Array(4); + + this.uClampOffset = new Float32Array(2); + + this._lastTextureID = -1; + + /** + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders + * + * @default 0 + * @member {number} + */ + this.clampOffset = 0; + + /** + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to -0.5 to add a pixel to the edge, recommended for transparent trimmed textures in atlas + * + * @default 0.5 + * @member {number} + */ + this.clampMargin = (typeof clampMargin === 'undefined') ? 0.5 : clampMargin; + } + + /** + * texture property + * @member {PIXI.Texture} + */ + get texture() + { + return this._texture; + } + + set texture(value) // eslint-disable-line require-jsdoc + { + this._texture = value; + this._lastTextureID = -1; + } + + /** + * Multiplies uvs array to transform + * @param {Float32Array} uvs mesh uvs + * @param {Float32Array} [out=uvs] output + * @returns {Float32Array} output + */ + multiplyUvs(uvs, out) + { + if (out === undefined) + { + out = uvs; + } + + const mat = this.mapCoord; + + for (let i = 0; i < uvs.length; i += 2) + { + const x = uvs[i]; + const y = uvs[i + 1]; + + out[i] = (x * mat.a) + (y * mat.c) + mat.tx; + out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; + } + + return out; + } + + /** + * updates matrices if texture was changed + * @param {boolean} forceUpdate if true, matrices will be updated any case + * @returns {boolean} whether or not it was updated + */ + update(forceUpdate) + { + const tex = this._texture; + + if (!tex || !tex.valid) + { + return false; + } + + if (!forceUpdate + && this._lastTextureID === tex._updateID) + { + return false; + } + + this._lastTextureID = tex._updateID; + + const uvs = tex._uvs; + + this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); + + const orig = tex.orig; + const trim = tex.trim; + + if (trim) + { + tempMat.set(orig.width / trim.width, 0, 0, orig.height / trim.height, + -trim.x / trim.width, -trim.y / trim.height); + this.mapCoord.append(tempMat); + } + + const texBase = tex.baseTexture; + const frame = this.uClampFrame; + const margin = this.clampMargin / texBase.resolution; + const offset = this.clampOffset; + + frame[0] = (tex._frame.x + margin + offset) / texBase.width; + frame[1] = (tex._frame.y + margin + offset) / texBase.height; + frame[2] = (tex._frame.x + tex._frame.width - margin + offset) / texBase.width; + frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; + this.uClampOffset[0] = offset / texBase.realWidth; + this.uClampOffset[1] = offset / texBase.realHeight; + + return true; + } +} diff --git a/src/deprecation.js b/src/deprecation.js index 209910a..dab6e70 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -586,6 +586,28 @@ }); } + if (extras) + { + Object.defineProperties(extras, { + /** + * @class + * @name TextureTransform + * @memberof PIXI.extras + * @see PIXI.TextureMatrix + * @deprecated since version 4.6.0 + */ + TextureTransform: { + get() + { + warn('The TextureTransform class has been renamed to TextureMatrix, ' + + 'please use PIXI.TextureMatrix from now on.'); + + return core.TextureMatrix; + }, + }, + }); + } + core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { warn('generateTexture has moved to the renderer, please use renderer.generateTexture(displayObject)'); diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js deleted file mode 100644 index 16536d2..0000000 --- a/src/extras/TextureTransform.js +++ /dev/null @@ -1,144 +0,0 @@ -import { default as Matrix } from '../core/math/Matrix'; - -const tempMat = new Matrix(); - -/** - * class controls uv transform and frame clamp for texture - * - * @class - * @memberof PIXI.extras - */ -export default class TextureTransform -{ - /** - * - * @param {PIXI.Texture} texture observed texture - * @param {number} [clampMargin] Changes frame clamping, 0.5 by default. Use -0.5 for extra border. - * @constructor - */ - constructor(texture, clampMargin) - { - this._texture = texture; - - this.mapCoord = new Matrix(); - - this.uClampFrame = new Float32Array(4); - - this.uClampOffset = new Float32Array(2); - - this._lastTextureID = -1; - - /** - * Changes frame clamping - * Works with TilingSprite and Mesh - * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders - * - * @default 0 - * @member {number} - */ - this.clampOffset = 0; - - /** - * Changes frame clamping - * Works with TilingSprite and Mesh - * Change to -0.5 to add a pixel to the edge, recommended for transparent trimmed textures in atlas - * - * @default 0.5 - * @member {number} - */ - this.clampMargin = (typeof clampMargin === 'undefined') ? 0.5 : clampMargin; - } - - /** - * texture property - * @member {PIXI.Texture} - */ - get texture() - { - return this._texture; - } - - set texture(value) // eslint-disable-line require-jsdoc - { - this._texture = value; - this._lastTextureID = -1; - } - - /** - * Multiplies uvs array to transform - * @param {Float32Array} uvs mesh uvs - * @param {Float32Array} [out=uvs] output - * @returns {Float32Array} output - */ - multiplyUvs(uvs, out) - { - if (out === undefined) - { - out = uvs; - } - - const mat = this.mapCoord; - - for (let i = 0; i < uvs.length; i += 2) - { - const x = uvs[i]; - const y = uvs[i + 1]; - - out[i] = (x * mat.a) + (y * mat.c) + mat.tx; - out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; - } - - return out; - } - - /** - * updates matrices if texture was changed - * @param {boolean} forceUpdate if true, matrices will be updated any case - * @returns {boolean} whether or not it was updated - */ - update(forceUpdate) - { - const tex = this._texture; - - if (!tex || !tex.valid) - { - return false; - } - - if (!forceUpdate - && this._lastTextureID === tex._updateID) - { - return false; - } - - this._lastTextureID = tex._updateID; - - const uvs = tex._uvs; - - this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); - - const orig = tex.orig; - const trim = tex.trim; - - if (trim) - { - tempMat.set(orig.width / trim.width, 0, 0, orig.height / trim.height, - -trim.x / trim.width, -trim.y / trim.height); - this.mapCoord.append(tempMat); - } - - const texBase = tex.baseTexture; - const frame = this.uClampFrame; - const margin = this.clampMargin / texBase.resolution; - const offset = this.clampOffset; - - frame[0] = (tex._frame.x + margin + offset) / texBase.width; - frame[1] = (tex._frame.y + margin + offset) / texBase.height; - frame[2] = (tex._frame.x + tex._frame.width - margin + offset) / texBase.width; - frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; - this.uClampOffset[0] = offset / texBase.realWidth; - this.uClampOffset[1] = offset / texBase.realHeight; - - return true; - } -} diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 736b207..84c0841 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -1,6 +1,5 @@ import * as core from '../core'; import CanvasTinter from '../core/sprites/canvas/CanvasTinter'; -import { default as TextureTransform } from './TextureTransform'; const tempPoint = new core.Point(); @@ -58,9 +57,9 @@ /** * transform that is applied to UV to get the texture coords * - * @member {PIXI.extras.TextureTransform} + * @member {PIXI.TextureMatrix} */ - this.uvTransform = texture.transform || new TextureTransform(texture); + this.uvTransform = texture.transform || new core.TextureMatrix(texture); /** * Plugin that is responsible for rendering this element. diff --git a/src/extras/index.js b/src/extras/index.js index 5b98cb1..a34d7ec 100644 --- a/src/extras/index.js +++ b/src/extras/index.js @@ -3,7 +3,6 @@ * @namespace PIXI.extras */ export { default as AnimatedSprite } from './AnimatedSprite'; -export { default as TextureTransform } from './TextureTransform'; export { default as TilingSprite } from './TilingSprite'; export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer'; export { default as BitmapText } from './BitmapText'; diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 5d01414..7006a3e 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,6 +1,5 @@ import * as core from '../core'; import Texture from '../core/textures/Texture'; -import { default as TextureTransform } from '../extras/TextureTransform'; const tempPoint = new core.Point(); const tempPolygon = new core.Polygon(); @@ -131,10 +130,10 @@ * its updated independently from texture uvTransform * updates of uvs are tied to that thing * - * @member {PIXI.extras.TextureTransform} + * @member {PIXI.TextureMatrix} * @private */ - this._uvTransform = new TextureTransform(texture); + this._uvTransform = new core.TextureMatrix(this._texture); /** * whether or not upload uvTransform to shader