diff --git a/src/core/index.js b/src/core/index.js index f2b3af1..1de5da5 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -29,6 +29,7 @@ export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer'; export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Texture } from './textures/Texture'; +export { default as TextureTransform } from './textures/TextureTransform'; 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 f2b3af1..1de5da5 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -29,6 +29,7 @@ export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer'; export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Texture } from './textures/Texture'; +export { default as TextureTransform } from './textures/TextureTransform'; 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/textures/Texture.js b/src/core/textures/Texture.js index bab0a01..95a0b61 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -141,6 +141,27 @@ */ this._updateID = 0; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * By default, clamps bottom and right edge of texture. + * Change to 0 if you texture has repeated right and bottom lines, that leads to smoother borders + * @default 1 + * @member {number} + */ + this.smoothEdge1 = 1; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to 0 to add a pixel to the edge, recommended for transparent trimmed textures in atlas + * @default 0 + * @member {number} + */ + this.smoothEdge2 = 0; } /** @@ -258,6 +279,24 @@ } /** + * returns uvs + * @member PIXI.TextureUvs# + * @memberof PIXI.Texture# + */ + get uvs() + { + return this._uvs; + } + + /** + * Updates uvs accordingly to the texture + */ + updateUvs() + { + this._updateUvs(); + } + + /** * Helper function that creates a Texture object from the given image url. * If the image is not in the texture cache it will be created and loaded. * diff --git a/src/core/index.js b/src/core/index.js index f2b3af1..1de5da5 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -29,6 +29,7 @@ export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer'; export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Texture } from './textures/Texture'; +export { default as TextureTransform } from './textures/TextureTransform'; 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/textures/Texture.js b/src/core/textures/Texture.js index bab0a01..95a0b61 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -141,6 +141,27 @@ */ this._updateID = 0; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * By default, clamps bottom and right edge of texture. + * Change to 0 if you texture has repeated right and bottom lines, that leads to smoother borders + * @default 1 + * @member {number} + */ + this.smoothEdge1 = 1; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to 0 to add a pixel to the edge, recommended for transparent trimmed textures in atlas + * @default 0 + * @member {number} + */ + this.smoothEdge2 = 0; } /** @@ -258,6 +279,24 @@ } /** + * returns uvs + * @member PIXI.TextureUvs# + * @memberof PIXI.Texture# + */ + get uvs() + { + return this._uvs; + } + + /** + * Updates uvs accordingly to the texture + */ + updateUvs() + { + this._updateUvs(); + } + + /** * Helper function that creates a Texture object from the given image url. * If the image is not in the texture cache it will be created and loaded. * diff --git a/src/core/textures/TextureTransform.js b/src/core/textures/TextureTransform.js new file mode 100644 index 0000000..6dfa2d9 --- /dev/null +++ b/src/core/textures/TextureTransform.js @@ -0,0 +1,90 @@ +import { default as Matrix } from '../math/Matrix'; + +const tempMat = new Matrix(); + +/** + * class controls uv transform and frame clamp for texture + */ +export default class TextureTransform { + /** + * + * @param {PIXI.Texture} texture observed texture + * @constructor + */ + constructor(texture) + { + this._texture = texture; + + this.mapCoord = new Matrix(); + + this.frameClamp = new Float32Array(4); + + this._lastTextureID = -1; + + this.update(); + } + + /** + * texture property + * @member {PIXI.Texture} + * @memberof PIXI.TextureTransform + */ + get texture() + { + return this._texture; + } + + /** + * sets texture value + * @param {PIXI.Texture} value texture to be set + */ + set texture(value) + { + this._texture = value; + this._lastTextureID = -1; + } + + /** + * updates matrices if texture was changed + * @param {boolean} forceUpdate if true, matrices will be updated any case + */ + update(forceUpdate) + { + const tex = this.texture; + + if (!tex || !tex.valid) + { + return; + } + + if (!forceUpdate + && this._lastTextureID === this.texture._updateID) + { + return; + } + + this._lastTextureID = this.texture._updateID; + + const uvs = this.texture._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.frameClamp; + + frame[0] = (tex._frame.x + tex.smoothEdge2) / texBase.width; + frame[1] = (tex._frame.y + tex.smoothEdge2) / texBase.height; + frame[2] = (tex._frame.x + tex._frame.width - tex.smoothEdge1 + tex.smoothEdge2) / texBase.width; + frame[3] = (tex._frame.y + tex._frame.height - tex.smoothEdge1 + tex.smoothEdge2) / texBase.height; + } +} diff --git a/src/core/index.js b/src/core/index.js index f2b3af1..1de5da5 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -29,6 +29,7 @@ export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer'; export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Texture } from './textures/Texture'; +export { default as TextureTransform } from './textures/TextureTransform'; 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/textures/Texture.js b/src/core/textures/Texture.js index bab0a01..95a0b61 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -141,6 +141,27 @@ */ this._updateID = 0; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * By default, clamps bottom and right edge of texture. + * Change to 0 if you texture has repeated right and bottom lines, that leads to smoother borders + * @default 1 + * @member {number} + */ + this.smoothEdge1 = 1; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to 0 to add a pixel to the edge, recommended for transparent trimmed textures in atlas + * @default 0 + * @member {number} + */ + this.smoothEdge2 = 0; } /** @@ -258,6 +279,24 @@ } /** + * returns uvs + * @member PIXI.TextureUvs# + * @memberof PIXI.Texture# + */ + get uvs() + { + return this._uvs; + } + + /** + * Updates uvs accordingly to the texture + */ + updateUvs() + { + this._updateUvs(); + } + + /** * Helper function that creates a Texture object from the given image url. * If the image is not in the texture cache it will be created and loaded. * diff --git a/src/core/textures/TextureTransform.js b/src/core/textures/TextureTransform.js new file mode 100644 index 0000000..6dfa2d9 --- /dev/null +++ b/src/core/textures/TextureTransform.js @@ -0,0 +1,90 @@ +import { default as Matrix } from '../math/Matrix'; + +const tempMat = new Matrix(); + +/** + * class controls uv transform and frame clamp for texture + */ +export default class TextureTransform { + /** + * + * @param {PIXI.Texture} texture observed texture + * @constructor + */ + constructor(texture) + { + this._texture = texture; + + this.mapCoord = new Matrix(); + + this.frameClamp = new Float32Array(4); + + this._lastTextureID = -1; + + this.update(); + } + + /** + * texture property + * @member {PIXI.Texture} + * @memberof PIXI.TextureTransform + */ + get texture() + { + return this._texture; + } + + /** + * sets texture value + * @param {PIXI.Texture} value texture to be set + */ + set texture(value) + { + this._texture = value; + this._lastTextureID = -1; + } + + /** + * updates matrices if texture was changed + * @param {boolean} forceUpdate if true, matrices will be updated any case + */ + update(forceUpdate) + { + const tex = this.texture; + + if (!tex || !tex.valid) + { + return; + } + + if (!forceUpdate + && this._lastTextureID === this.texture._updateID) + { + return; + } + + this._lastTextureID = this.texture._updateID; + + const uvs = this.texture._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.frameClamp; + + frame[0] = (tex._frame.x + tex.smoothEdge2) / texBase.width; + frame[1] = (tex._frame.y + tex.smoothEdge2) / texBase.height; + frame[2] = (tex._frame.x + tex._frame.width - tex.smoothEdge1 + tex.smoothEdge2) / texBase.width; + frame[3] = (tex._frame.y + tex._frame.height - tex.smoothEdge1 + tex.smoothEdge2) / texBase.height; + } +} diff --git a/src/core/textures/TextureUvs.js b/src/core/textures/TextureUvs.js index 0ed66e6..cfa8c0b 100644 --- a/src/core/textures/TextureUvs.js +++ b/src/core/textures/TextureUvs.js @@ -27,6 +27,7 @@ this.y3 = 1; this.uvsUint32 = new Uint32Array(4); + this.uvsMatrix = null; } /** diff --git a/src/core/index.js b/src/core/index.js index f2b3af1..1de5da5 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -29,6 +29,7 @@ export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer'; export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Texture } from './textures/Texture'; +export { default as TextureTransform } from './textures/TextureTransform'; 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/textures/Texture.js b/src/core/textures/Texture.js index bab0a01..95a0b61 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -141,6 +141,27 @@ */ this._updateID = 0; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * By default, clamps bottom and right edge of texture. + * Change to 0 if you texture has repeated right and bottom lines, that leads to smoother borders + * @default 1 + * @member {number} + */ + this.smoothEdge1 = 1; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to 0 to add a pixel to the edge, recommended for transparent trimmed textures in atlas + * @default 0 + * @member {number} + */ + this.smoothEdge2 = 0; } /** @@ -258,6 +279,24 @@ } /** + * returns uvs + * @member PIXI.TextureUvs# + * @memberof PIXI.Texture# + */ + get uvs() + { + return this._uvs; + } + + /** + * Updates uvs accordingly to the texture + */ + updateUvs() + { + this._updateUvs(); + } + + /** * Helper function that creates a Texture object from the given image url. * If the image is not in the texture cache it will be created and loaded. * diff --git a/src/core/textures/TextureTransform.js b/src/core/textures/TextureTransform.js new file mode 100644 index 0000000..6dfa2d9 --- /dev/null +++ b/src/core/textures/TextureTransform.js @@ -0,0 +1,90 @@ +import { default as Matrix } from '../math/Matrix'; + +const tempMat = new Matrix(); + +/** + * class controls uv transform and frame clamp for texture + */ +export default class TextureTransform { + /** + * + * @param {PIXI.Texture} texture observed texture + * @constructor + */ + constructor(texture) + { + this._texture = texture; + + this.mapCoord = new Matrix(); + + this.frameClamp = new Float32Array(4); + + this._lastTextureID = -1; + + this.update(); + } + + /** + * texture property + * @member {PIXI.Texture} + * @memberof PIXI.TextureTransform + */ + get texture() + { + return this._texture; + } + + /** + * sets texture value + * @param {PIXI.Texture} value texture to be set + */ + set texture(value) + { + this._texture = value; + this._lastTextureID = -1; + } + + /** + * updates matrices if texture was changed + * @param {boolean} forceUpdate if true, matrices will be updated any case + */ + update(forceUpdate) + { + const tex = this.texture; + + if (!tex || !tex.valid) + { + return; + } + + if (!forceUpdate + && this._lastTextureID === this.texture._updateID) + { + return; + } + + this._lastTextureID = this.texture._updateID; + + const uvs = this.texture._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.frameClamp; + + frame[0] = (tex._frame.x + tex.smoothEdge2) / texBase.width; + frame[1] = (tex._frame.y + tex.smoothEdge2) / texBase.height; + frame[2] = (tex._frame.x + tex._frame.width - tex.smoothEdge1 + tex.smoothEdge2) / texBase.width; + frame[3] = (tex._frame.y + tex._frame.height - tex.smoothEdge1 + tex.smoothEdge2) / texBase.height; + } +} diff --git a/src/core/textures/TextureUvs.js b/src/core/textures/TextureUvs.js index 0ed66e6..cfa8c0b 100644 --- a/src/core/textures/TextureUvs.js +++ b/src/core/textures/TextureUvs.js @@ -27,6 +27,7 @@ this.y3 = 1; this.uvsUint32 = new Uint32Array(4); + this.uvsMatrix = null; } /** diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index a0174e7..1e3a999 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -1,10 +1,6 @@ import * as core from '../core'; -import Texture from '../core/textures/Texture'; import CanvasTinter from '../core/sprites/canvas/CanvasTinter'; -import TilingShader from './webgl/TilingShader'; -import { Rectangle } from '../core/math'; -const tempArray = new Float32Array(4); const tempPoint = new core.Point(); /** @@ -26,18 +22,11 @@ super(texture); /** - * The scaling of the image that is being tiled + * Tile transform * - * @member {PIXI.Point} + * @member {PIXI.TransformStatic} */ - this.tileScale = new core.Point(1, 1); - - /** - * The offset position of the image that is being tiled - * - * @member {PIXI.Point} - */ - this.tilePosition = new core.Point(0, 0); + this.tileTransform = new core.TransformStatic(); // /// private @@ -57,17 +46,67 @@ */ this._height = height; - /** - * An internal WebGL UV cache. - * - * @member {PIXI.TextureUvs} - * @private - */ - this._uvs = new core.TextureUvs(); - this._canvasPattern = null; this._glDatas = []; + + /** + * transform that is applied to UV to get the texture coords + * @member {PIXI.TextureTransform} + */ + this.uvTransform = new core.TextureTransform(texture); + } + + /** + * setter for clampEdge + * @param {number} value assigned value + */ + set clampEdge(value) + { + this.uvTransform.clampEdge = value; + this.uvTransform.update(true); + } + + /** + * The scaling of the image that is being tiled + * + * @member {PIXI.ObservablePoint} + * @memberof PIXI.DisplayObject# + */ + get tileScale() + { + return this.tileTransform.scale; + } + + /** + * Copies the point to the scale of the tiled image. + * + * @param {PIXI.Point|PIXI.ObservablePoint} value - The value to set to. + */ + set tileScale(value) + { + this.tileTransform.scale.copy(value); + } + + /** + * The offset of the image that is being tiled + * + * @member {PIXI.ObservablePoint} + * @memberof PIXI.TilingSprite# + */ + get tilePosition() + { + return this.tileTransform.position; + } + + /** + * Copies the point to the position of the tiled image. + * + * @param {PIXI.Point|PIXI.ObservablePoint} value - The value to set to. + */ + set tilePosition(value) + { + this.tileTransform.position.copy(value); } /** @@ -75,7 +114,7 @@ */ _onTextureUpdate() { - return; + this.uvTransform.texture = this._texture; } /** @@ -89,84 +128,16 @@ // tweak our texture temporarily.. const texture = this._texture; - if (!texture || !texture._uvs) + if (!texture || !texture.valid) { return; } - // get rid of any thing that may be batching. - renderer.flush(); + this.tileTransform.updateLocalTransform(); + this.uvTransform.update(); - const gl = renderer.gl; - let glData = this._glDatas[renderer.CONTEXT_UID]; - - if (!glData) - { - glData = { - shader: new TilingShader(gl), - quad: new core.Quad(gl), - }; - - this._glDatas[renderer.CONTEXT_UID] = glData; - - glData.quad.initVao(glData.shader); - } - - // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space - // before transforming the sprite coords.. - const vertices = glData.quad.vertices; - - vertices[0] = vertices[6] = (this._width) * -this.anchor.x; - vertices[1] = vertices[3] = this._height * -this.anchor.y; - - vertices[2] = vertices[4] = (this._width) * (1 - this.anchor.x); - vertices[5] = vertices[7] = this._height * (1 - this.anchor.y); - - glData.quad.upload(); - - renderer.bindShader(glData.shader); - - const textureUvs = texture._uvs; - const textureWidth = texture._frame.width; - const textureHeight = texture._frame.height; - const textureBaseWidth = texture.baseTexture.width; - const textureBaseHeight = texture.baseTexture.height; - - const uPixelSize = glData.shader.uniforms.uPixelSize; - - uPixelSize[0] = 1.0 / textureBaseWidth; - uPixelSize[1] = 1.0 / textureBaseHeight; - glData.shader.uniforms.uPixelSize = uPixelSize; - - const uFrame = glData.shader.uniforms.uFrame; - - uFrame[0] = textureUvs.x0; - uFrame[1] = textureUvs.y0; - uFrame[2] = textureUvs.x1 - textureUvs.x0; - uFrame[3] = textureUvs.y2 - textureUvs.y0; - glData.shader.uniforms.uFrame = uFrame; - - const uTransform = glData.shader.uniforms.uTransform; - - uTransform[0] = (this.tilePosition.x % (textureWidth * this.tileScale.x)) / this._width; - uTransform[1] = (this.tilePosition.y % (textureHeight * this.tileScale.y)) / this._height; - uTransform[2] = (textureBaseWidth / this._width) * this.tileScale.x; - uTransform[3] = (textureBaseHeight / this._height) * this.tileScale.y; - glData.shader.uniforms.uTransform = uTransform; - - glData.shader.uniforms.translationMatrix = this.worldTransform.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(this.tint, color); - color[3] = this.worldAlpha; - - glData.shader.uniforms.uColor = color; - - renderer.bindTexture(this._texture, 0); - - renderer.state.setBlendMode(this.blendMode); - glData.quad.draw(); + renderer.setObjectRenderer(renderer.plugins.tiling); + renderer.plugins.tiling.render(this); } /** @@ -409,7 +380,7 @@ */ static from(source, width, height) { - return new TilingSprite(Texture.from(source), width, height); + return new TilingSprite(core.Texture.from(source), width, height); } /** diff --git a/src/core/index.js b/src/core/index.js index f2b3af1..1de5da5 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -29,6 +29,7 @@ export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer'; export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Texture } from './textures/Texture'; +export { default as TextureTransform } from './textures/TextureTransform'; 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/textures/Texture.js b/src/core/textures/Texture.js index bab0a01..95a0b61 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -141,6 +141,27 @@ */ this._updateID = 0; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * By default, clamps bottom and right edge of texture. + * Change to 0 if you texture has repeated right and bottom lines, that leads to smoother borders + * @default 1 + * @member {number} + */ + this.smoothEdge1 = 1; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to 0 to add a pixel to the edge, recommended for transparent trimmed textures in atlas + * @default 0 + * @member {number} + */ + this.smoothEdge2 = 0; } /** @@ -258,6 +279,24 @@ } /** + * returns uvs + * @member PIXI.TextureUvs# + * @memberof PIXI.Texture# + */ + get uvs() + { + return this._uvs; + } + + /** + * Updates uvs accordingly to the texture + */ + updateUvs() + { + this._updateUvs(); + } + + /** * Helper function that creates a Texture object from the given image url. * If the image is not in the texture cache it will be created and loaded. * diff --git a/src/core/textures/TextureTransform.js b/src/core/textures/TextureTransform.js new file mode 100644 index 0000000..6dfa2d9 --- /dev/null +++ b/src/core/textures/TextureTransform.js @@ -0,0 +1,90 @@ +import { default as Matrix } from '../math/Matrix'; + +const tempMat = new Matrix(); + +/** + * class controls uv transform and frame clamp for texture + */ +export default class TextureTransform { + /** + * + * @param {PIXI.Texture} texture observed texture + * @constructor + */ + constructor(texture) + { + this._texture = texture; + + this.mapCoord = new Matrix(); + + this.frameClamp = new Float32Array(4); + + this._lastTextureID = -1; + + this.update(); + } + + /** + * texture property + * @member {PIXI.Texture} + * @memberof PIXI.TextureTransform + */ + get texture() + { + return this._texture; + } + + /** + * sets texture value + * @param {PIXI.Texture} value texture to be set + */ + set texture(value) + { + this._texture = value; + this._lastTextureID = -1; + } + + /** + * updates matrices if texture was changed + * @param {boolean} forceUpdate if true, matrices will be updated any case + */ + update(forceUpdate) + { + const tex = this.texture; + + if (!tex || !tex.valid) + { + return; + } + + if (!forceUpdate + && this._lastTextureID === this.texture._updateID) + { + return; + } + + this._lastTextureID = this.texture._updateID; + + const uvs = this.texture._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.frameClamp; + + frame[0] = (tex._frame.x + tex.smoothEdge2) / texBase.width; + frame[1] = (tex._frame.y + tex.smoothEdge2) / texBase.height; + frame[2] = (tex._frame.x + tex._frame.width - tex.smoothEdge1 + tex.smoothEdge2) / texBase.width; + frame[3] = (tex._frame.y + tex._frame.height - tex.smoothEdge1 + tex.smoothEdge2) / texBase.height; + } +} diff --git a/src/core/textures/TextureUvs.js b/src/core/textures/TextureUvs.js index 0ed66e6..cfa8c0b 100644 --- a/src/core/textures/TextureUvs.js +++ b/src/core/textures/TextureUvs.js @@ -27,6 +27,7 @@ this.y3 = 1; this.uvsUint32 = new Uint32Array(4); + this.uvsMatrix = null; } /** diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index a0174e7..1e3a999 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -1,10 +1,6 @@ import * as core from '../core'; -import Texture from '../core/textures/Texture'; import CanvasTinter from '../core/sprites/canvas/CanvasTinter'; -import TilingShader from './webgl/TilingShader'; -import { Rectangle } from '../core/math'; -const tempArray = new Float32Array(4); const tempPoint = new core.Point(); /** @@ -26,18 +22,11 @@ super(texture); /** - * The scaling of the image that is being tiled + * Tile transform * - * @member {PIXI.Point} + * @member {PIXI.TransformStatic} */ - this.tileScale = new core.Point(1, 1); - - /** - * The offset position of the image that is being tiled - * - * @member {PIXI.Point} - */ - this.tilePosition = new core.Point(0, 0); + this.tileTransform = new core.TransformStatic(); // /// private @@ -57,17 +46,67 @@ */ this._height = height; - /** - * An internal WebGL UV cache. - * - * @member {PIXI.TextureUvs} - * @private - */ - this._uvs = new core.TextureUvs(); - this._canvasPattern = null; this._glDatas = []; + + /** + * transform that is applied to UV to get the texture coords + * @member {PIXI.TextureTransform} + */ + this.uvTransform = new core.TextureTransform(texture); + } + + /** + * setter for clampEdge + * @param {number} value assigned value + */ + set clampEdge(value) + { + this.uvTransform.clampEdge = value; + this.uvTransform.update(true); + } + + /** + * The scaling of the image that is being tiled + * + * @member {PIXI.ObservablePoint} + * @memberof PIXI.DisplayObject# + */ + get tileScale() + { + return this.tileTransform.scale; + } + + /** + * Copies the point to the scale of the tiled image. + * + * @param {PIXI.Point|PIXI.ObservablePoint} value - The value to set to. + */ + set tileScale(value) + { + this.tileTransform.scale.copy(value); + } + + /** + * The offset of the image that is being tiled + * + * @member {PIXI.ObservablePoint} + * @memberof PIXI.TilingSprite# + */ + get tilePosition() + { + return this.tileTransform.position; + } + + /** + * Copies the point to the position of the tiled image. + * + * @param {PIXI.Point|PIXI.ObservablePoint} value - The value to set to. + */ + set tilePosition(value) + { + this.tileTransform.position.copy(value); } /** @@ -75,7 +114,7 @@ */ _onTextureUpdate() { - return; + this.uvTransform.texture = this._texture; } /** @@ -89,84 +128,16 @@ // tweak our texture temporarily.. const texture = this._texture; - if (!texture || !texture._uvs) + if (!texture || !texture.valid) { return; } - // get rid of any thing that may be batching. - renderer.flush(); + this.tileTransform.updateLocalTransform(); + this.uvTransform.update(); - const gl = renderer.gl; - let glData = this._glDatas[renderer.CONTEXT_UID]; - - if (!glData) - { - glData = { - shader: new TilingShader(gl), - quad: new core.Quad(gl), - }; - - this._glDatas[renderer.CONTEXT_UID] = glData; - - glData.quad.initVao(glData.shader); - } - - // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space - // before transforming the sprite coords.. - const vertices = glData.quad.vertices; - - vertices[0] = vertices[6] = (this._width) * -this.anchor.x; - vertices[1] = vertices[3] = this._height * -this.anchor.y; - - vertices[2] = vertices[4] = (this._width) * (1 - this.anchor.x); - vertices[5] = vertices[7] = this._height * (1 - this.anchor.y); - - glData.quad.upload(); - - renderer.bindShader(glData.shader); - - const textureUvs = texture._uvs; - const textureWidth = texture._frame.width; - const textureHeight = texture._frame.height; - const textureBaseWidth = texture.baseTexture.width; - const textureBaseHeight = texture.baseTexture.height; - - const uPixelSize = glData.shader.uniforms.uPixelSize; - - uPixelSize[0] = 1.0 / textureBaseWidth; - uPixelSize[1] = 1.0 / textureBaseHeight; - glData.shader.uniforms.uPixelSize = uPixelSize; - - const uFrame = glData.shader.uniforms.uFrame; - - uFrame[0] = textureUvs.x0; - uFrame[1] = textureUvs.y0; - uFrame[2] = textureUvs.x1 - textureUvs.x0; - uFrame[3] = textureUvs.y2 - textureUvs.y0; - glData.shader.uniforms.uFrame = uFrame; - - const uTransform = glData.shader.uniforms.uTransform; - - uTransform[0] = (this.tilePosition.x % (textureWidth * this.tileScale.x)) / this._width; - uTransform[1] = (this.tilePosition.y % (textureHeight * this.tileScale.y)) / this._height; - uTransform[2] = (textureBaseWidth / this._width) * this.tileScale.x; - uTransform[3] = (textureBaseHeight / this._height) * this.tileScale.y; - glData.shader.uniforms.uTransform = uTransform; - - glData.shader.uniforms.translationMatrix = this.worldTransform.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(this.tint, color); - color[3] = this.worldAlpha; - - glData.shader.uniforms.uColor = color; - - renderer.bindTexture(this._texture, 0); - - renderer.state.setBlendMode(this.blendMode); - glData.quad.draw(); + renderer.setObjectRenderer(renderer.plugins.tiling); + renderer.plugins.tiling.render(this); } /** @@ -409,7 +380,7 @@ */ static from(source, width, height) { - return new TilingSprite(Texture.from(source), width, height); + return new TilingSprite(core.Texture.from(source), width, height); } /** diff --git a/src/extras/index.js b/src/extras/index.js index 73bed41..1a90436 100644 --- a/src/extras/index.js +++ b/src/extras/index.js @@ -3,6 +3,7 @@ */ export { default as MovieClip } from './MovieClip'; export { default as TilingSprite } from './TilingSprite'; +export { default as TilingRenderer } from './webgl/TilingRenderer'; export { default as BitmapText } from './BitmapText'; export { default as cacheAsBitmap } from './cacheAsBitmap'; diff --git a/src/core/index.js b/src/core/index.js index f2b3af1..1de5da5 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -29,6 +29,7 @@ export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer'; export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Texture } from './textures/Texture'; +export { default as TextureTransform } from './textures/TextureTransform'; 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/textures/Texture.js b/src/core/textures/Texture.js index bab0a01..95a0b61 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -141,6 +141,27 @@ */ this._updateID = 0; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * By default, clamps bottom and right edge of texture. + * Change to 0 if you texture has repeated right and bottom lines, that leads to smoother borders + * @default 1 + * @member {number} + */ + this.smoothEdge1 = 1; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to 0 to add a pixel to the edge, recommended for transparent trimmed textures in atlas + * @default 0 + * @member {number} + */ + this.smoothEdge2 = 0; } /** @@ -258,6 +279,24 @@ } /** + * returns uvs + * @member PIXI.TextureUvs# + * @memberof PIXI.Texture# + */ + get uvs() + { + return this._uvs; + } + + /** + * Updates uvs accordingly to the texture + */ + updateUvs() + { + this._updateUvs(); + } + + /** * Helper function that creates a Texture object from the given image url. * If the image is not in the texture cache it will be created and loaded. * diff --git a/src/core/textures/TextureTransform.js b/src/core/textures/TextureTransform.js new file mode 100644 index 0000000..6dfa2d9 --- /dev/null +++ b/src/core/textures/TextureTransform.js @@ -0,0 +1,90 @@ +import { default as Matrix } from '../math/Matrix'; + +const tempMat = new Matrix(); + +/** + * class controls uv transform and frame clamp for texture + */ +export default class TextureTransform { + /** + * + * @param {PIXI.Texture} texture observed texture + * @constructor + */ + constructor(texture) + { + this._texture = texture; + + this.mapCoord = new Matrix(); + + this.frameClamp = new Float32Array(4); + + this._lastTextureID = -1; + + this.update(); + } + + /** + * texture property + * @member {PIXI.Texture} + * @memberof PIXI.TextureTransform + */ + get texture() + { + return this._texture; + } + + /** + * sets texture value + * @param {PIXI.Texture} value texture to be set + */ + set texture(value) + { + this._texture = value; + this._lastTextureID = -1; + } + + /** + * updates matrices if texture was changed + * @param {boolean} forceUpdate if true, matrices will be updated any case + */ + update(forceUpdate) + { + const tex = this.texture; + + if (!tex || !tex.valid) + { + return; + } + + if (!forceUpdate + && this._lastTextureID === this.texture._updateID) + { + return; + } + + this._lastTextureID = this.texture._updateID; + + const uvs = this.texture._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.frameClamp; + + frame[0] = (tex._frame.x + tex.smoothEdge2) / texBase.width; + frame[1] = (tex._frame.y + tex.smoothEdge2) / texBase.height; + frame[2] = (tex._frame.x + tex._frame.width - tex.smoothEdge1 + tex.smoothEdge2) / texBase.width; + frame[3] = (tex._frame.y + tex._frame.height - tex.smoothEdge1 + tex.smoothEdge2) / texBase.height; + } +} diff --git a/src/core/textures/TextureUvs.js b/src/core/textures/TextureUvs.js index 0ed66e6..cfa8c0b 100644 --- a/src/core/textures/TextureUvs.js +++ b/src/core/textures/TextureUvs.js @@ -27,6 +27,7 @@ this.y3 = 1; this.uvsUint32 = new Uint32Array(4); + this.uvsMatrix = null; } /** diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index a0174e7..1e3a999 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -1,10 +1,6 @@ import * as core from '../core'; -import Texture from '../core/textures/Texture'; import CanvasTinter from '../core/sprites/canvas/CanvasTinter'; -import TilingShader from './webgl/TilingShader'; -import { Rectangle } from '../core/math'; -const tempArray = new Float32Array(4); const tempPoint = new core.Point(); /** @@ -26,18 +22,11 @@ super(texture); /** - * The scaling of the image that is being tiled + * Tile transform * - * @member {PIXI.Point} + * @member {PIXI.TransformStatic} */ - this.tileScale = new core.Point(1, 1); - - /** - * The offset position of the image that is being tiled - * - * @member {PIXI.Point} - */ - this.tilePosition = new core.Point(0, 0); + this.tileTransform = new core.TransformStatic(); // /// private @@ -57,17 +46,67 @@ */ this._height = height; - /** - * An internal WebGL UV cache. - * - * @member {PIXI.TextureUvs} - * @private - */ - this._uvs = new core.TextureUvs(); - this._canvasPattern = null; this._glDatas = []; + + /** + * transform that is applied to UV to get the texture coords + * @member {PIXI.TextureTransform} + */ + this.uvTransform = new core.TextureTransform(texture); + } + + /** + * setter for clampEdge + * @param {number} value assigned value + */ + set clampEdge(value) + { + this.uvTransform.clampEdge = value; + this.uvTransform.update(true); + } + + /** + * The scaling of the image that is being tiled + * + * @member {PIXI.ObservablePoint} + * @memberof PIXI.DisplayObject# + */ + get tileScale() + { + return this.tileTransform.scale; + } + + /** + * Copies the point to the scale of the tiled image. + * + * @param {PIXI.Point|PIXI.ObservablePoint} value - The value to set to. + */ + set tileScale(value) + { + this.tileTransform.scale.copy(value); + } + + /** + * The offset of the image that is being tiled + * + * @member {PIXI.ObservablePoint} + * @memberof PIXI.TilingSprite# + */ + get tilePosition() + { + return this.tileTransform.position; + } + + /** + * Copies the point to the position of the tiled image. + * + * @param {PIXI.Point|PIXI.ObservablePoint} value - The value to set to. + */ + set tilePosition(value) + { + this.tileTransform.position.copy(value); } /** @@ -75,7 +114,7 @@ */ _onTextureUpdate() { - return; + this.uvTransform.texture = this._texture; } /** @@ -89,84 +128,16 @@ // tweak our texture temporarily.. const texture = this._texture; - if (!texture || !texture._uvs) + if (!texture || !texture.valid) { return; } - // get rid of any thing that may be batching. - renderer.flush(); + this.tileTransform.updateLocalTransform(); + this.uvTransform.update(); - const gl = renderer.gl; - let glData = this._glDatas[renderer.CONTEXT_UID]; - - if (!glData) - { - glData = { - shader: new TilingShader(gl), - quad: new core.Quad(gl), - }; - - this._glDatas[renderer.CONTEXT_UID] = glData; - - glData.quad.initVao(glData.shader); - } - - // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space - // before transforming the sprite coords.. - const vertices = glData.quad.vertices; - - vertices[0] = vertices[6] = (this._width) * -this.anchor.x; - vertices[1] = vertices[3] = this._height * -this.anchor.y; - - vertices[2] = vertices[4] = (this._width) * (1 - this.anchor.x); - vertices[5] = vertices[7] = this._height * (1 - this.anchor.y); - - glData.quad.upload(); - - renderer.bindShader(glData.shader); - - const textureUvs = texture._uvs; - const textureWidth = texture._frame.width; - const textureHeight = texture._frame.height; - const textureBaseWidth = texture.baseTexture.width; - const textureBaseHeight = texture.baseTexture.height; - - const uPixelSize = glData.shader.uniforms.uPixelSize; - - uPixelSize[0] = 1.0 / textureBaseWidth; - uPixelSize[1] = 1.0 / textureBaseHeight; - glData.shader.uniforms.uPixelSize = uPixelSize; - - const uFrame = glData.shader.uniforms.uFrame; - - uFrame[0] = textureUvs.x0; - uFrame[1] = textureUvs.y0; - uFrame[2] = textureUvs.x1 - textureUvs.x0; - uFrame[3] = textureUvs.y2 - textureUvs.y0; - glData.shader.uniforms.uFrame = uFrame; - - const uTransform = glData.shader.uniforms.uTransform; - - uTransform[0] = (this.tilePosition.x % (textureWidth * this.tileScale.x)) / this._width; - uTransform[1] = (this.tilePosition.y % (textureHeight * this.tileScale.y)) / this._height; - uTransform[2] = (textureBaseWidth / this._width) * this.tileScale.x; - uTransform[3] = (textureBaseHeight / this._height) * this.tileScale.y; - glData.shader.uniforms.uTransform = uTransform; - - glData.shader.uniforms.translationMatrix = this.worldTransform.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(this.tint, color); - color[3] = this.worldAlpha; - - glData.shader.uniforms.uColor = color; - - renderer.bindTexture(this._texture, 0); - - renderer.state.setBlendMode(this.blendMode); - glData.quad.draw(); + renderer.setObjectRenderer(renderer.plugins.tiling); + renderer.plugins.tiling.render(this); } /** @@ -409,7 +380,7 @@ */ static from(source, width, height) { - return new TilingSprite(Texture.from(source), width, height); + return new TilingSprite(core.Texture.from(source), width, height); } /** diff --git a/src/extras/index.js b/src/extras/index.js index 73bed41..1a90436 100644 --- a/src/extras/index.js +++ b/src/extras/index.js @@ -3,6 +3,7 @@ */ export { default as MovieClip } from './MovieClip'; export { default as TilingSprite } from './TilingSprite'; +export { default as TilingRenderer } from './webgl/TilingRenderer'; export { default as BitmapText } from './BitmapText'; export { default as cacheAsBitmap } from './cacheAsBitmap'; diff --git a/src/extras/webgl/TilingRenderer.js b/src/extras/webgl/TilingRenderer.js new file mode 100644 index 0000000..c6634ed --- /dev/null +++ b/src/extras/webgl/TilingRenderer.js @@ -0,0 +1,143 @@ +import * as core from '../../core'; +import { WRAP_MODES } from '../../core/const'; + +const glslify = require('glslify'); // eslint-disable-line no-undef + +const tempMat = new core.Matrix(); +const tempArray = new Float32Array(4); + +/** + * WebGL renderer plugin for tiling sprites + */ +export class TilingRenderer extends core.ObjectRenderer { + + /** + * constructor for renderer + * @param {WebGLRenderer} renderer The renderer this tiling awesomeness works for. + */ + constructor(renderer) + { + super(renderer); + + this.shader = null; + this.simpleShader = null; + this.quad = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + onContextChange() + { + const gl = this.renderer.gl; + + this.shader = new core.Shader(gl, + glslify('./tilingSprite.vert'), + glslify('./tilingSprite.frag')); + this.simpleShader = new core.Shader(gl, + glslify('./tilingSprite.vert'), + glslify('./tilingSprite_simple.frag')); + + this.quad = new core.Quad(gl); + this.quad.initVao(this.shader); + } + + /** + * + * @param {PIXI.extras.TilingSprite} ts tilingSprite to be rendered + */ + render(ts) + { + const quad = this.quad; + let vertices = quad.vertices; + + vertices[0] = vertices[6] = (ts._width) * -ts.anchor.x; + vertices[1] = vertices[3] = ts._height * -ts.anchor.y; + + vertices[2] = vertices[4] = (ts._width) * (1.0 - ts.anchor.x); + vertices[5] = vertices[7] = ts._height * (1.0 - ts.anchor.y); + + vertices = quad.uvs; + + vertices[0] = vertices[6] = -ts.anchor.x; + vertices[1] = vertices[3] = -ts.anchor.y; + + vertices[2] = vertices[4] = 1.0 - ts.anchor.x; + vertices[5] = vertices[7] = 1.0 - ts.anchor.y; + + quad.upload(); + + const renderer = this.renderer; + const tex = ts._texture; + const baseTex = tex.baseTexture; + const lt = ts.tileTransform.localTransform; + const uv = ts.uvTransform; + let isSimple = tex.frame.width === baseTex.width && tex.frame.height === baseTex.height; + + // auto, force repeat wrapMode for big tiling textures + if (isSimple) + { + if (!baseTex._glTextures[renderer.CONTEXT_UID]) + { + if (baseTex.wrapMode === WRAP_MODES.CLAMP) + { + baseTex.wrapMode = WRAP_MODES.REPEAT; + } + } + else + { + isSimple = baseTex.wrapMode !== WRAP_MODES.CLAMP; + } + } + + const shader = isSimple ? this.simpleShader : this.shader; + + renderer.bindShader(shader); + + const w = tex.width; + const h = tex.height; + const W = ts._width; + const H = ts._height; + + tempMat.set(lt.a * w / W, + lt.b * w / H, + lt.c * h / W, + lt.d * h / H, + lt.tx / W, + lt.ty / H); + + // that part is the same as above: + // tempMat.identity(); + // tempMat.scale(tex.width, tex.height); + // tempMat.prepend(lt); + // tempMat.scale(1.0 / ts._width, 1.0 / ts._height); + + tempMat.invert(); + if (isSimple) + { + tempMat.append(uv.mapCoord); + } + else + { + shader.uniforms.uMapCoord = uv.mapCoord.toArray(true); + shader.uniforms.uFrame = uv.frameClamp; + } + shader.uniforms.uTransform = tempMat.toArray(true); + + const color = tempArray; + + core.utils.hex2rgb(ts.tint, color); + color[3] = ts.worldAlpha; + shader.uniforms.uColor = color; + shader.uniforms.translationMatrix = ts.transform.worldTransform.toArray(true); + + renderer.bindTexture(tex); + renderer.setBlendMode(ts.blendMode); + + quad.draw(); + } +} + +core.WebGLRenderer.registerPlugin('tiling', TilingRenderer); diff --git a/src/core/index.js b/src/core/index.js index f2b3af1..1de5da5 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -29,6 +29,7 @@ export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer'; export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Texture } from './textures/Texture'; +export { default as TextureTransform } from './textures/TextureTransform'; 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/textures/Texture.js b/src/core/textures/Texture.js index bab0a01..95a0b61 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -141,6 +141,27 @@ */ this._updateID = 0; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * By default, clamps bottom and right edge of texture. + * Change to 0 if you texture has repeated right and bottom lines, that leads to smoother borders + * @default 1 + * @member {number} + */ + this.smoothEdge1 = 1; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to 0 to add a pixel to the edge, recommended for transparent trimmed textures in atlas + * @default 0 + * @member {number} + */ + this.smoothEdge2 = 0; } /** @@ -258,6 +279,24 @@ } /** + * returns uvs + * @member PIXI.TextureUvs# + * @memberof PIXI.Texture# + */ + get uvs() + { + return this._uvs; + } + + /** + * Updates uvs accordingly to the texture + */ + updateUvs() + { + this._updateUvs(); + } + + /** * Helper function that creates a Texture object from the given image url. * If the image is not in the texture cache it will be created and loaded. * diff --git a/src/core/textures/TextureTransform.js b/src/core/textures/TextureTransform.js new file mode 100644 index 0000000..6dfa2d9 --- /dev/null +++ b/src/core/textures/TextureTransform.js @@ -0,0 +1,90 @@ +import { default as Matrix } from '../math/Matrix'; + +const tempMat = new Matrix(); + +/** + * class controls uv transform and frame clamp for texture + */ +export default class TextureTransform { + /** + * + * @param {PIXI.Texture} texture observed texture + * @constructor + */ + constructor(texture) + { + this._texture = texture; + + this.mapCoord = new Matrix(); + + this.frameClamp = new Float32Array(4); + + this._lastTextureID = -1; + + this.update(); + } + + /** + * texture property + * @member {PIXI.Texture} + * @memberof PIXI.TextureTransform + */ + get texture() + { + return this._texture; + } + + /** + * sets texture value + * @param {PIXI.Texture} value texture to be set + */ + set texture(value) + { + this._texture = value; + this._lastTextureID = -1; + } + + /** + * updates matrices if texture was changed + * @param {boolean} forceUpdate if true, matrices will be updated any case + */ + update(forceUpdate) + { + const tex = this.texture; + + if (!tex || !tex.valid) + { + return; + } + + if (!forceUpdate + && this._lastTextureID === this.texture._updateID) + { + return; + } + + this._lastTextureID = this.texture._updateID; + + const uvs = this.texture._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.frameClamp; + + frame[0] = (tex._frame.x + tex.smoothEdge2) / texBase.width; + frame[1] = (tex._frame.y + tex.smoothEdge2) / texBase.height; + frame[2] = (tex._frame.x + tex._frame.width - tex.smoothEdge1 + tex.smoothEdge2) / texBase.width; + frame[3] = (tex._frame.y + tex._frame.height - tex.smoothEdge1 + tex.smoothEdge2) / texBase.height; + } +} diff --git a/src/core/textures/TextureUvs.js b/src/core/textures/TextureUvs.js index 0ed66e6..cfa8c0b 100644 --- a/src/core/textures/TextureUvs.js +++ b/src/core/textures/TextureUvs.js @@ -27,6 +27,7 @@ this.y3 = 1; this.uvsUint32 = new Uint32Array(4); + this.uvsMatrix = null; } /** diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index a0174e7..1e3a999 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -1,10 +1,6 @@ import * as core from '../core'; -import Texture from '../core/textures/Texture'; import CanvasTinter from '../core/sprites/canvas/CanvasTinter'; -import TilingShader from './webgl/TilingShader'; -import { Rectangle } from '../core/math'; -const tempArray = new Float32Array(4); const tempPoint = new core.Point(); /** @@ -26,18 +22,11 @@ super(texture); /** - * The scaling of the image that is being tiled + * Tile transform * - * @member {PIXI.Point} + * @member {PIXI.TransformStatic} */ - this.tileScale = new core.Point(1, 1); - - /** - * The offset position of the image that is being tiled - * - * @member {PIXI.Point} - */ - this.tilePosition = new core.Point(0, 0); + this.tileTransform = new core.TransformStatic(); // /// private @@ -57,17 +46,67 @@ */ this._height = height; - /** - * An internal WebGL UV cache. - * - * @member {PIXI.TextureUvs} - * @private - */ - this._uvs = new core.TextureUvs(); - this._canvasPattern = null; this._glDatas = []; + + /** + * transform that is applied to UV to get the texture coords + * @member {PIXI.TextureTransform} + */ + this.uvTransform = new core.TextureTransform(texture); + } + + /** + * setter for clampEdge + * @param {number} value assigned value + */ + set clampEdge(value) + { + this.uvTransform.clampEdge = value; + this.uvTransform.update(true); + } + + /** + * The scaling of the image that is being tiled + * + * @member {PIXI.ObservablePoint} + * @memberof PIXI.DisplayObject# + */ + get tileScale() + { + return this.tileTransform.scale; + } + + /** + * Copies the point to the scale of the tiled image. + * + * @param {PIXI.Point|PIXI.ObservablePoint} value - The value to set to. + */ + set tileScale(value) + { + this.tileTransform.scale.copy(value); + } + + /** + * The offset of the image that is being tiled + * + * @member {PIXI.ObservablePoint} + * @memberof PIXI.TilingSprite# + */ + get tilePosition() + { + return this.tileTransform.position; + } + + /** + * Copies the point to the position of the tiled image. + * + * @param {PIXI.Point|PIXI.ObservablePoint} value - The value to set to. + */ + set tilePosition(value) + { + this.tileTransform.position.copy(value); } /** @@ -75,7 +114,7 @@ */ _onTextureUpdate() { - return; + this.uvTransform.texture = this._texture; } /** @@ -89,84 +128,16 @@ // tweak our texture temporarily.. const texture = this._texture; - if (!texture || !texture._uvs) + if (!texture || !texture.valid) { return; } - // get rid of any thing that may be batching. - renderer.flush(); + this.tileTransform.updateLocalTransform(); + this.uvTransform.update(); - const gl = renderer.gl; - let glData = this._glDatas[renderer.CONTEXT_UID]; - - if (!glData) - { - glData = { - shader: new TilingShader(gl), - quad: new core.Quad(gl), - }; - - this._glDatas[renderer.CONTEXT_UID] = glData; - - glData.quad.initVao(glData.shader); - } - - // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space - // before transforming the sprite coords.. - const vertices = glData.quad.vertices; - - vertices[0] = vertices[6] = (this._width) * -this.anchor.x; - vertices[1] = vertices[3] = this._height * -this.anchor.y; - - vertices[2] = vertices[4] = (this._width) * (1 - this.anchor.x); - vertices[5] = vertices[7] = this._height * (1 - this.anchor.y); - - glData.quad.upload(); - - renderer.bindShader(glData.shader); - - const textureUvs = texture._uvs; - const textureWidth = texture._frame.width; - const textureHeight = texture._frame.height; - const textureBaseWidth = texture.baseTexture.width; - const textureBaseHeight = texture.baseTexture.height; - - const uPixelSize = glData.shader.uniforms.uPixelSize; - - uPixelSize[0] = 1.0 / textureBaseWidth; - uPixelSize[1] = 1.0 / textureBaseHeight; - glData.shader.uniforms.uPixelSize = uPixelSize; - - const uFrame = glData.shader.uniforms.uFrame; - - uFrame[0] = textureUvs.x0; - uFrame[1] = textureUvs.y0; - uFrame[2] = textureUvs.x1 - textureUvs.x0; - uFrame[3] = textureUvs.y2 - textureUvs.y0; - glData.shader.uniforms.uFrame = uFrame; - - const uTransform = glData.shader.uniforms.uTransform; - - uTransform[0] = (this.tilePosition.x % (textureWidth * this.tileScale.x)) / this._width; - uTransform[1] = (this.tilePosition.y % (textureHeight * this.tileScale.y)) / this._height; - uTransform[2] = (textureBaseWidth / this._width) * this.tileScale.x; - uTransform[3] = (textureBaseHeight / this._height) * this.tileScale.y; - glData.shader.uniforms.uTransform = uTransform; - - glData.shader.uniforms.translationMatrix = this.worldTransform.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(this.tint, color); - color[3] = this.worldAlpha; - - glData.shader.uniforms.uColor = color; - - renderer.bindTexture(this._texture, 0); - - renderer.state.setBlendMode(this.blendMode); - glData.quad.draw(); + renderer.setObjectRenderer(renderer.plugins.tiling); + renderer.plugins.tiling.render(this); } /** @@ -409,7 +380,7 @@ */ static from(source, width, height) { - return new TilingSprite(Texture.from(source), width, height); + return new TilingSprite(core.Texture.from(source), width, height); } /** diff --git a/src/extras/index.js b/src/extras/index.js index 73bed41..1a90436 100644 --- a/src/extras/index.js +++ b/src/extras/index.js @@ -3,6 +3,7 @@ */ export { default as MovieClip } from './MovieClip'; export { default as TilingSprite } from './TilingSprite'; +export { default as TilingRenderer } from './webgl/TilingRenderer'; export { default as BitmapText } from './BitmapText'; export { default as cacheAsBitmap } from './cacheAsBitmap'; diff --git a/src/extras/webgl/TilingRenderer.js b/src/extras/webgl/TilingRenderer.js new file mode 100644 index 0000000..c6634ed --- /dev/null +++ b/src/extras/webgl/TilingRenderer.js @@ -0,0 +1,143 @@ +import * as core from '../../core'; +import { WRAP_MODES } from '../../core/const'; + +const glslify = require('glslify'); // eslint-disable-line no-undef + +const tempMat = new core.Matrix(); +const tempArray = new Float32Array(4); + +/** + * WebGL renderer plugin for tiling sprites + */ +export class TilingRenderer extends core.ObjectRenderer { + + /** + * constructor for renderer + * @param {WebGLRenderer} renderer The renderer this tiling awesomeness works for. + */ + constructor(renderer) + { + super(renderer); + + this.shader = null; + this.simpleShader = null; + this.quad = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + onContextChange() + { + const gl = this.renderer.gl; + + this.shader = new core.Shader(gl, + glslify('./tilingSprite.vert'), + glslify('./tilingSprite.frag')); + this.simpleShader = new core.Shader(gl, + glslify('./tilingSprite.vert'), + glslify('./tilingSprite_simple.frag')); + + this.quad = new core.Quad(gl); + this.quad.initVao(this.shader); + } + + /** + * + * @param {PIXI.extras.TilingSprite} ts tilingSprite to be rendered + */ + render(ts) + { + const quad = this.quad; + let vertices = quad.vertices; + + vertices[0] = vertices[6] = (ts._width) * -ts.anchor.x; + vertices[1] = vertices[3] = ts._height * -ts.anchor.y; + + vertices[2] = vertices[4] = (ts._width) * (1.0 - ts.anchor.x); + vertices[5] = vertices[7] = ts._height * (1.0 - ts.anchor.y); + + vertices = quad.uvs; + + vertices[0] = vertices[6] = -ts.anchor.x; + vertices[1] = vertices[3] = -ts.anchor.y; + + vertices[2] = vertices[4] = 1.0 - ts.anchor.x; + vertices[5] = vertices[7] = 1.0 - ts.anchor.y; + + quad.upload(); + + const renderer = this.renderer; + const tex = ts._texture; + const baseTex = tex.baseTexture; + const lt = ts.tileTransform.localTransform; + const uv = ts.uvTransform; + let isSimple = tex.frame.width === baseTex.width && tex.frame.height === baseTex.height; + + // auto, force repeat wrapMode for big tiling textures + if (isSimple) + { + if (!baseTex._glTextures[renderer.CONTEXT_UID]) + { + if (baseTex.wrapMode === WRAP_MODES.CLAMP) + { + baseTex.wrapMode = WRAP_MODES.REPEAT; + } + } + else + { + isSimple = baseTex.wrapMode !== WRAP_MODES.CLAMP; + } + } + + const shader = isSimple ? this.simpleShader : this.shader; + + renderer.bindShader(shader); + + const w = tex.width; + const h = tex.height; + const W = ts._width; + const H = ts._height; + + tempMat.set(lt.a * w / W, + lt.b * w / H, + lt.c * h / W, + lt.d * h / H, + lt.tx / W, + lt.ty / H); + + // that part is the same as above: + // tempMat.identity(); + // tempMat.scale(tex.width, tex.height); + // tempMat.prepend(lt); + // tempMat.scale(1.0 / ts._width, 1.0 / ts._height); + + tempMat.invert(); + if (isSimple) + { + tempMat.append(uv.mapCoord); + } + else + { + shader.uniforms.uMapCoord = uv.mapCoord.toArray(true); + shader.uniforms.uFrame = uv.frameClamp; + } + shader.uniforms.uTransform = tempMat.toArray(true); + + const color = tempArray; + + core.utils.hex2rgb(ts.tint, color); + color[3] = ts.worldAlpha; + shader.uniforms.uColor = color; + shader.uniforms.translationMatrix = ts.transform.worldTransform.toArray(true); + + renderer.bindTexture(tex); + renderer.setBlendMode(ts.blendMode); + + quad.draw(); + } +} + +core.WebGLRenderer.registerPlugin('tiling', TilingRenderer); diff --git a/src/extras/webgl/TilingShader.js b/src/extras/webgl/TilingShader.js deleted file mode 100644 index 1e63a14..0000000 --- a/src/extras/webgl/TilingShader.js +++ /dev/null @@ -1,22 +0,0 @@ -import Shader from '../../core/Shader'; -const glslify = require('glslify'); // eslint-disable-line no-undef - -/** - * @class - * @extends PIXI.Shader - * @memberof PIXI.mesh - */ -export default class TilingShader extends Shader -{ - /** - * @param {WebGLRenderingContext} gl - The WebGL rendering context. - */ - constructor(gl) - { - super( - gl, - glslify('./tilingSprite.vert'), - glslify('./tilingSprite.frag') - ); - } -} diff --git a/src/core/index.js b/src/core/index.js index f2b3af1..1de5da5 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -29,6 +29,7 @@ export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer'; export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Texture } from './textures/Texture'; +export { default as TextureTransform } from './textures/TextureTransform'; 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/textures/Texture.js b/src/core/textures/Texture.js index bab0a01..95a0b61 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -141,6 +141,27 @@ */ this._updateID = 0; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * By default, clamps bottom and right edge of texture. + * Change to 0 if you texture has repeated right and bottom lines, that leads to smoother borders + * @default 1 + * @member {number} + */ + this.smoothEdge1 = 1; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to 0 to add a pixel to the edge, recommended for transparent trimmed textures in atlas + * @default 0 + * @member {number} + */ + this.smoothEdge2 = 0; } /** @@ -258,6 +279,24 @@ } /** + * returns uvs + * @member PIXI.TextureUvs# + * @memberof PIXI.Texture# + */ + get uvs() + { + return this._uvs; + } + + /** + * Updates uvs accordingly to the texture + */ + updateUvs() + { + this._updateUvs(); + } + + /** * Helper function that creates a Texture object from the given image url. * If the image is not in the texture cache it will be created and loaded. * diff --git a/src/core/textures/TextureTransform.js b/src/core/textures/TextureTransform.js new file mode 100644 index 0000000..6dfa2d9 --- /dev/null +++ b/src/core/textures/TextureTransform.js @@ -0,0 +1,90 @@ +import { default as Matrix } from '../math/Matrix'; + +const tempMat = new Matrix(); + +/** + * class controls uv transform and frame clamp for texture + */ +export default class TextureTransform { + /** + * + * @param {PIXI.Texture} texture observed texture + * @constructor + */ + constructor(texture) + { + this._texture = texture; + + this.mapCoord = new Matrix(); + + this.frameClamp = new Float32Array(4); + + this._lastTextureID = -1; + + this.update(); + } + + /** + * texture property + * @member {PIXI.Texture} + * @memberof PIXI.TextureTransform + */ + get texture() + { + return this._texture; + } + + /** + * sets texture value + * @param {PIXI.Texture} value texture to be set + */ + set texture(value) + { + this._texture = value; + this._lastTextureID = -1; + } + + /** + * updates matrices if texture was changed + * @param {boolean} forceUpdate if true, matrices will be updated any case + */ + update(forceUpdate) + { + const tex = this.texture; + + if (!tex || !tex.valid) + { + return; + } + + if (!forceUpdate + && this._lastTextureID === this.texture._updateID) + { + return; + } + + this._lastTextureID = this.texture._updateID; + + const uvs = this.texture._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.frameClamp; + + frame[0] = (tex._frame.x + tex.smoothEdge2) / texBase.width; + frame[1] = (tex._frame.y + tex.smoothEdge2) / texBase.height; + frame[2] = (tex._frame.x + tex._frame.width - tex.smoothEdge1 + tex.smoothEdge2) / texBase.width; + frame[3] = (tex._frame.y + tex._frame.height - tex.smoothEdge1 + tex.smoothEdge2) / texBase.height; + } +} diff --git a/src/core/textures/TextureUvs.js b/src/core/textures/TextureUvs.js index 0ed66e6..cfa8c0b 100644 --- a/src/core/textures/TextureUvs.js +++ b/src/core/textures/TextureUvs.js @@ -27,6 +27,7 @@ this.y3 = 1; this.uvsUint32 = new Uint32Array(4); + this.uvsMatrix = null; } /** diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index a0174e7..1e3a999 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -1,10 +1,6 @@ import * as core from '../core'; -import Texture from '../core/textures/Texture'; import CanvasTinter from '../core/sprites/canvas/CanvasTinter'; -import TilingShader from './webgl/TilingShader'; -import { Rectangle } from '../core/math'; -const tempArray = new Float32Array(4); const tempPoint = new core.Point(); /** @@ -26,18 +22,11 @@ super(texture); /** - * The scaling of the image that is being tiled + * Tile transform * - * @member {PIXI.Point} + * @member {PIXI.TransformStatic} */ - this.tileScale = new core.Point(1, 1); - - /** - * The offset position of the image that is being tiled - * - * @member {PIXI.Point} - */ - this.tilePosition = new core.Point(0, 0); + this.tileTransform = new core.TransformStatic(); // /// private @@ -57,17 +46,67 @@ */ this._height = height; - /** - * An internal WebGL UV cache. - * - * @member {PIXI.TextureUvs} - * @private - */ - this._uvs = new core.TextureUvs(); - this._canvasPattern = null; this._glDatas = []; + + /** + * transform that is applied to UV to get the texture coords + * @member {PIXI.TextureTransform} + */ + this.uvTransform = new core.TextureTransform(texture); + } + + /** + * setter for clampEdge + * @param {number} value assigned value + */ + set clampEdge(value) + { + this.uvTransform.clampEdge = value; + this.uvTransform.update(true); + } + + /** + * The scaling of the image that is being tiled + * + * @member {PIXI.ObservablePoint} + * @memberof PIXI.DisplayObject# + */ + get tileScale() + { + return this.tileTransform.scale; + } + + /** + * Copies the point to the scale of the tiled image. + * + * @param {PIXI.Point|PIXI.ObservablePoint} value - The value to set to. + */ + set tileScale(value) + { + this.tileTransform.scale.copy(value); + } + + /** + * The offset of the image that is being tiled + * + * @member {PIXI.ObservablePoint} + * @memberof PIXI.TilingSprite# + */ + get tilePosition() + { + return this.tileTransform.position; + } + + /** + * Copies the point to the position of the tiled image. + * + * @param {PIXI.Point|PIXI.ObservablePoint} value - The value to set to. + */ + set tilePosition(value) + { + this.tileTransform.position.copy(value); } /** @@ -75,7 +114,7 @@ */ _onTextureUpdate() { - return; + this.uvTransform.texture = this._texture; } /** @@ -89,84 +128,16 @@ // tweak our texture temporarily.. const texture = this._texture; - if (!texture || !texture._uvs) + if (!texture || !texture.valid) { return; } - // get rid of any thing that may be batching. - renderer.flush(); + this.tileTransform.updateLocalTransform(); + this.uvTransform.update(); - const gl = renderer.gl; - let glData = this._glDatas[renderer.CONTEXT_UID]; - - if (!glData) - { - glData = { - shader: new TilingShader(gl), - quad: new core.Quad(gl), - }; - - this._glDatas[renderer.CONTEXT_UID] = glData; - - glData.quad.initVao(glData.shader); - } - - // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space - // before transforming the sprite coords.. - const vertices = glData.quad.vertices; - - vertices[0] = vertices[6] = (this._width) * -this.anchor.x; - vertices[1] = vertices[3] = this._height * -this.anchor.y; - - vertices[2] = vertices[4] = (this._width) * (1 - this.anchor.x); - vertices[5] = vertices[7] = this._height * (1 - this.anchor.y); - - glData.quad.upload(); - - renderer.bindShader(glData.shader); - - const textureUvs = texture._uvs; - const textureWidth = texture._frame.width; - const textureHeight = texture._frame.height; - const textureBaseWidth = texture.baseTexture.width; - const textureBaseHeight = texture.baseTexture.height; - - const uPixelSize = glData.shader.uniforms.uPixelSize; - - uPixelSize[0] = 1.0 / textureBaseWidth; - uPixelSize[1] = 1.0 / textureBaseHeight; - glData.shader.uniforms.uPixelSize = uPixelSize; - - const uFrame = glData.shader.uniforms.uFrame; - - uFrame[0] = textureUvs.x0; - uFrame[1] = textureUvs.y0; - uFrame[2] = textureUvs.x1 - textureUvs.x0; - uFrame[3] = textureUvs.y2 - textureUvs.y0; - glData.shader.uniforms.uFrame = uFrame; - - const uTransform = glData.shader.uniforms.uTransform; - - uTransform[0] = (this.tilePosition.x % (textureWidth * this.tileScale.x)) / this._width; - uTransform[1] = (this.tilePosition.y % (textureHeight * this.tileScale.y)) / this._height; - uTransform[2] = (textureBaseWidth / this._width) * this.tileScale.x; - uTransform[3] = (textureBaseHeight / this._height) * this.tileScale.y; - glData.shader.uniforms.uTransform = uTransform; - - glData.shader.uniforms.translationMatrix = this.worldTransform.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(this.tint, color); - color[3] = this.worldAlpha; - - glData.shader.uniforms.uColor = color; - - renderer.bindTexture(this._texture, 0); - - renderer.state.setBlendMode(this.blendMode); - glData.quad.draw(); + renderer.setObjectRenderer(renderer.plugins.tiling); + renderer.plugins.tiling.render(this); } /** @@ -409,7 +380,7 @@ */ static from(source, width, height) { - return new TilingSprite(Texture.from(source), width, height); + return new TilingSprite(core.Texture.from(source), width, height); } /** diff --git a/src/extras/index.js b/src/extras/index.js index 73bed41..1a90436 100644 --- a/src/extras/index.js +++ b/src/extras/index.js @@ -3,6 +3,7 @@ */ export { default as MovieClip } from './MovieClip'; export { default as TilingSprite } from './TilingSprite'; +export { default as TilingRenderer } from './webgl/TilingRenderer'; export { default as BitmapText } from './BitmapText'; export { default as cacheAsBitmap } from './cacheAsBitmap'; diff --git a/src/extras/webgl/TilingRenderer.js b/src/extras/webgl/TilingRenderer.js new file mode 100644 index 0000000..c6634ed --- /dev/null +++ b/src/extras/webgl/TilingRenderer.js @@ -0,0 +1,143 @@ +import * as core from '../../core'; +import { WRAP_MODES } from '../../core/const'; + +const glslify = require('glslify'); // eslint-disable-line no-undef + +const tempMat = new core.Matrix(); +const tempArray = new Float32Array(4); + +/** + * WebGL renderer plugin for tiling sprites + */ +export class TilingRenderer extends core.ObjectRenderer { + + /** + * constructor for renderer + * @param {WebGLRenderer} renderer The renderer this tiling awesomeness works for. + */ + constructor(renderer) + { + super(renderer); + + this.shader = null; + this.simpleShader = null; + this.quad = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + onContextChange() + { + const gl = this.renderer.gl; + + this.shader = new core.Shader(gl, + glslify('./tilingSprite.vert'), + glslify('./tilingSprite.frag')); + this.simpleShader = new core.Shader(gl, + glslify('./tilingSprite.vert'), + glslify('./tilingSprite_simple.frag')); + + this.quad = new core.Quad(gl); + this.quad.initVao(this.shader); + } + + /** + * + * @param {PIXI.extras.TilingSprite} ts tilingSprite to be rendered + */ + render(ts) + { + const quad = this.quad; + let vertices = quad.vertices; + + vertices[0] = vertices[6] = (ts._width) * -ts.anchor.x; + vertices[1] = vertices[3] = ts._height * -ts.anchor.y; + + vertices[2] = vertices[4] = (ts._width) * (1.0 - ts.anchor.x); + vertices[5] = vertices[7] = ts._height * (1.0 - ts.anchor.y); + + vertices = quad.uvs; + + vertices[0] = vertices[6] = -ts.anchor.x; + vertices[1] = vertices[3] = -ts.anchor.y; + + vertices[2] = vertices[4] = 1.0 - ts.anchor.x; + vertices[5] = vertices[7] = 1.0 - ts.anchor.y; + + quad.upload(); + + const renderer = this.renderer; + const tex = ts._texture; + const baseTex = tex.baseTexture; + const lt = ts.tileTransform.localTransform; + const uv = ts.uvTransform; + let isSimple = tex.frame.width === baseTex.width && tex.frame.height === baseTex.height; + + // auto, force repeat wrapMode for big tiling textures + if (isSimple) + { + if (!baseTex._glTextures[renderer.CONTEXT_UID]) + { + if (baseTex.wrapMode === WRAP_MODES.CLAMP) + { + baseTex.wrapMode = WRAP_MODES.REPEAT; + } + } + else + { + isSimple = baseTex.wrapMode !== WRAP_MODES.CLAMP; + } + } + + const shader = isSimple ? this.simpleShader : this.shader; + + renderer.bindShader(shader); + + const w = tex.width; + const h = tex.height; + const W = ts._width; + const H = ts._height; + + tempMat.set(lt.a * w / W, + lt.b * w / H, + lt.c * h / W, + lt.d * h / H, + lt.tx / W, + lt.ty / H); + + // that part is the same as above: + // tempMat.identity(); + // tempMat.scale(tex.width, tex.height); + // tempMat.prepend(lt); + // tempMat.scale(1.0 / ts._width, 1.0 / ts._height); + + tempMat.invert(); + if (isSimple) + { + tempMat.append(uv.mapCoord); + } + else + { + shader.uniforms.uMapCoord = uv.mapCoord.toArray(true); + shader.uniforms.uFrame = uv.frameClamp; + } + shader.uniforms.uTransform = tempMat.toArray(true); + + const color = tempArray; + + core.utils.hex2rgb(ts.tint, color); + color[3] = ts.worldAlpha; + shader.uniforms.uColor = color; + shader.uniforms.translationMatrix = ts.transform.worldTransform.toArray(true); + + renderer.bindTexture(tex); + renderer.setBlendMode(ts.blendMode); + + quad.draw(); + } +} + +core.WebGLRenderer.registerPlugin('tiling', TilingRenderer); diff --git a/src/extras/webgl/TilingShader.js b/src/extras/webgl/TilingShader.js deleted file mode 100644 index 1e63a14..0000000 --- a/src/extras/webgl/TilingShader.js +++ /dev/null @@ -1,22 +0,0 @@ -import Shader from '../../core/Shader'; -const glslify = require('glslify'); // eslint-disable-line no-undef - -/** - * @class - * @extends PIXI.Shader - * @memberof PIXI.mesh - */ -export default class TilingShader extends Shader -{ - /** - * @param {WebGLRenderingContext} gl - The WebGL rendering context. - */ - constructor(gl) - { - super( - gl, - glslify('./tilingSprite.vert'), - glslify('./tilingSprite.frag') - ); - } -} diff --git a/src/extras/webgl/tilingSprite.frag b/src/extras/webgl/tilingSprite.frag index 3996251..1e70b07 100644 --- a/src/extras/webgl/tilingSprite.frag +++ b/src/extras/webgl/tilingSprite.frag @@ -2,14 +2,15 @@ uniform sampler2D uSampler; uniform vec4 uColor; +uniform mat3 uMapCoord; uniform vec4 uFrame; uniform vec2 uPixelSize; void main(void) { - vec2 coord = mod(vTextureCoord, uFrame.zw); - coord = clamp(coord, uPixelSize, uFrame.zw - uPixelSize); - coord += uFrame.xy; + vec2 coord = mod(vTextureCoord, vec2(1.0, 1.0)); + coord = (uMapCoord * vec3(coord, 1.0)).xy; + coord = clamp(coord, uFrame.xy, uFrame.zw); vec4 sample = texture2D(uSampler, coord); vec4 color = vec4(uColor.rgb * uColor.a, uColor.a); diff --git a/src/core/index.js b/src/core/index.js index f2b3af1..1de5da5 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -29,6 +29,7 @@ export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer'; export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Texture } from './textures/Texture'; +export { default as TextureTransform } from './textures/TextureTransform'; 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/textures/Texture.js b/src/core/textures/Texture.js index bab0a01..95a0b61 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -141,6 +141,27 @@ */ this._updateID = 0; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * By default, clamps bottom and right edge of texture. + * Change to 0 if you texture has repeated right and bottom lines, that leads to smoother borders + * @default 1 + * @member {number} + */ + this.smoothEdge1 = 1; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to 0 to add a pixel to the edge, recommended for transparent trimmed textures in atlas + * @default 0 + * @member {number} + */ + this.smoothEdge2 = 0; } /** @@ -258,6 +279,24 @@ } /** + * returns uvs + * @member PIXI.TextureUvs# + * @memberof PIXI.Texture# + */ + get uvs() + { + return this._uvs; + } + + /** + * Updates uvs accordingly to the texture + */ + updateUvs() + { + this._updateUvs(); + } + + /** * Helper function that creates a Texture object from the given image url. * If the image is not in the texture cache it will be created and loaded. * diff --git a/src/core/textures/TextureTransform.js b/src/core/textures/TextureTransform.js new file mode 100644 index 0000000..6dfa2d9 --- /dev/null +++ b/src/core/textures/TextureTransform.js @@ -0,0 +1,90 @@ +import { default as Matrix } from '../math/Matrix'; + +const tempMat = new Matrix(); + +/** + * class controls uv transform and frame clamp for texture + */ +export default class TextureTransform { + /** + * + * @param {PIXI.Texture} texture observed texture + * @constructor + */ + constructor(texture) + { + this._texture = texture; + + this.mapCoord = new Matrix(); + + this.frameClamp = new Float32Array(4); + + this._lastTextureID = -1; + + this.update(); + } + + /** + * texture property + * @member {PIXI.Texture} + * @memberof PIXI.TextureTransform + */ + get texture() + { + return this._texture; + } + + /** + * sets texture value + * @param {PIXI.Texture} value texture to be set + */ + set texture(value) + { + this._texture = value; + this._lastTextureID = -1; + } + + /** + * updates matrices if texture was changed + * @param {boolean} forceUpdate if true, matrices will be updated any case + */ + update(forceUpdate) + { + const tex = this.texture; + + if (!tex || !tex.valid) + { + return; + } + + if (!forceUpdate + && this._lastTextureID === this.texture._updateID) + { + return; + } + + this._lastTextureID = this.texture._updateID; + + const uvs = this.texture._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.frameClamp; + + frame[0] = (tex._frame.x + tex.smoothEdge2) / texBase.width; + frame[1] = (tex._frame.y + tex.smoothEdge2) / texBase.height; + frame[2] = (tex._frame.x + tex._frame.width - tex.smoothEdge1 + tex.smoothEdge2) / texBase.width; + frame[3] = (tex._frame.y + tex._frame.height - tex.smoothEdge1 + tex.smoothEdge2) / texBase.height; + } +} diff --git a/src/core/textures/TextureUvs.js b/src/core/textures/TextureUvs.js index 0ed66e6..cfa8c0b 100644 --- a/src/core/textures/TextureUvs.js +++ b/src/core/textures/TextureUvs.js @@ -27,6 +27,7 @@ this.y3 = 1; this.uvsUint32 = new Uint32Array(4); + this.uvsMatrix = null; } /** diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index a0174e7..1e3a999 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -1,10 +1,6 @@ import * as core from '../core'; -import Texture from '../core/textures/Texture'; import CanvasTinter from '../core/sprites/canvas/CanvasTinter'; -import TilingShader from './webgl/TilingShader'; -import { Rectangle } from '../core/math'; -const tempArray = new Float32Array(4); const tempPoint = new core.Point(); /** @@ -26,18 +22,11 @@ super(texture); /** - * The scaling of the image that is being tiled + * Tile transform * - * @member {PIXI.Point} + * @member {PIXI.TransformStatic} */ - this.tileScale = new core.Point(1, 1); - - /** - * The offset position of the image that is being tiled - * - * @member {PIXI.Point} - */ - this.tilePosition = new core.Point(0, 0); + this.tileTransform = new core.TransformStatic(); // /// private @@ -57,17 +46,67 @@ */ this._height = height; - /** - * An internal WebGL UV cache. - * - * @member {PIXI.TextureUvs} - * @private - */ - this._uvs = new core.TextureUvs(); - this._canvasPattern = null; this._glDatas = []; + + /** + * transform that is applied to UV to get the texture coords + * @member {PIXI.TextureTransform} + */ + this.uvTransform = new core.TextureTransform(texture); + } + + /** + * setter for clampEdge + * @param {number} value assigned value + */ + set clampEdge(value) + { + this.uvTransform.clampEdge = value; + this.uvTransform.update(true); + } + + /** + * The scaling of the image that is being tiled + * + * @member {PIXI.ObservablePoint} + * @memberof PIXI.DisplayObject# + */ + get tileScale() + { + return this.tileTransform.scale; + } + + /** + * Copies the point to the scale of the tiled image. + * + * @param {PIXI.Point|PIXI.ObservablePoint} value - The value to set to. + */ + set tileScale(value) + { + this.tileTransform.scale.copy(value); + } + + /** + * The offset of the image that is being tiled + * + * @member {PIXI.ObservablePoint} + * @memberof PIXI.TilingSprite# + */ + get tilePosition() + { + return this.tileTransform.position; + } + + /** + * Copies the point to the position of the tiled image. + * + * @param {PIXI.Point|PIXI.ObservablePoint} value - The value to set to. + */ + set tilePosition(value) + { + this.tileTransform.position.copy(value); } /** @@ -75,7 +114,7 @@ */ _onTextureUpdate() { - return; + this.uvTransform.texture = this._texture; } /** @@ -89,84 +128,16 @@ // tweak our texture temporarily.. const texture = this._texture; - if (!texture || !texture._uvs) + if (!texture || !texture.valid) { return; } - // get rid of any thing that may be batching. - renderer.flush(); + this.tileTransform.updateLocalTransform(); + this.uvTransform.update(); - const gl = renderer.gl; - let glData = this._glDatas[renderer.CONTEXT_UID]; - - if (!glData) - { - glData = { - shader: new TilingShader(gl), - quad: new core.Quad(gl), - }; - - this._glDatas[renderer.CONTEXT_UID] = glData; - - glData.quad.initVao(glData.shader); - } - - // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space - // before transforming the sprite coords.. - const vertices = glData.quad.vertices; - - vertices[0] = vertices[6] = (this._width) * -this.anchor.x; - vertices[1] = vertices[3] = this._height * -this.anchor.y; - - vertices[2] = vertices[4] = (this._width) * (1 - this.anchor.x); - vertices[5] = vertices[7] = this._height * (1 - this.anchor.y); - - glData.quad.upload(); - - renderer.bindShader(glData.shader); - - const textureUvs = texture._uvs; - const textureWidth = texture._frame.width; - const textureHeight = texture._frame.height; - const textureBaseWidth = texture.baseTexture.width; - const textureBaseHeight = texture.baseTexture.height; - - const uPixelSize = glData.shader.uniforms.uPixelSize; - - uPixelSize[0] = 1.0 / textureBaseWidth; - uPixelSize[1] = 1.0 / textureBaseHeight; - glData.shader.uniforms.uPixelSize = uPixelSize; - - const uFrame = glData.shader.uniforms.uFrame; - - uFrame[0] = textureUvs.x0; - uFrame[1] = textureUvs.y0; - uFrame[2] = textureUvs.x1 - textureUvs.x0; - uFrame[3] = textureUvs.y2 - textureUvs.y0; - glData.shader.uniforms.uFrame = uFrame; - - const uTransform = glData.shader.uniforms.uTransform; - - uTransform[0] = (this.tilePosition.x % (textureWidth * this.tileScale.x)) / this._width; - uTransform[1] = (this.tilePosition.y % (textureHeight * this.tileScale.y)) / this._height; - uTransform[2] = (textureBaseWidth / this._width) * this.tileScale.x; - uTransform[3] = (textureBaseHeight / this._height) * this.tileScale.y; - glData.shader.uniforms.uTransform = uTransform; - - glData.shader.uniforms.translationMatrix = this.worldTransform.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(this.tint, color); - color[3] = this.worldAlpha; - - glData.shader.uniforms.uColor = color; - - renderer.bindTexture(this._texture, 0); - - renderer.state.setBlendMode(this.blendMode); - glData.quad.draw(); + renderer.setObjectRenderer(renderer.plugins.tiling); + renderer.plugins.tiling.render(this); } /** @@ -409,7 +380,7 @@ */ static from(source, width, height) { - return new TilingSprite(Texture.from(source), width, height); + return new TilingSprite(core.Texture.from(source), width, height); } /** diff --git a/src/extras/index.js b/src/extras/index.js index 73bed41..1a90436 100644 --- a/src/extras/index.js +++ b/src/extras/index.js @@ -3,6 +3,7 @@ */ export { default as MovieClip } from './MovieClip'; export { default as TilingSprite } from './TilingSprite'; +export { default as TilingRenderer } from './webgl/TilingRenderer'; export { default as BitmapText } from './BitmapText'; export { default as cacheAsBitmap } from './cacheAsBitmap'; diff --git a/src/extras/webgl/TilingRenderer.js b/src/extras/webgl/TilingRenderer.js new file mode 100644 index 0000000..c6634ed --- /dev/null +++ b/src/extras/webgl/TilingRenderer.js @@ -0,0 +1,143 @@ +import * as core from '../../core'; +import { WRAP_MODES } from '../../core/const'; + +const glslify = require('glslify'); // eslint-disable-line no-undef + +const tempMat = new core.Matrix(); +const tempArray = new Float32Array(4); + +/** + * WebGL renderer plugin for tiling sprites + */ +export class TilingRenderer extends core.ObjectRenderer { + + /** + * constructor for renderer + * @param {WebGLRenderer} renderer The renderer this tiling awesomeness works for. + */ + constructor(renderer) + { + super(renderer); + + this.shader = null; + this.simpleShader = null; + this.quad = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + onContextChange() + { + const gl = this.renderer.gl; + + this.shader = new core.Shader(gl, + glslify('./tilingSprite.vert'), + glslify('./tilingSprite.frag')); + this.simpleShader = new core.Shader(gl, + glslify('./tilingSprite.vert'), + glslify('./tilingSprite_simple.frag')); + + this.quad = new core.Quad(gl); + this.quad.initVao(this.shader); + } + + /** + * + * @param {PIXI.extras.TilingSprite} ts tilingSprite to be rendered + */ + render(ts) + { + const quad = this.quad; + let vertices = quad.vertices; + + vertices[0] = vertices[6] = (ts._width) * -ts.anchor.x; + vertices[1] = vertices[3] = ts._height * -ts.anchor.y; + + vertices[2] = vertices[4] = (ts._width) * (1.0 - ts.anchor.x); + vertices[5] = vertices[7] = ts._height * (1.0 - ts.anchor.y); + + vertices = quad.uvs; + + vertices[0] = vertices[6] = -ts.anchor.x; + vertices[1] = vertices[3] = -ts.anchor.y; + + vertices[2] = vertices[4] = 1.0 - ts.anchor.x; + vertices[5] = vertices[7] = 1.0 - ts.anchor.y; + + quad.upload(); + + const renderer = this.renderer; + const tex = ts._texture; + const baseTex = tex.baseTexture; + const lt = ts.tileTransform.localTransform; + const uv = ts.uvTransform; + let isSimple = tex.frame.width === baseTex.width && tex.frame.height === baseTex.height; + + // auto, force repeat wrapMode for big tiling textures + if (isSimple) + { + if (!baseTex._glTextures[renderer.CONTEXT_UID]) + { + if (baseTex.wrapMode === WRAP_MODES.CLAMP) + { + baseTex.wrapMode = WRAP_MODES.REPEAT; + } + } + else + { + isSimple = baseTex.wrapMode !== WRAP_MODES.CLAMP; + } + } + + const shader = isSimple ? this.simpleShader : this.shader; + + renderer.bindShader(shader); + + const w = tex.width; + const h = tex.height; + const W = ts._width; + const H = ts._height; + + tempMat.set(lt.a * w / W, + lt.b * w / H, + lt.c * h / W, + lt.d * h / H, + lt.tx / W, + lt.ty / H); + + // that part is the same as above: + // tempMat.identity(); + // tempMat.scale(tex.width, tex.height); + // tempMat.prepend(lt); + // tempMat.scale(1.0 / ts._width, 1.0 / ts._height); + + tempMat.invert(); + if (isSimple) + { + tempMat.append(uv.mapCoord); + } + else + { + shader.uniforms.uMapCoord = uv.mapCoord.toArray(true); + shader.uniforms.uFrame = uv.frameClamp; + } + shader.uniforms.uTransform = tempMat.toArray(true); + + const color = tempArray; + + core.utils.hex2rgb(ts.tint, color); + color[3] = ts.worldAlpha; + shader.uniforms.uColor = color; + shader.uniforms.translationMatrix = ts.transform.worldTransform.toArray(true); + + renderer.bindTexture(tex); + renderer.setBlendMode(ts.blendMode); + + quad.draw(); + } +} + +core.WebGLRenderer.registerPlugin('tiling', TilingRenderer); diff --git a/src/extras/webgl/TilingShader.js b/src/extras/webgl/TilingShader.js deleted file mode 100644 index 1e63a14..0000000 --- a/src/extras/webgl/TilingShader.js +++ /dev/null @@ -1,22 +0,0 @@ -import Shader from '../../core/Shader'; -const glslify = require('glslify'); // eslint-disable-line no-undef - -/** - * @class - * @extends PIXI.Shader - * @memberof PIXI.mesh - */ -export default class TilingShader extends Shader -{ - /** - * @param {WebGLRenderingContext} gl - The WebGL rendering context. - */ - constructor(gl) - { - super( - gl, - glslify('./tilingSprite.vert'), - glslify('./tilingSprite.frag') - ); - } -} diff --git a/src/extras/webgl/tilingSprite.frag b/src/extras/webgl/tilingSprite.frag index 3996251..1e70b07 100644 --- a/src/extras/webgl/tilingSprite.frag +++ b/src/extras/webgl/tilingSprite.frag @@ -2,14 +2,15 @@ uniform sampler2D uSampler; uniform vec4 uColor; +uniform mat3 uMapCoord; uniform vec4 uFrame; uniform vec2 uPixelSize; void main(void) { - vec2 coord = mod(vTextureCoord, uFrame.zw); - coord = clamp(coord, uPixelSize, uFrame.zw - uPixelSize); - coord += uFrame.xy; + vec2 coord = mod(vTextureCoord, vec2(1.0, 1.0)); + coord = (uMapCoord * vec3(coord, 1.0)).xy; + coord = clamp(coord, uFrame.xy, uFrame.zw); vec4 sample = texture2D(uSampler, coord); vec4 color = vec4(uColor.rgb * uColor.a, uColor.a); diff --git a/src/extras/webgl/tilingSprite.vert b/src/extras/webgl/tilingSprite.vert index bb78293..acc096c 100644 --- a/src/extras/webgl/tilingSprite.vert +++ b/src/extras/webgl/tilingSprite.vert @@ -3,9 +3,7 @@ uniform mat3 projectionMatrix; uniform mat3 translationMatrix; - -uniform vec4 uFrame; -uniform vec4 uTransform; +uniform mat3 uTransform; varying vec2 vTextureCoord; @@ -13,8 +11,5 @@ { gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); - vec2 coord = aTextureCoord; - coord -= uTransform.xy; - coord /= uTransform.zw; - vTextureCoord = coord; + vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy; } diff --git a/src/core/index.js b/src/core/index.js index f2b3af1..1de5da5 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -29,6 +29,7 @@ export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer'; export { default as CanvasGraphicsRenderer } from './graphics/canvas/CanvasGraphicsRenderer'; export { default as Texture } from './textures/Texture'; +export { default as TextureTransform } from './textures/TextureTransform'; 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/textures/Texture.js b/src/core/textures/Texture.js index bab0a01..95a0b61 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -141,6 +141,27 @@ */ this._updateID = 0; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * By default, clamps bottom and right edge of texture. + * Change to 0 if you texture has repeated right and bottom lines, that leads to smoother borders + * @default 1 + * @member {number} + */ + this.smoothEdge1 = 1; + + /** + * Experimental! + * Changes frame clamping + * Works with TilingSprite and Mesh + * Change to 0 to add a pixel to the edge, recommended for transparent trimmed textures in atlas + * @default 0 + * @member {number} + */ + this.smoothEdge2 = 0; } /** @@ -258,6 +279,24 @@ } /** + * returns uvs + * @member PIXI.TextureUvs# + * @memberof PIXI.Texture# + */ + get uvs() + { + return this._uvs; + } + + /** + * Updates uvs accordingly to the texture + */ + updateUvs() + { + this._updateUvs(); + } + + /** * Helper function that creates a Texture object from the given image url. * If the image is not in the texture cache it will be created and loaded. * diff --git a/src/core/textures/TextureTransform.js b/src/core/textures/TextureTransform.js new file mode 100644 index 0000000..6dfa2d9 --- /dev/null +++ b/src/core/textures/TextureTransform.js @@ -0,0 +1,90 @@ +import { default as Matrix } from '../math/Matrix'; + +const tempMat = new Matrix(); + +/** + * class controls uv transform and frame clamp for texture + */ +export default class TextureTransform { + /** + * + * @param {PIXI.Texture} texture observed texture + * @constructor + */ + constructor(texture) + { + this._texture = texture; + + this.mapCoord = new Matrix(); + + this.frameClamp = new Float32Array(4); + + this._lastTextureID = -1; + + this.update(); + } + + /** + * texture property + * @member {PIXI.Texture} + * @memberof PIXI.TextureTransform + */ + get texture() + { + return this._texture; + } + + /** + * sets texture value + * @param {PIXI.Texture} value texture to be set + */ + set texture(value) + { + this._texture = value; + this._lastTextureID = -1; + } + + /** + * updates matrices if texture was changed + * @param {boolean} forceUpdate if true, matrices will be updated any case + */ + update(forceUpdate) + { + const tex = this.texture; + + if (!tex || !tex.valid) + { + return; + } + + if (!forceUpdate + && this._lastTextureID === this.texture._updateID) + { + return; + } + + this._lastTextureID = this.texture._updateID; + + const uvs = this.texture._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.frameClamp; + + frame[0] = (tex._frame.x + tex.smoothEdge2) / texBase.width; + frame[1] = (tex._frame.y + tex.smoothEdge2) / texBase.height; + frame[2] = (tex._frame.x + tex._frame.width - tex.smoothEdge1 + tex.smoothEdge2) / texBase.width; + frame[3] = (tex._frame.y + tex._frame.height - tex.smoothEdge1 + tex.smoothEdge2) / texBase.height; + } +} diff --git a/src/core/textures/TextureUvs.js b/src/core/textures/TextureUvs.js index 0ed66e6..cfa8c0b 100644 --- a/src/core/textures/TextureUvs.js +++ b/src/core/textures/TextureUvs.js @@ -27,6 +27,7 @@ this.y3 = 1; this.uvsUint32 = new Uint32Array(4); + this.uvsMatrix = null; } /** diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index a0174e7..1e3a999 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -1,10 +1,6 @@ import * as core from '../core'; -import Texture from '../core/textures/Texture'; import CanvasTinter from '../core/sprites/canvas/CanvasTinter'; -import TilingShader from './webgl/TilingShader'; -import { Rectangle } from '../core/math'; -const tempArray = new Float32Array(4); const tempPoint = new core.Point(); /** @@ -26,18 +22,11 @@ super(texture); /** - * The scaling of the image that is being tiled + * Tile transform * - * @member {PIXI.Point} + * @member {PIXI.TransformStatic} */ - this.tileScale = new core.Point(1, 1); - - /** - * The offset position of the image that is being tiled - * - * @member {PIXI.Point} - */ - this.tilePosition = new core.Point(0, 0); + this.tileTransform = new core.TransformStatic(); // /// private @@ -57,17 +46,67 @@ */ this._height = height; - /** - * An internal WebGL UV cache. - * - * @member {PIXI.TextureUvs} - * @private - */ - this._uvs = new core.TextureUvs(); - this._canvasPattern = null; this._glDatas = []; + + /** + * transform that is applied to UV to get the texture coords + * @member {PIXI.TextureTransform} + */ + this.uvTransform = new core.TextureTransform(texture); + } + + /** + * setter for clampEdge + * @param {number} value assigned value + */ + set clampEdge(value) + { + this.uvTransform.clampEdge = value; + this.uvTransform.update(true); + } + + /** + * The scaling of the image that is being tiled + * + * @member {PIXI.ObservablePoint} + * @memberof PIXI.DisplayObject# + */ + get tileScale() + { + return this.tileTransform.scale; + } + + /** + * Copies the point to the scale of the tiled image. + * + * @param {PIXI.Point|PIXI.ObservablePoint} value - The value to set to. + */ + set tileScale(value) + { + this.tileTransform.scale.copy(value); + } + + /** + * The offset of the image that is being tiled + * + * @member {PIXI.ObservablePoint} + * @memberof PIXI.TilingSprite# + */ + get tilePosition() + { + return this.tileTransform.position; + } + + /** + * Copies the point to the position of the tiled image. + * + * @param {PIXI.Point|PIXI.ObservablePoint} value - The value to set to. + */ + set tilePosition(value) + { + this.tileTransform.position.copy(value); } /** @@ -75,7 +114,7 @@ */ _onTextureUpdate() { - return; + this.uvTransform.texture = this._texture; } /** @@ -89,84 +128,16 @@ // tweak our texture temporarily.. const texture = this._texture; - if (!texture || !texture._uvs) + if (!texture || !texture.valid) { return; } - // get rid of any thing that may be batching. - renderer.flush(); + this.tileTransform.updateLocalTransform(); + this.uvTransform.update(); - const gl = renderer.gl; - let glData = this._glDatas[renderer.CONTEXT_UID]; - - if (!glData) - { - glData = { - shader: new TilingShader(gl), - quad: new core.Quad(gl), - }; - - this._glDatas[renderer.CONTEXT_UID] = glData; - - glData.quad.initVao(glData.shader); - } - - // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space - // before transforming the sprite coords.. - const vertices = glData.quad.vertices; - - vertices[0] = vertices[6] = (this._width) * -this.anchor.x; - vertices[1] = vertices[3] = this._height * -this.anchor.y; - - vertices[2] = vertices[4] = (this._width) * (1 - this.anchor.x); - vertices[5] = vertices[7] = this._height * (1 - this.anchor.y); - - glData.quad.upload(); - - renderer.bindShader(glData.shader); - - const textureUvs = texture._uvs; - const textureWidth = texture._frame.width; - const textureHeight = texture._frame.height; - const textureBaseWidth = texture.baseTexture.width; - const textureBaseHeight = texture.baseTexture.height; - - const uPixelSize = glData.shader.uniforms.uPixelSize; - - uPixelSize[0] = 1.0 / textureBaseWidth; - uPixelSize[1] = 1.0 / textureBaseHeight; - glData.shader.uniforms.uPixelSize = uPixelSize; - - const uFrame = glData.shader.uniforms.uFrame; - - uFrame[0] = textureUvs.x0; - uFrame[1] = textureUvs.y0; - uFrame[2] = textureUvs.x1 - textureUvs.x0; - uFrame[3] = textureUvs.y2 - textureUvs.y0; - glData.shader.uniforms.uFrame = uFrame; - - const uTransform = glData.shader.uniforms.uTransform; - - uTransform[0] = (this.tilePosition.x % (textureWidth * this.tileScale.x)) / this._width; - uTransform[1] = (this.tilePosition.y % (textureHeight * this.tileScale.y)) / this._height; - uTransform[2] = (textureBaseWidth / this._width) * this.tileScale.x; - uTransform[3] = (textureBaseHeight / this._height) * this.tileScale.y; - glData.shader.uniforms.uTransform = uTransform; - - glData.shader.uniforms.translationMatrix = this.worldTransform.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(this.tint, color); - color[3] = this.worldAlpha; - - glData.shader.uniforms.uColor = color; - - renderer.bindTexture(this._texture, 0); - - renderer.state.setBlendMode(this.blendMode); - glData.quad.draw(); + renderer.setObjectRenderer(renderer.plugins.tiling); + renderer.plugins.tiling.render(this); } /** @@ -409,7 +380,7 @@ */ static from(source, width, height) { - return new TilingSprite(Texture.from(source), width, height); + return new TilingSprite(core.Texture.from(source), width, height); } /** diff --git a/src/extras/index.js b/src/extras/index.js index 73bed41..1a90436 100644 --- a/src/extras/index.js +++ b/src/extras/index.js @@ -3,6 +3,7 @@ */ export { default as MovieClip } from './MovieClip'; export { default as TilingSprite } from './TilingSprite'; +export { default as TilingRenderer } from './webgl/TilingRenderer'; export { default as BitmapText } from './BitmapText'; export { default as cacheAsBitmap } from './cacheAsBitmap'; diff --git a/src/extras/webgl/TilingRenderer.js b/src/extras/webgl/TilingRenderer.js new file mode 100644 index 0000000..c6634ed --- /dev/null +++ b/src/extras/webgl/TilingRenderer.js @@ -0,0 +1,143 @@ +import * as core from '../../core'; +import { WRAP_MODES } from '../../core/const'; + +const glslify = require('glslify'); // eslint-disable-line no-undef + +const tempMat = new core.Matrix(); +const tempArray = new Float32Array(4); + +/** + * WebGL renderer plugin for tiling sprites + */ +export class TilingRenderer extends core.ObjectRenderer { + + /** + * constructor for renderer + * @param {WebGLRenderer} renderer The renderer this tiling awesomeness works for. + */ + constructor(renderer) + { + super(renderer); + + this.shader = null; + this.simpleShader = null; + this.quad = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + onContextChange() + { + const gl = this.renderer.gl; + + this.shader = new core.Shader(gl, + glslify('./tilingSprite.vert'), + glslify('./tilingSprite.frag')); + this.simpleShader = new core.Shader(gl, + glslify('./tilingSprite.vert'), + glslify('./tilingSprite_simple.frag')); + + this.quad = new core.Quad(gl); + this.quad.initVao(this.shader); + } + + /** + * + * @param {PIXI.extras.TilingSprite} ts tilingSprite to be rendered + */ + render(ts) + { + const quad = this.quad; + let vertices = quad.vertices; + + vertices[0] = vertices[6] = (ts._width) * -ts.anchor.x; + vertices[1] = vertices[3] = ts._height * -ts.anchor.y; + + vertices[2] = vertices[4] = (ts._width) * (1.0 - ts.anchor.x); + vertices[5] = vertices[7] = ts._height * (1.0 - ts.anchor.y); + + vertices = quad.uvs; + + vertices[0] = vertices[6] = -ts.anchor.x; + vertices[1] = vertices[3] = -ts.anchor.y; + + vertices[2] = vertices[4] = 1.0 - ts.anchor.x; + vertices[5] = vertices[7] = 1.0 - ts.anchor.y; + + quad.upload(); + + const renderer = this.renderer; + const tex = ts._texture; + const baseTex = tex.baseTexture; + const lt = ts.tileTransform.localTransform; + const uv = ts.uvTransform; + let isSimple = tex.frame.width === baseTex.width && tex.frame.height === baseTex.height; + + // auto, force repeat wrapMode for big tiling textures + if (isSimple) + { + if (!baseTex._glTextures[renderer.CONTEXT_UID]) + { + if (baseTex.wrapMode === WRAP_MODES.CLAMP) + { + baseTex.wrapMode = WRAP_MODES.REPEAT; + } + } + else + { + isSimple = baseTex.wrapMode !== WRAP_MODES.CLAMP; + } + } + + const shader = isSimple ? this.simpleShader : this.shader; + + renderer.bindShader(shader); + + const w = tex.width; + const h = tex.height; + const W = ts._width; + const H = ts._height; + + tempMat.set(lt.a * w / W, + lt.b * w / H, + lt.c * h / W, + lt.d * h / H, + lt.tx / W, + lt.ty / H); + + // that part is the same as above: + // tempMat.identity(); + // tempMat.scale(tex.width, tex.height); + // tempMat.prepend(lt); + // tempMat.scale(1.0 / ts._width, 1.0 / ts._height); + + tempMat.invert(); + if (isSimple) + { + tempMat.append(uv.mapCoord); + } + else + { + shader.uniforms.uMapCoord = uv.mapCoord.toArray(true); + shader.uniforms.uFrame = uv.frameClamp; + } + shader.uniforms.uTransform = tempMat.toArray(true); + + const color = tempArray; + + core.utils.hex2rgb(ts.tint, color); + color[3] = ts.worldAlpha; + shader.uniforms.uColor = color; + shader.uniforms.translationMatrix = ts.transform.worldTransform.toArray(true); + + renderer.bindTexture(tex); + renderer.setBlendMode(ts.blendMode); + + quad.draw(); + } +} + +core.WebGLRenderer.registerPlugin('tiling', TilingRenderer); diff --git a/src/extras/webgl/TilingShader.js b/src/extras/webgl/TilingShader.js deleted file mode 100644 index 1e63a14..0000000 --- a/src/extras/webgl/TilingShader.js +++ /dev/null @@ -1,22 +0,0 @@ -import Shader from '../../core/Shader'; -const glslify = require('glslify'); // eslint-disable-line no-undef - -/** - * @class - * @extends PIXI.Shader - * @memberof PIXI.mesh - */ -export default class TilingShader extends Shader -{ - /** - * @param {WebGLRenderingContext} gl - The WebGL rendering context. - */ - constructor(gl) - { - super( - gl, - glslify('./tilingSprite.vert'), - glslify('./tilingSprite.frag') - ); - } -} diff --git a/src/extras/webgl/tilingSprite.frag b/src/extras/webgl/tilingSprite.frag index 3996251..1e70b07 100644 --- a/src/extras/webgl/tilingSprite.frag +++ b/src/extras/webgl/tilingSprite.frag @@ -2,14 +2,15 @@ uniform sampler2D uSampler; uniform vec4 uColor; +uniform mat3 uMapCoord; uniform vec4 uFrame; uniform vec2 uPixelSize; void main(void) { - vec2 coord = mod(vTextureCoord, uFrame.zw); - coord = clamp(coord, uPixelSize, uFrame.zw - uPixelSize); - coord += uFrame.xy; + vec2 coord = mod(vTextureCoord, vec2(1.0, 1.0)); + coord = (uMapCoord * vec3(coord, 1.0)).xy; + coord = clamp(coord, uFrame.xy, uFrame.zw); vec4 sample = texture2D(uSampler, coord); vec4 color = vec4(uColor.rgb * uColor.a, uColor.a); diff --git a/src/extras/webgl/tilingSprite.vert b/src/extras/webgl/tilingSprite.vert index bb78293..acc096c 100644 --- a/src/extras/webgl/tilingSprite.vert +++ b/src/extras/webgl/tilingSprite.vert @@ -3,9 +3,7 @@ uniform mat3 projectionMatrix; uniform mat3 translationMatrix; - -uniform vec4 uFrame; -uniform vec4 uTransform; +uniform mat3 uTransform; varying vec2 vTextureCoord; @@ -13,8 +11,5 @@ { gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); - vec2 coord = aTextureCoord; - coord -= uTransform.xy; - coord /= uTransform.zw; - vTextureCoord = coord; + vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy; } diff --git a/src/extras/webgl/tilingSprite_simple.frag b/src/extras/webgl/tilingSprite_simple.frag new file mode 100644 index 0000000..6675bf3 --- /dev/null +++ b/src/extras/webgl/tilingSprite_simple.frag @@ -0,0 +1,11 @@ +varying vec2 vTextureCoord; + +uniform sampler2D uSampler; +uniform vec4 uColor; + +void main(void) +{ + vec4 sample = texture2D(uSampler, vTextureCoord); + vec4 color = vec4(uColor.rgb * uColor.a, uColor.a); + gl_FragColor = sample * color; +}