diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index 2a9eba9..09eaf9c 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -42,7 +42,7 @@ } const base = this._texture.baseTexture; - const textureSource = base.source; + const textureSource = base.getDrawableSource(); const w = base.width * base.resolution; const h = base.height * base.resolution; @@ -56,3 +56,56 @@ this.drawSegment(context, textureSource, w, h, 18, 19, 28, 29); this.drawSegment(context, textureSource, w, h, 20, 21, 30, 31); }; + +/** + * Renders one segment of the plane. + * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure + * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. + * + * @method drawSegment + * @memberof PIXI.NineSlicePlane# + * @param {CanvasRenderingContext2D} context - The context to draw with. + * @param {CanvasImageSource} textureSource - The source to draw. + * @param {number} w - width of the texture + * @param {number} h - height of the texture + * @param {number} x1 - x index 1 + * @param {number} y1 - y index 1 + * @param {number} x2 - x index 2 + * @param {number} y2 - y index 2 + */ +NineSlicePlane.prototype.drawSegment = function drawSegment(context, textureSource, w, h, x1, y1, x2, y2) +{ + // otherwise you get weird results when using slices of that are 0 wide or high. + const uvs = this.uvs; + const vertices = this.vertices; + + let sw = (uvs[x2] - uvs[x1]) * w; + let sh = (uvs[y2] - uvs[y1]) * h; + let dw = vertices[x2] - vertices[x1]; + let dh = vertices[y2] - vertices[y1]; + + // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. + if (sw < 1) + { + sw = 1; + } + + if (sh < 1) + { + sh = 1; + } + + // make sure destination is at least 1 pixel wide and high, otherwise you get + // lines when rendering close to original size. + if (dw < 1) + { + dw = 1; + } + + if (dh < 1) + { + dh = 1; + } + + context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); +}; diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index 2a9eba9..09eaf9c 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -42,7 +42,7 @@ } const base = this._texture.baseTexture; - const textureSource = base.source; + const textureSource = base.getDrawableSource(); const w = base.width * base.resolution; const h = base.height * base.resolution; @@ -56,3 +56,56 @@ this.drawSegment(context, textureSource, w, h, 18, 19, 28, 29); this.drawSegment(context, textureSource, w, h, 20, 21, 30, 31); }; + +/** + * Renders one segment of the plane. + * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure + * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. + * + * @method drawSegment + * @memberof PIXI.NineSlicePlane# + * @param {CanvasRenderingContext2D} context - The context to draw with. + * @param {CanvasImageSource} textureSource - The source to draw. + * @param {number} w - width of the texture + * @param {number} h - height of the texture + * @param {number} x1 - x index 1 + * @param {number} y1 - y index 1 + * @param {number} x2 - x index 2 + * @param {number} y2 - y index 2 + */ +NineSlicePlane.prototype.drawSegment = function drawSegment(context, textureSource, w, h, x1, y1, x2, y2) +{ + // otherwise you get weird results when using slices of that are 0 wide or high. + const uvs = this.uvs; + const vertices = this.vertices; + + let sw = (uvs[x2] - uvs[x1]) * w; + let sh = (uvs[y2] - uvs[y1]) * h; + let dw = vertices[x2] - vertices[x1]; + let dh = vertices[y2] - vertices[y1]; + + // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. + if (sw < 1) + { + sw = 1; + } + + if (sh < 1) + { + sh = 1; + } + + // make sure destination is at least 1 pixel wide and high, otherwise you get + // lines when rendering close to original size. + if (dw < 1) + { + dw = 1; + } + + if (dh < 1) + { + dh = 1; + } + + context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); +}; diff --git a/packages/core/src/renderTexture/RenderTexture.js b/packages/core/src/renderTexture/RenderTexture.js index f1ecf1f..a64b7e7 100644 --- a/packages/core/src/renderTexture/RenderTexture.js +++ b/packages/core/src/renderTexture/RenderTexture.js @@ -94,7 +94,7 @@ */ this.filterFrame = null; - this._updateUvs(); + this.updateUvs(); } /** @@ -120,7 +120,7 @@ this.baseTexture.resize(width, height); } - this._updateUvs(); + this.updateUvs(); } /** diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index 2a9eba9..09eaf9c 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -42,7 +42,7 @@ } const base = this._texture.baseTexture; - const textureSource = base.source; + const textureSource = base.getDrawableSource(); const w = base.width * base.resolution; const h = base.height * base.resolution; @@ -56,3 +56,56 @@ this.drawSegment(context, textureSource, w, h, 18, 19, 28, 29); this.drawSegment(context, textureSource, w, h, 20, 21, 30, 31); }; + +/** + * Renders one segment of the plane. + * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure + * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. + * + * @method drawSegment + * @memberof PIXI.NineSlicePlane# + * @param {CanvasRenderingContext2D} context - The context to draw with. + * @param {CanvasImageSource} textureSource - The source to draw. + * @param {number} w - width of the texture + * @param {number} h - height of the texture + * @param {number} x1 - x index 1 + * @param {number} y1 - y index 1 + * @param {number} x2 - x index 2 + * @param {number} y2 - y index 2 + */ +NineSlicePlane.prototype.drawSegment = function drawSegment(context, textureSource, w, h, x1, y1, x2, y2) +{ + // otherwise you get weird results when using slices of that are 0 wide or high. + const uvs = this.uvs; + const vertices = this.vertices; + + let sw = (uvs[x2] - uvs[x1]) * w; + let sh = (uvs[y2] - uvs[y1]) * h; + let dw = vertices[x2] - vertices[x1]; + let dh = vertices[y2] - vertices[y1]; + + // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. + if (sw < 1) + { + sw = 1; + } + + if (sh < 1) + { + sh = 1; + } + + // make sure destination is at least 1 pixel wide and high, otherwise you get + // lines when rendering close to original size. + if (dw < 1) + { + dw = 1; + } + + if (dh < 1) + { + dh = 1; + } + + context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); +}; diff --git a/packages/core/src/renderTexture/RenderTexture.js b/packages/core/src/renderTexture/RenderTexture.js index f1ecf1f..a64b7e7 100644 --- a/packages/core/src/renderTexture/RenderTexture.js +++ b/packages/core/src/renderTexture/RenderTexture.js @@ -94,7 +94,7 @@ */ this.filterFrame = null; - this._updateUvs(); + this.updateUvs(); } /** @@ -120,7 +120,7 @@ this.baseTexture.resize(width, height); } - this._updateUvs(); + this.updateUvs(); } /** diff --git a/packages/core/src/textures/RenderTexture.js b/packages/core/src/textures/RenderTexture.js deleted file mode 100644 index 34f09b2..0000000 --- a/packages/core/src/textures/RenderTexture.js +++ /dev/null @@ -1,153 +0,0 @@ -import BaseRenderTexture from './BaseRenderTexture'; -import Texture from './Texture'; - -/** - * A RenderTexture is a special texture that allows any PixiJS display object to be rendered to it. - * - * __Hint__: All DisplayObjects (i.e. Sprites) that render to a RenderTexture should be preloaded - * otherwise black rectangles will be drawn instead. - * - * A RenderTexture takes a snapshot of any Display Object given to its render method. For example: - * - * ```js - * let renderer = PIXI.autoDetectRenderer(1024, 1024, { view: canvas, ratio: 1 }); - * let renderTexture = PIXI.RenderTexture.create(800, 600); - * let sprite = PIXI.Sprite.fromImage("spinObj_01.png"); - * - * sprite.position.x = 800/2; - * sprite.position.y = 600/2; - * sprite.anchor.x = 0.5; - * sprite.anchor.y = 0.5; - * - * renderer.render(sprite, renderTexture); - * ``` - * - * The Sprite in this case will be rendered using its local transform. To render this sprite at 0,0 - * you can clear the transform - * - * ```js - * - * sprite.setTransform() - * - * let renderTexture = new PIXI.RenderTexture.create(100, 100); - * - * renderer.render(sprite, renderTexture); // Renders to center of RenderTexture - * ``` - * - * @class - * @extends PIXI.Texture - * @memberof PIXI - */ -export default class RenderTexture extends Texture -{ - /** - * @param {PIXI.BaseRenderTexture} baseRenderTexture - The renderer used for this RenderTexture - * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show - */ - constructor(baseRenderTexture, frame) - { - // support for legacy.. - let _legacyRenderer = null; - - if (!(baseRenderTexture instanceof BaseRenderTexture)) - { - /* eslint-disable prefer-rest-params, no-console */ - const width = arguments[1]; - const height = arguments[2]; - const scaleMode = arguments[3]; - const resolution = arguments[4]; - - // we have an old render texture.. - console.warn(`Please use RenderTexture.create(${width}, ${height}) instead of the ctor directly.`); - _legacyRenderer = arguments[0]; - /* eslint-enable prefer-rest-params, no-console */ - - frame = null; - baseRenderTexture = new BaseRenderTexture({ - width, - height, - scaleMode, - resolution, - }); - } - - /** - * The base texture object that this texture uses - * - * @member {BaseTexture} - */ - super(baseRenderTexture, frame); - - this.legacyRenderer = _legacyRenderer; - - /** - * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered. - * - * @member {boolean} - */ - this.valid = true; - - /** - * FilterSystem temporary storage - * @private - * @member {PIXI.Rectangle} - */ - this.filterFrame = null; - - this._updateUvs(); - } - - /** - * Resizes the RenderTexture. - * - * @param {number} width - The width to resize to. - * @param {number} height - The height to resize to. - * @param {boolean} [resizeBaseTexture=true] - Should the baseTexture.width and height values be resized as well? - */ - resize(width, height, resizeBaseTexture = true) - { - width = Math.ceil(width); - height = Math.ceil(height); - - // TODO - could be not required.. - this.valid = (width > 0 && height > 0); - - this._frame.width = this.orig.width = width; - this._frame.height = this.orig.height = height; - - if (resizeBaseTexture) - { - this.baseTexture.resize(width, height); - } - - this._updateUvs(); - } - - /** - * A short hand way of creating a render texture. - * - * @param {object} [options] - Options - * @param {number} [options.width=100] - The width of the render texture - * @param {number} [options.height=100] - The height of the render texture - * @param {number} [options.scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values - * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the texture being generated - * @return {PIXI.RenderTexture} The new render texture - */ - static create(options) - { - // fallback, old-style: create(width, height, scaleMode, resolution) - if (typeof options === 'number') - { - /* eslint-disable prefer-rest-params */ - options = { - width: options, - height: arguments[1], - scaleMode: arguments[2], - resolution: arguments[3], - }; - /* eslint-enable prefer-rest-params */ - } - - return new RenderTexture(new BaseRenderTexture(options)); - } -} diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index 2a9eba9..09eaf9c 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -42,7 +42,7 @@ } const base = this._texture.baseTexture; - const textureSource = base.source; + const textureSource = base.getDrawableSource(); const w = base.width * base.resolution; const h = base.height * base.resolution; @@ -56,3 +56,56 @@ this.drawSegment(context, textureSource, w, h, 18, 19, 28, 29); this.drawSegment(context, textureSource, w, h, 20, 21, 30, 31); }; + +/** + * Renders one segment of the plane. + * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure + * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. + * + * @method drawSegment + * @memberof PIXI.NineSlicePlane# + * @param {CanvasRenderingContext2D} context - The context to draw with. + * @param {CanvasImageSource} textureSource - The source to draw. + * @param {number} w - width of the texture + * @param {number} h - height of the texture + * @param {number} x1 - x index 1 + * @param {number} y1 - y index 1 + * @param {number} x2 - x index 2 + * @param {number} y2 - y index 2 + */ +NineSlicePlane.prototype.drawSegment = function drawSegment(context, textureSource, w, h, x1, y1, x2, y2) +{ + // otherwise you get weird results when using slices of that are 0 wide or high. + const uvs = this.uvs; + const vertices = this.vertices; + + let sw = (uvs[x2] - uvs[x1]) * w; + let sh = (uvs[y2] - uvs[y1]) * h; + let dw = vertices[x2] - vertices[x1]; + let dh = vertices[y2] - vertices[y1]; + + // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. + if (sw < 1) + { + sw = 1; + } + + if (sh < 1) + { + sh = 1; + } + + // make sure destination is at least 1 pixel wide and high, otherwise you get + // lines when rendering close to original size. + if (dw < 1) + { + dw = 1; + } + + if (dh < 1) + { + dh = 1; + } + + context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); +}; diff --git a/packages/core/src/renderTexture/RenderTexture.js b/packages/core/src/renderTexture/RenderTexture.js index f1ecf1f..a64b7e7 100644 --- a/packages/core/src/renderTexture/RenderTexture.js +++ b/packages/core/src/renderTexture/RenderTexture.js @@ -94,7 +94,7 @@ */ this.filterFrame = null; - this._updateUvs(); + this.updateUvs(); } /** @@ -120,7 +120,7 @@ this.baseTexture.resize(width, height); } - this._updateUvs(); + this.updateUvs(); } /** diff --git a/packages/core/src/textures/RenderTexture.js b/packages/core/src/textures/RenderTexture.js deleted file mode 100644 index 34f09b2..0000000 --- a/packages/core/src/textures/RenderTexture.js +++ /dev/null @@ -1,153 +0,0 @@ -import BaseRenderTexture from './BaseRenderTexture'; -import Texture from './Texture'; - -/** - * A RenderTexture is a special texture that allows any PixiJS display object to be rendered to it. - * - * __Hint__: All DisplayObjects (i.e. Sprites) that render to a RenderTexture should be preloaded - * otherwise black rectangles will be drawn instead. - * - * A RenderTexture takes a snapshot of any Display Object given to its render method. For example: - * - * ```js - * let renderer = PIXI.autoDetectRenderer(1024, 1024, { view: canvas, ratio: 1 }); - * let renderTexture = PIXI.RenderTexture.create(800, 600); - * let sprite = PIXI.Sprite.fromImage("spinObj_01.png"); - * - * sprite.position.x = 800/2; - * sprite.position.y = 600/2; - * sprite.anchor.x = 0.5; - * sprite.anchor.y = 0.5; - * - * renderer.render(sprite, renderTexture); - * ``` - * - * The Sprite in this case will be rendered using its local transform. To render this sprite at 0,0 - * you can clear the transform - * - * ```js - * - * sprite.setTransform() - * - * let renderTexture = new PIXI.RenderTexture.create(100, 100); - * - * renderer.render(sprite, renderTexture); // Renders to center of RenderTexture - * ``` - * - * @class - * @extends PIXI.Texture - * @memberof PIXI - */ -export default class RenderTexture extends Texture -{ - /** - * @param {PIXI.BaseRenderTexture} baseRenderTexture - The renderer used for this RenderTexture - * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show - */ - constructor(baseRenderTexture, frame) - { - // support for legacy.. - let _legacyRenderer = null; - - if (!(baseRenderTexture instanceof BaseRenderTexture)) - { - /* eslint-disable prefer-rest-params, no-console */ - const width = arguments[1]; - const height = arguments[2]; - const scaleMode = arguments[3]; - const resolution = arguments[4]; - - // we have an old render texture.. - console.warn(`Please use RenderTexture.create(${width}, ${height}) instead of the ctor directly.`); - _legacyRenderer = arguments[0]; - /* eslint-enable prefer-rest-params, no-console */ - - frame = null; - baseRenderTexture = new BaseRenderTexture({ - width, - height, - scaleMode, - resolution, - }); - } - - /** - * The base texture object that this texture uses - * - * @member {BaseTexture} - */ - super(baseRenderTexture, frame); - - this.legacyRenderer = _legacyRenderer; - - /** - * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered. - * - * @member {boolean} - */ - this.valid = true; - - /** - * FilterSystem temporary storage - * @private - * @member {PIXI.Rectangle} - */ - this.filterFrame = null; - - this._updateUvs(); - } - - /** - * Resizes the RenderTexture. - * - * @param {number} width - The width to resize to. - * @param {number} height - The height to resize to. - * @param {boolean} [resizeBaseTexture=true] - Should the baseTexture.width and height values be resized as well? - */ - resize(width, height, resizeBaseTexture = true) - { - width = Math.ceil(width); - height = Math.ceil(height); - - // TODO - could be not required.. - this.valid = (width > 0 && height > 0); - - this._frame.width = this.orig.width = width; - this._frame.height = this.orig.height = height; - - if (resizeBaseTexture) - { - this.baseTexture.resize(width, height); - } - - this._updateUvs(); - } - - /** - * A short hand way of creating a render texture. - * - * @param {object} [options] - Options - * @param {number} [options.width=100] - The width of the render texture - * @param {number} [options.height=100] - The height of the render texture - * @param {number} [options.scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values - * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the texture being generated - * @return {PIXI.RenderTexture} The new render texture - */ - static create(options) - { - // fallback, old-style: create(width, height, scaleMode, resolution) - if (typeof options === 'number') - { - /* eslint-disable prefer-rest-params */ - options = { - width: options, - height: arguments[1], - scaleMode: arguments[2], - resolution: arguments[3], - }; - /* eslint-enable prefer-rest-params */ - } - - return new RenderTexture(new BaseRenderTexture(options)); - } -} diff --git a/packages/core/src/textures/Texture.js b/packages/core/src/textures/Texture.js index 5a22225..237dec3 100644 --- a/packages/core/src/textures/Texture.js +++ b/packages/core/src/textures/Texture.js @@ -81,7 +81,7 @@ /** * This is the trimmed area of original texture, before it was put in atlas - * Please call `_updateUvs()` after you change coordinates of `trim` manually. + * Please call `updateUvs()` after you change coordinates of `trim` manually. * * @member {PIXI.Rectangle} */ @@ -102,7 +102,7 @@ this.requiresUpdate = false; /** - * The WebGL UV data cache. + * The WebGL UV data cache. Can be used as quad UV * * @member {PIXI.TextureUvs} * @private @@ -110,6 +110,14 @@ this._uvs = null; /** + * Default TextureMatrix instance for this texture + * By default that object is not created because its heavy + * + * @member {PIXI.TextureMatrix} + */ + this.uvMatrix = null; + + /** * This is the area of original texture, before it was put in atlas * * @member {PIXI.Rectangle} @@ -145,24 +153,16 @@ } /** - * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. + * Update ID is observed by sprites and TextureMatrix instances. + * Call updateUvs() to increment it. * - * @event PIXI.Texture#update - * @protected - * @param {PIXI.Texture} texture - Instance of texture being updated. + * @member {number} + * @private */ this._updateID = 0; /** - * Contains data for uvs. May contain clamp settings and some matrices. - * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} - * @default null - */ - this.transform = null; - - /** * The ids under which this Texture has been added to the texture cache. This is * automatically set as long as Texture.addToCache is used, but may not be set if a * Texture is added directly to the TextureCache array. @@ -255,8 +255,9 @@ /** * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture. + * Call it after changing the frame */ - _updateUvs() + updateUvs() { if (!this._uvs) { @@ -446,7 +447,7 @@ /** * The frame specifies the region of the base texture that this texture uses. - * Please call `_updateUvs()` after you change coordinates of `frame` manually. + * Please call `updateUvs()` after you change coordinates of `frame` manually. * * @member {PIXI.Rectangle} */ @@ -484,7 +485,7 @@ if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } @@ -507,7 +508,7 @@ this._rotate = rotate; if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index 2a9eba9..09eaf9c 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -42,7 +42,7 @@ } const base = this._texture.baseTexture; - const textureSource = base.source; + const textureSource = base.getDrawableSource(); const w = base.width * base.resolution; const h = base.height * base.resolution; @@ -56,3 +56,56 @@ this.drawSegment(context, textureSource, w, h, 18, 19, 28, 29); this.drawSegment(context, textureSource, w, h, 20, 21, 30, 31); }; + +/** + * Renders one segment of the plane. + * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure + * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. + * + * @method drawSegment + * @memberof PIXI.NineSlicePlane# + * @param {CanvasRenderingContext2D} context - The context to draw with. + * @param {CanvasImageSource} textureSource - The source to draw. + * @param {number} w - width of the texture + * @param {number} h - height of the texture + * @param {number} x1 - x index 1 + * @param {number} y1 - y index 1 + * @param {number} x2 - x index 2 + * @param {number} y2 - y index 2 + */ +NineSlicePlane.prototype.drawSegment = function drawSegment(context, textureSource, w, h, x1, y1, x2, y2) +{ + // otherwise you get weird results when using slices of that are 0 wide or high. + const uvs = this.uvs; + const vertices = this.vertices; + + let sw = (uvs[x2] - uvs[x1]) * w; + let sh = (uvs[y2] - uvs[y1]) * h; + let dw = vertices[x2] - vertices[x1]; + let dh = vertices[y2] - vertices[y1]; + + // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. + if (sw < 1) + { + sw = 1; + } + + if (sh < 1) + { + sh = 1; + } + + // make sure destination is at least 1 pixel wide and high, otherwise you get + // lines when rendering close to original size. + if (dw < 1) + { + dw = 1; + } + + if (dh < 1) + { + dh = 1; + } + + context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); +}; diff --git a/packages/core/src/renderTexture/RenderTexture.js b/packages/core/src/renderTexture/RenderTexture.js index f1ecf1f..a64b7e7 100644 --- a/packages/core/src/renderTexture/RenderTexture.js +++ b/packages/core/src/renderTexture/RenderTexture.js @@ -94,7 +94,7 @@ */ this.filterFrame = null; - this._updateUvs(); + this.updateUvs(); } /** @@ -120,7 +120,7 @@ this.baseTexture.resize(width, height); } - this._updateUvs(); + this.updateUvs(); } /** diff --git a/packages/core/src/textures/RenderTexture.js b/packages/core/src/textures/RenderTexture.js deleted file mode 100644 index 34f09b2..0000000 --- a/packages/core/src/textures/RenderTexture.js +++ /dev/null @@ -1,153 +0,0 @@ -import BaseRenderTexture from './BaseRenderTexture'; -import Texture from './Texture'; - -/** - * A RenderTexture is a special texture that allows any PixiJS display object to be rendered to it. - * - * __Hint__: All DisplayObjects (i.e. Sprites) that render to a RenderTexture should be preloaded - * otherwise black rectangles will be drawn instead. - * - * A RenderTexture takes a snapshot of any Display Object given to its render method. For example: - * - * ```js - * let renderer = PIXI.autoDetectRenderer(1024, 1024, { view: canvas, ratio: 1 }); - * let renderTexture = PIXI.RenderTexture.create(800, 600); - * let sprite = PIXI.Sprite.fromImage("spinObj_01.png"); - * - * sprite.position.x = 800/2; - * sprite.position.y = 600/2; - * sprite.anchor.x = 0.5; - * sprite.anchor.y = 0.5; - * - * renderer.render(sprite, renderTexture); - * ``` - * - * The Sprite in this case will be rendered using its local transform. To render this sprite at 0,0 - * you can clear the transform - * - * ```js - * - * sprite.setTransform() - * - * let renderTexture = new PIXI.RenderTexture.create(100, 100); - * - * renderer.render(sprite, renderTexture); // Renders to center of RenderTexture - * ``` - * - * @class - * @extends PIXI.Texture - * @memberof PIXI - */ -export default class RenderTexture extends Texture -{ - /** - * @param {PIXI.BaseRenderTexture} baseRenderTexture - The renderer used for this RenderTexture - * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show - */ - constructor(baseRenderTexture, frame) - { - // support for legacy.. - let _legacyRenderer = null; - - if (!(baseRenderTexture instanceof BaseRenderTexture)) - { - /* eslint-disable prefer-rest-params, no-console */ - const width = arguments[1]; - const height = arguments[2]; - const scaleMode = arguments[3]; - const resolution = arguments[4]; - - // we have an old render texture.. - console.warn(`Please use RenderTexture.create(${width}, ${height}) instead of the ctor directly.`); - _legacyRenderer = arguments[0]; - /* eslint-enable prefer-rest-params, no-console */ - - frame = null; - baseRenderTexture = new BaseRenderTexture({ - width, - height, - scaleMode, - resolution, - }); - } - - /** - * The base texture object that this texture uses - * - * @member {BaseTexture} - */ - super(baseRenderTexture, frame); - - this.legacyRenderer = _legacyRenderer; - - /** - * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered. - * - * @member {boolean} - */ - this.valid = true; - - /** - * FilterSystem temporary storage - * @private - * @member {PIXI.Rectangle} - */ - this.filterFrame = null; - - this._updateUvs(); - } - - /** - * Resizes the RenderTexture. - * - * @param {number} width - The width to resize to. - * @param {number} height - The height to resize to. - * @param {boolean} [resizeBaseTexture=true] - Should the baseTexture.width and height values be resized as well? - */ - resize(width, height, resizeBaseTexture = true) - { - width = Math.ceil(width); - height = Math.ceil(height); - - // TODO - could be not required.. - this.valid = (width > 0 && height > 0); - - this._frame.width = this.orig.width = width; - this._frame.height = this.orig.height = height; - - if (resizeBaseTexture) - { - this.baseTexture.resize(width, height); - } - - this._updateUvs(); - } - - /** - * A short hand way of creating a render texture. - * - * @param {object} [options] - Options - * @param {number} [options.width=100] - The width of the render texture - * @param {number} [options.height=100] - The height of the render texture - * @param {number} [options.scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values - * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the texture being generated - * @return {PIXI.RenderTexture} The new render texture - */ - static create(options) - { - // fallback, old-style: create(width, height, scaleMode, resolution) - if (typeof options === 'number') - { - /* eslint-disable prefer-rest-params */ - options = { - width: options, - height: arguments[1], - scaleMode: arguments[2], - resolution: arguments[3], - }; - /* eslint-enable prefer-rest-params */ - } - - return new RenderTexture(new BaseRenderTexture(options)); - } -} diff --git a/packages/core/src/textures/Texture.js b/packages/core/src/textures/Texture.js index 5a22225..237dec3 100644 --- a/packages/core/src/textures/Texture.js +++ b/packages/core/src/textures/Texture.js @@ -81,7 +81,7 @@ /** * This is the trimmed area of original texture, before it was put in atlas - * Please call `_updateUvs()` after you change coordinates of `trim` manually. + * Please call `updateUvs()` after you change coordinates of `trim` manually. * * @member {PIXI.Rectangle} */ @@ -102,7 +102,7 @@ this.requiresUpdate = false; /** - * The WebGL UV data cache. + * The WebGL UV data cache. Can be used as quad UV * * @member {PIXI.TextureUvs} * @private @@ -110,6 +110,14 @@ this._uvs = null; /** + * Default TextureMatrix instance for this texture + * By default that object is not created because its heavy + * + * @member {PIXI.TextureMatrix} + */ + this.uvMatrix = null; + + /** * This is the area of original texture, before it was put in atlas * * @member {PIXI.Rectangle} @@ -145,24 +153,16 @@ } /** - * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. + * Update ID is observed by sprites and TextureMatrix instances. + * Call updateUvs() to increment it. * - * @event PIXI.Texture#update - * @protected - * @param {PIXI.Texture} texture - Instance of texture being updated. + * @member {number} + * @private */ this._updateID = 0; /** - * Contains data for uvs. May contain clamp settings and some matrices. - * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} - * @default null - */ - this.transform = null; - - /** * The ids under which this Texture has been added to the texture cache. This is * automatically set as long as Texture.addToCache is used, but may not be set if a * Texture is added directly to the TextureCache array. @@ -255,8 +255,9 @@ /** * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture. + * Call it after changing the frame */ - _updateUvs() + updateUvs() { if (!this._uvs) { @@ -446,7 +447,7 @@ /** * The frame specifies the region of the base texture that this texture uses. - * Please call `_updateUvs()` after you change coordinates of `frame` manually. + * Please call `updateUvs()` after you change coordinates of `frame` manually. * * @member {PIXI.Rectangle} */ @@ -484,7 +485,7 @@ if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } @@ -507,7 +508,7 @@ this._rotate = rotate; if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } diff --git a/packages/core/src/textures/TextureMatrix.js b/packages/core/src/textures/TextureMatrix.js index 2d4ecea..8198ed1 100644 --- a/packages/core/src/textures/TextureMatrix.js +++ b/packages/core/src/textures/TextureMatrix.js @@ -3,12 +3,18 @@ const tempMat = new Matrix(); /** - * Class controls uv transform and frame clamp for texture - * Can be used in Texture "transform" field, or separately, you can use different clamp settings on the same texture. + * Class controls uv mapping from Texture normal space to BaseTexture normal space. + * Takes `trim` and `rotate` into account. + * May contain clamp settings for Meshes and TilingSprite. + * + * Can be used in Texture `uvMatrix` field, or separately, you can use different clamp settings on the same texture. * If you want to add support for texture region of certain feature or filter, that's what you're looking for. * + * Takes track of Texture changes through `_lastTextureID` private field. + * Use `update()` method call to track it from outside. + * * @see PIXI.Texture - * @see PIXI.mesh.Mesh + * @see PIXI.Mesh * @see PIXI.TilingSprite * @class * @memberof PIXI @@ -31,6 +37,10 @@ this.uClampOffset = new Float32Array(2); + /** + * @member {number} Tracks Texture frame changes + * @private + */ this._lastTextureID = -1; /** diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index 2a9eba9..09eaf9c 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -42,7 +42,7 @@ } const base = this._texture.baseTexture; - const textureSource = base.source; + const textureSource = base.getDrawableSource(); const w = base.width * base.resolution; const h = base.height * base.resolution; @@ -56,3 +56,56 @@ this.drawSegment(context, textureSource, w, h, 18, 19, 28, 29); this.drawSegment(context, textureSource, w, h, 20, 21, 30, 31); }; + +/** + * Renders one segment of the plane. + * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure + * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. + * + * @method drawSegment + * @memberof PIXI.NineSlicePlane# + * @param {CanvasRenderingContext2D} context - The context to draw with. + * @param {CanvasImageSource} textureSource - The source to draw. + * @param {number} w - width of the texture + * @param {number} h - height of the texture + * @param {number} x1 - x index 1 + * @param {number} y1 - y index 1 + * @param {number} x2 - x index 2 + * @param {number} y2 - y index 2 + */ +NineSlicePlane.prototype.drawSegment = function drawSegment(context, textureSource, w, h, x1, y1, x2, y2) +{ + // otherwise you get weird results when using slices of that are 0 wide or high. + const uvs = this.uvs; + const vertices = this.vertices; + + let sw = (uvs[x2] - uvs[x1]) * w; + let sh = (uvs[y2] - uvs[y1]) * h; + let dw = vertices[x2] - vertices[x1]; + let dh = vertices[y2] - vertices[y1]; + + // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. + if (sw < 1) + { + sw = 1; + } + + if (sh < 1) + { + sh = 1; + } + + // make sure destination is at least 1 pixel wide and high, otherwise you get + // lines when rendering close to original size. + if (dw < 1) + { + dw = 1; + } + + if (dh < 1) + { + dh = 1; + } + + context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); +}; diff --git a/packages/core/src/renderTexture/RenderTexture.js b/packages/core/src/renderTexture/RenderTexture.js index f1ecf1f..a64b7e7 100644 --- a/packages/core/src/renderTexture/RenderTexture.js +++ b/packages/core/src/renderTexture/RenderTexture.js @@ -94,7 +94,7 @@ */ this.filterFrame = null; - this._updateUvs(); + this.updateUvs(); } /** @@ -120,7 +120,7 @@ this.baseTexture.resize(width, height); } - this._updateUvs(); + this.updateUvs(); } /** diff --git a/packages/core/src/textures/RenderTexture.js b/packages/core/src/textures/RenderTexture.js deleted file mode 100644 index 34f09b2..0000000 --- a/packages/core/src/textures/RenderTexture.js +++ /dev/null @@ -1,153 +0,0 @@ -import BaseRenderTexture from './BaseRenderTexture'; -import Texture from './Texture'; - -/** - * A RenderTexture is a special texture that allows any PixiJS display object to be rendered to it. - * - * __Hint__: All DisplayObjects (i.e. Sprites) that render to a RenderTexture should be preloaded - * otherwise black rectangles will be drawn instead. - * - * A RenderTexture takes a snapshot of any Display Object given to its render method. For example: - * - * ```js - * let renderer = PIXI.autoDetectRenderer(1024, 1024, { view: canvas, ratio: 1 }); - * let renderTexture = PIXI.RenderTexture.create(800, 600); - * let sprite = PIXI.Sprite.fromImage("spinObj_01.png"); - * - * sprite.position.x = 800/2; - * sprite.position.y = 600/2; - * sprite.anchor.x = 0.5; - * sprite.anchor.y = 0.5; - * - * renderer.render(sprite, renderTexture); - * ``` - * - * The Sprite in this case will be rendered using its local transform. To render this sprite at 0,0 - * you can clear the transform - * - * ```js - * - * sprite.setTransform() - * - * let renderTexture = new PIXI.RenderTexture.create(100, 100); - * - * renderer.render(sprite, renderTexture); // Renders to center of RenderTexture - * ``` - * - * @class - * @extends PIXI.Texture - * @memberof PIXI - */ -export default class RenderTexture extends Texture -{ - /** - * @param {PIXI.BaseRenderTexture} baseRenderTexture - The renderer used for this RenderTexture - * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show - */ - constructor(baseRenderTexture, frame) - { - // support for legacy.. - let _legacyRenderer = null; - - if (!(baseRenderTexture instanceof BaseRenderTexture)) - { - /* eslint-disable prefer-rest-params, no-console */ - const width = arguments[1]; - const height = arguments[2]; - const scaleMode = arguments[3]; - const resolution = arguments[4]; - - // we have an old render texture.. - console.warn(`Please use RenderTexture.create(${width}, ${height}) instead of the ctor directly.`); - _legacyRenderer = arguments[0]; - /* eslint-enable prefer-rest-params, no-console */ - - frame = null; - baseRenderTexture = new BaseRenderTexture({ - width, - height, - scaleMode, - resolution, - }); - } - - /** - * The base texture object that this texture uses - * - * @member {BaseTexture} - */ - super(baseRenderTexture, frame); - - this.legacyRenderer = _legacyRenderer; - - /** - * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered. - * - * @member {boolean} - */ - this.valid = true; - - /** - * FilterSystem temporary storage - * @private - * @member {PIXI.Rectangle} - */ - this.filterFrame = null; - - this._updateUvs(); - } - - /** - * Resizes the RenderTexture. - * - * @param {number} width - The width to resize to. - * @param {number} height - The height to resize to. - * @param {boolean} [resizeBaseTexture=true] - Should the baseTexture.width and height values be resized as well? - */ - resize(width, height, resizeBaseTexture = true) - { - width = Math.ceil(width); - height = Math.ceil(height); - - // TODO - could be not required.. - this.valid = (width > 0 && height > 0); - - this._frame.width = this.orig.width = width; - this._frame.height = this.orig.height = height; - - if (resizeBaseTexture) - { - this.baseTexture.resize(width, height); - } - - this._updateUvs(); - } - - /** - * A short hand way of creating a render texture. - * - * @param {object} [options] - Options - * @param {number} [options.width=100] - The width of the render texture - * @param {number} [options.height=100] - The height of the render texture - * @param {number} [options.scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values - * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the texture being generated - * @return {PIXI.RenderTexture} The new render texture - */ - static create(options) - { - // fallback, old-style: create(width, height, scaleMode, resolution) - if (typeof options === 'number') - { - /* eslint-disable prefer-rest-params */ - options = { - width: options, - height: arguments[1], - scaleMode: arguments[2], - resolution: arguments[3], - }; - /* eslint-enable prefer-rest-params */ - } - - return new RenderTexture(new BaseRenderTexture(options)); - } -} diff --git a/packages/core/src/textures/Texture.js b/packages/core/src/textures/Texture.js index 5a22225..237dec3 100644 --- a/packages/core/src/textures/Texture.js +++ b/packages/core/src/textures/Texture.js @@ -81,7 +81,7 @@ /** * This is the trimmed area of original texture, before it was put in atlas - * Please call `_updateUvs()` after you change coordinates of `trim` manually. + * Please call `updateUvs()` after you change coordinates of `trim` manually. * * @member {PIXI.Rectangle} */ @@ -102,7 +102,7 @@ this.requiresUpdate = false; /** - * The WebGL UV data cache. + * The WebGL UV data cache. Can be used as quad UV * * @member {PIXI.TextureUvs} * @private @@ -110,6 +110,14 @@ this._uvs = null; /** + * Default TextureMatrix instance for this texture + * By default that object is not created because its heavy + * + * @member {PIXI.TextureMatrix} + */ + this.uvMatrix = null; + + /** * This is the area of original texture, before it was put in atlas * * @member {PIXI.Rectangle} @@ -145,24 +153,16 @@ } /** - * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. + * Update ID is observed by sprites and TextureMatrix instances. + * Call updateUvs() to increment it. * - * @event PIXI.Texture#update - * @protected - * @param {PIXI.Texture} texture - Instance of texture being updated. + * @member {number} + * @private */ this._updateID = 0; /** - * Contains data for uvs. May contain clamp settings and some matrices. - * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} - * @default null - */ - this.transform = null; - - /** * The ids under which this Texture has been added to the texture cache. This is * automatically set as long as Texture.addToCache is used, but may not be set if a * Texture is added directly to the TextureCache array. @@ -255,8 +255,9 @@ /** * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture. + * Call it after changing the frame */ - _updateUvs() + updateUvs() { if (!this._uvs) { @@ -446,7 +447,7 @@ /** * The frame specifies the region of the base texture that this texture uses. - * Please call `_updateUvs()` after you change coordinates of `frame` manually. + * Please call `updateUvs()` after you change coordinates of `frame` manually. * * @member {PIXI.Rectangle} */ @@ -484,7 +485,7 @@ if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } @@ -507,7 +508,7 @@ this._rotate = rotate; if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } diff --git a/packages/core/src/textures/TextureMatrix.js b/packages/core/src/textures/TextureMatrix.js index 2d4ecea..8198ed1 100644 --- a/packages/core/src/textures/TextureMatrix.js +++ b/packages/core/src/textures/TextureMatrix.js @@ -3,12 +3,18 @@ const tempMat = new Matrix(); /** - * Class controls uv transform and frame clamp for texture - * Can be used in Texture "transform" field, or separately, you can use different clamp settings on the same texture. + * Class controls uv mapping from Texture normal space to BaseTexture normal space. + * Takes `trim` and `rotate` into account. + * May contain clamp settings for Meshes and TilingSprite. + * + * Can be used in Texture `uvMatrix` field, or separately, you can use different clamp settings on the same texture. * If you want to add support for texture region of certain feature or filter, that's what you're looking for. * + * Takes track of Texture changes through `_lastTextureID` private field. + * Use `update()` method call to track it from outside. + * * @see PIXI.Texture - * @see PIXI.mesh.Mesh + * @see PIXI.Mesh * @see PIXI.TilingSprite * @class * @memberof PIXI @@ -31,6 +37,10 @@ this.uClampOffset = new Float32Array(2); + /** + * @member {number} Tracks Texture frame changes + * @private + */ this._lastTextureID = -1; /** diff --git a/packages/mesh/src/Mesh.js b/packages/mesh/src/Mesh.js index 55dc092..e7c8f9e 100644 --- a/packages/mesh/src/Mesh.js +++ b/packages/mesh/src/Mesh.js @@ -1,5 +1,5 @@ import RawMesh from './RawMesh'; -import { Geometry, Program, Shader, Texture } from '@pixi/core'; +import { Geometry, Program, Shader, Texture, TextureMatrix } from '@pixi/core'; import { Matrix } from '@pixi/math'; import { BLEND_MODES } from '@pixi/constants'; import { hex2rgb, premultiplyRgba } from '@pixi/utils'; @@ -23,7 +23,7 @@ * @param {Uint16Array} [indices] - if you want to specify the indices * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts */ - constructor(texture, vertices, uvs, indices, drawMode) + constructor(texture = Texture.EMPTY, vertices, uvs, indices, drawMode) { const geometry = new Geometry(); @@ -40,7 +40,7 @@ const uniforms = { uSampler: texture, - uTransform: Matrix.IDENTITY, + uTextureMatrix: Matrix.IDENTITY, alpha: 1, uColor: new Float32Array([1, 1, 1, 1]), }; @@ -70,7 +70,7 @@ * @default PIXI.Texture.EMPTY * @private */ - this.texture = texture || Texture.EMPTY; + this.texture = texture; /** * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any @@ -85,6 +85,31 @@ this.tint = 0xFFFFFF; this.blendMode = BLEND_MODES.NORMAL; + + /** + * TextureMatrix instance for this Mesh, used to track Texture changes + * + * @member {PIXI.TextureMatrix} + * @readonly + */ + this.uvMatrix = new TextureMatrix(this._texture); + + /** + * whether or not upload uvMatrix to shader + * if its false, then uvs should be pre-multiplied + * if you change it for generated mesh, please call 'refresh(true)' + * @member {boolean} + * @default false + */ + this.uploadUvMatrix = false; + + /** + * Uploads vertices buffer every frame, like in PixiJS V3 and V4. + * + * @member {boolean} + * @default true + */ + this.autoUpdate = true; } /** @@ -152,7 +177,7 @@ if (value) { // wait for the texture to load - if (value.baseTexture.hasLoaded) + if (value.baseTexture.valid) { this._onTextureUpdate(); } @@ -177,6 +202,62 @@ */ _onTextureUpdate() { + // constructor texture update stop + if (!this.uvMatrix) + { + return; + } + this.uvMatrix.texture = this._texture; + this.refresh(); + } + + /** + * multiplies uvs only if uploadUvMatrix is false + * call it after you change uvs manually + * make sure that texture is valid + */ + multiplyUvs() + { + if (!this.uploadUvMatrix) + { + this.uvMatrix.multiplyUvs(this.uvs); + } + } + + /** + * Refreshes uvs for generated meshes (rope, plane) + * sometimes refreshes vertices too + * + * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case + */ + refresh(forceUpdate) + { + if (this.uvMatrix.update(forceUpdate)) + { + this._refresh(); + } + } + + /** + * Updates the object transform for rendering + * + * @private + */ + updateTransform() + { + if (this.autoUpdate) + { + this.geometry.buffers[0].update(); + } + this.containerUpdateTransform(); + } + + /** + * re-calculates mesh coords + * @private + */ + _refresh() + { /* empty */ } } diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index 2a9eba9..09eaf9c 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -42,7 +42,7 @@ } const base = this._texture.baseTexture; - const textureSource = base.source; + const textureSource = base.getDrawableSource(); const w = base.width * base.resolution; const h = base.height * base.resolution; @@ -56,3 +56,56 @@ this.drawSegment(context, textureSource, w, h, 18, 19, 28, 29); this.drawSegment(context, textureSource, w, h, 20, 21, 30, 31); }; + +/** + * Renders one segment of the plane. + * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure + * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. + * + * @method drawSegment + * @memberof PIXI.NineSlicePlane# + * @param {CanvasRenderingContext2D} context - The context to draw with. + * @param {CanvasImageSource} textureSource - The source to draw. + * @param {number} w - width of the texture + * @param {number} h - height of the texture + * @param {number} x1 - x index 1 + * @param {number} y1 - y index 1 + * @param {number} x2 - x index 2 + * @param {number} y2 - y index 2 + */ +NineSlicePlane.prototype.drawSegment = function drawSegment(context, textureSource, w, h, x1, y1, x2, y2) +{ + // otherwise you get weird results when using slices of that are 0 wide or high. + const uvs = this.uvs; + const vertices = this.vertices; + + let sw = (uvs[x2] - uvs[x1]) * w; + let sh = (uvs[y2] - uvs[y1]) * h; + let dw = vertices[x2] - vertices[x1]; + let dh = vertices[y2] - vertices[y1]; + + // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. + if (sw < 1) + { + sw = 1; + } + + if (sh < 1) + { + sh = 1; + } + + // make sure destination is at least 1 pixel wide and high, otherwise you get + // lines when rendering close to original size. + if (dw < 1) + { + dw = 1; + } + + if (dh < 1) + { + dh = 1; + } + + context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); +}; diff --git a/packages/core/src/renderTexture/RenderTexture.js b/packages/core/src/renderTexture/RenderTexture.js index f1ecf1f..a64b7e7 100644 --- a/packages/core/src/renderTexture/RenderTexture.js +++ b/packages/core/src/renderTexture/RenderTexture.js @@ -94,7 +94,7 @@ */ this.filterFrame = null; - this._updateUvs(); + this.updateUvs(); } /** @@ -120,7 +120,7 @@ this.baseTexture.resize(width, height); } - this._updateUvs(); + this.updateUvs(); } /** diff --git a/packages/core/src/textures/RenderTexture.js b/packages/core/src/textures/RenderTexture.js deleted file mode 100644 index 34f09b2..0000000 --- a/packages/core/src/textures/RenderTexture.js +++ /dev/null @@ -1,153 +0,0 @@ -import BaseRenderTexture from './BaseRenderTexture'; -import Texture from './Texture'; - -/** - * A RenderTexture is a special texture that allows any PixiJS display object to be rendered to it. - * - * __Hint__: All DisplayObjects (i.e. Sprites) that render to a RenderTexture should be preloaded - * otherwise black rectangles will be drawn instead. - * - * A RenderTexture takes a snapshot of any Display Object given to its render method. For example: - * - * ```js - * let renderer = PIXI.autoDetectRenderer(1024, 1024, { view: canvas, ratio: 1 }); - * let renderTexture = PIXI.RenderTexture.create(800, 600); - * let sprite = PIXI.Sprite.fromImage("spinObj_01.png"); - * - * sprite.position.x = 800/2; - * sprite.position.y = 600/2; - * sprite.anchor.x = 0.5; - * sprite.anchor.y = 0.5; - * - * renderer.render(sprite, renderTexture); - * ``` - * - * The Sprite in this case will be rendered using its local transform. To render this sprite at 0,0 - * you can clear the transform - * - * ```js - * - * sprite.setTransform() - * - * let renderTexture = new PIXI.RenderTexture.create(100, 100); - * - * renderer.render(sprite, renderTexture); // Renders to center of RenderTexture - * ``` - * - * @class - * @extends PIXI.Texture - * @memberof PIXI - */ -export default class RenderTexture extends Texture -{ - /** - * @param {PIXI.BaseRenderTexture} baseRenderTexture - The renderer used for this RenderTexture - * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show - */ - constructor(baseRenderTexture, frame) - { - // support for legacy.. - let _legacyRenderer = null; - - if (!(baseRenderTexture instanceof BaseRenderTexture)) - { - /* eslint-disable prefer-rest-params, no-console */ - const width = arguments[1]; - const height = arguments[2]; - const scaleMode = arguments[3]; - const resolution = arguments[4]; - - // we have an old render texture.. - console.warn(`Please use RenderTexture.create(${width}, ${height}) instead of the ctor directly.`); - _legacyRenderer = arguments[0]; - /* eslint-enable prefer-rest-params, no-console */ - - frame = null; - baseRenderTexture = new BaseRenderTexture({ - width, - height, - scaleMode, - resolution, - }); - } - - /** - * The base texture object that this texture uses - * - * @member {BaseTexture} - */ - super(baseRenderTexture, frame); - - this.legacyRenderer = _legacyRenderer; - - /** - * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered. - * - * @member {boolean} - */ - this.valid = true; - - /** - * FilterSystem temporary storage - * @private - * @member {PIXI.Rectangle} - */ - this.filterFrame = null; - - this._updateUvs(); - } - - /** - * Resizes the RenderTexture. - * - * @param {number} width - The width to resize to. - * @param {number} height - The height to resize to. - * @param {boolean} [resizeBaseTexture=true] - Should the baseTexture.width and height values be resized as well? - */ - resize(width, height, resizeBaseTexture = true) - { - width = Math.ceil(width); - height = Math.ceil(height); - - // TODO - could be not required.. - this.valid = (width > 0 && height > 0); - - this._frame.width = this.orig.width = width; - this._frame.height = this.orig.height = height; - - if (resizeBaseTexture) - { - this.baseTexture.resize(width, height); - } - - this._updateUvs(); - } - - /** - * A short hand way of creating a render texture. - * - * @param {object} [options] - Options - * @param {number} [options.width=100] - The width of the render texture - * @param {number} [options.height=100] - The height of the render texture - * @param {number} [options.scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values - * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the texture being generated - * @return {PIXI.RenderTexture} The new render texture - */ - static create(options) - { - // fallback, old-style: create(width, height, scaleMode, resolution) - if (typeof options === 'number') - { - /* eslint-disable prefer-rest-params */ - options = { - width: options, - height: arguments[1], - scaleMode: arguments[2], - resolution: arguments[3], - }; - /* eslint-enable prefer-rest-params */ - } - - return new RenderTexture(new BaseRenderTexture(options)); - } -} diff --git a/packages/core/src/textures/Texture.js b/packages/core/src/textures/Texture.js index 5a22225..237dec3 100644 --- a/packages/core/src/textures/Texture.js +++ b/packages/core/src/textures/Texture.js @@ -81,7 +81,7 @@ /** * This is the trimmed area of original texture, before it was put in atlas - * Please call `_updateUvs()` after you change coordinates of `trim` manually. + * Please call `updateUvs()` after you change coordinates of `trim` manually. * * @member {PIXI.Rectangle} */ @@ -102,7 +102,7 @@ this.requiresUpdate = false; /** - * The WebGL UV data cache. + * The WebGL UV data cache. Can be used as quad UV * * @member {PIXI.TextureUvs} * @private @@ -110,6 +110,14 @@ this._uvs = null; /** + * Default TextureMatrix instance for this texture + * By default that object is not created because its heavy + * + * @member {PIXI.TextureMatrix} + */ + this.uvMatrix = null; + + /** * This is the area of original texture, before it was put in atlas * * @member {PIXI.Rectangle} @@ -145,24 +153,16 @@ } /** - * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. + * Update ID is observed by sprites and TextureMatrix instances. + * Call updateUvs() to increment it. * - * @event PIXI.Texture#update - * @protected - * @param {PIXI.Texture} texture - Instance of texture being updated. + * @member {number} + * @private */ this._updateID = 0; /** - * Contains data for uvs. May contain clamp settings and some matrices. - * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} - * @default null - */ - this.transform = null; - - /** * The ids under which this Texture has been added to the texture cache. This is * automatically set as long as Texture.addToCache is used, but may not be set if a * Texture is added directly to the TextureCache array. @@ -255,8 +255,9 @@ /** * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture. + * Call it after changing the frame */ - _updateUvs() + updateUvs() { if (!this._uvs) { @@ -446,7 +447,7 @@ /** * The frame specifies the region of the base texture that this texture uses. - * Please call `_updateUvs()` after you change coordinates of `frame` manually. + * Please call `updateUvs()` after you change coordinates of `frame` manually. * * @member {PIXI.Rectangle} */ @@ -484,7 +485,7 @@ if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } @@ -507,7 +508,7 @@ this._rotate = rotate; if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } diff --git a/packages/core/src/textures/TextureMatrix.js b/packages/core/src/textures/TextureMatrix.js index 2d4ecea..8198ed1 100644 --- a/packages/core/src/textures/TextureMatrix.js +++ b/packages/core/src/textures/TextureMatrix.js @@ -3,12 +3,18 @@ const tempMat = new Matrix(); /** - * Class controls uv transform and frame clamp for texture - * Can be used in Texture "transform" field, or separately, you can use different clamp settings on the same texture. + * Class controls uv mapping from Texture normal space to BaseTexture normal space. + * Takes `trim` and `rotate` into account. + * May contain clamp settings for Meshes and TilingSprite. + * + * Can be used in Texture `uvMatrix` field, or separately, you can use different clamp settings on the same texture. * If you want to add support for texture region of certain feature or filter, that's what you're looking for. * + * Takes track of Texture changes through `_lastTextureID` private field. + * Use `update()` method call to track it from outside. + * * @see PIXI.Texture - * @see PIXI.mesh.Mesh + * @see PIXI.Mesh * @see PIXI.TilingSprite * @class * @memberof PIXI @@ -31,6 +37,10 @@ this.uClampOffset = new Float32Array(2); + /** + * @member {number} Tracks Texture frame changes + * @private + */ this._lastTextureID = -1; /** diff --git a/packages/mesh/src/Mesh.js b/packages/mesh/src/Mesh.js index 55dc092..e7c8f9e 100644 --- a/packages/mesh/src/Mesh.js +++ b/packages/mesh/src/Mesh.js @@ -1,5 +1,5 @@ import RawMesh from './RawMesh'; -import { Geometry, Program, Shader, Texture } from '@pixi/core'; +import { Geometry, Program, Shader, Texture, TextureMatrix } from '@pixi/core'; import { Matrix } from '@pixi/math'; import { BLEND_MODES } from '@pixi/constants'; import { hex2rgb, premultiplyRgba } from '@pixi/utils'; @@ -23,7 +23,7 @@ * @param {Uint16Array} [indices] - if you want to specify the indices * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts */ - constructor(texture, vertices, uvs, indices, drawMode) + constructor(texture = Texture.EMPTY, vertices, uvs, indices, drawMode) { const geometry = new Geometry(); @@ -40,7 +40,7 @@ const uniforms = { uSampler: texture, - uTransform: Matrix.IDENTITY, + uTextureMatrix: Matrix.IDENTITY, alpha: 1, uColor: new Float32Array([1, 1, 1, 1]), }; @@ -70,7 +70,7 @@ * @default PIXI.Texture.EMPTY * @private */ - this.texture = texture || Texture.EMPTY; + this.texture = texture; /** * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any @@ -85,6 +85,31 @@ this.tint = 0xFFFFFF; this.blendMode = BLEND_MODES.NORMAL; + + /** + * TextureMatrix instance for this Mesh, used to track Texture changes + * + * @member {PIXI.TextureMatrix} + * @readonly + */ + this.uvMatrix = new TextureMatrix(this._texture); + + /** + * whether or not upload uvMatrix to shader + * if its false, then uvs should be pre-multiplied + * if you change it for generated mesh, please call 'refresh(true)' + * @member {boolean} + * @default false + */ + this.uploadUvMatrix = false; + + /** + * Uploads vertices buffer every frame, like in PixiJS V3 and V4. + * + * @member {boolean} + * @default true + */ + this.autoUpdate = true; } /** @@ -152,7 +177,7 @@ if (value) { // wait for the texture to load - if (value.baseTexture.hasLoaded) + if (value.baseTexture.valid) { this._onTextureUpdate(); } @@ -177,6 +202,62 @@ */ _onTextureUpdate() { + // constructor texture update stop + if (!this.uvMatrix) + { + return; + } + this.uvMatrix.texture = this._texture; + this.refresh(); + } + + /** + * multiplies uvs only if uploadUvMatrix is false + * call it after you change uvs manually + * make sure that texture is valid + */ + multiplyUvs() + { + if (!this.uploadUvMatrix) + { + this.uvMatrix.multiplyUvs(this.uvs); + } + } + + /** + * Refreshes uvs for generated meshes (rope, plane) + * sometimes refreshes vertices too + * + * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case + */ + refresh(forceUpdate) + { + if (this.uvMatrix.update(forceUpdate)) + { + this._refresh(); + } + } + + /** + * Updates the object transform for rendering + * + * @private + */ + updateTransform() + { + if (this.autoUpdate) + { + this.geometry.buffers[0].update(); + } + this.containerUpdateTransform(); + } + + /** + * re-calculates mesh coords + * @private + */ + _refresh() + { /* empty */ } } diff --git a/packages/mesh/src/MeshRenderer.js b/packages/mesh/src/MeshRenderer.js index ce6dfe6..12b159b 100644 --- a/packages/mesh/src/MeshRenderer.js +++ b/packages/mesh/src/MeshRenderer.js @@ -1,4 +1,5 @@ import { ObjectRenderer } from '@pixi/core'; +import { Matrix } from '@pixi/math'; /** * WebGL renderer plugin for tiling sprites @@ -44,12 +45,23 @@ // TODO // set the shader props.. // probably only need to set once! - // as its then a refference.. + // as its then a reference.. if (mesh.shader.program.uniformData.translationMatrix) { // the transform! mesh.shader.uniforms.translationMatrix = mesh.transform.worldTransform.toArray(true); } + if (mesh.shader.uniforms.uTextureMatrix) + { + if (mesh.uploadUvMatrix) + { + mesh.shader.uniforms.uTextureMatrix = mesh.uvMatrix.mapCoord.toArray(true); + } + else + { + mesh.shader.uniforms.uTextureMatrix = Matrix.IDENTITY.toArray(true); + } + } // bind and sync uniforms.. this.renderer.shader.bind(mesh.shader); diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index 2a9eba9..09eaf9c 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -42,7 +42,7 @@ } const base = this._texture.baseTexture; - const textureSource = base.source; + const textureSource = base.getDrawableSource(); const w = base.width * base.resolution; const h = base.height * base.resolution; @@ -56,3 +56,56 @@ this.drawSegment(context, textureSource, w, h, 18, 19, 28, 29); this.drawSegment(context, textureSource, w, h, 20, 21, 30, 31); }; + +/** + * Renders one segment of the plane. + * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure + * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. + * + * @method drawSegment + * @memberof PIXI.NineSlicePlane# + * @param {CanvasRenderingContext2D} context - The context to draw with. + * @param {CanvasImageSource} textureSource - The source to draw. + * @param {number} w - width of the texture + * @param {number} h - height of the texture + * @param {number} x1 - x index 1 + * @param {number} y1 - y index 1 + * @param {number} x2 - x index 2 + * @param {number} y2 - y index 2 + */ +NineSlicePlane.prototype.drawSegment = function drawSegment(context, textureSource, w, h, x1, y1, x2, y2) +{ + // otherwise you get weird results when using slices of that are 0 wide or high. + const uvs = this.uvs; + const vertices = this.vertices; + + let sw = (uvs[x2] - uvs[x1]) * w; + let sh = (uvs[y2] - uvs[y1]) * h; + let dw = vertices[x2] - vertices[x1]; + let dh = vertices[y2] - vertices[y1]; + + // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. + if (sw < 1) + { + sw = 1; + } + + if (sh < 1) + { + sh = 1; + } + + // make sure destination is at least 1 pixel wide and high, otherwise you get + // lines when rendering close to original size. + if (dw < 1) + { + dw = 1; + } + + if (dh < 1) + { + dh = 1; + } + + context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); +}; diff --git a/packages/core/src/renderTexture/RenderTexture.js b/packages/core/src/renderTexture/RenderTexture.js index f1ecf1f..a64b7e7 100644 --- a/packages/core/src/renderTexture/RenderTexture.js +++ b/packages/core/src/renderTexture/RenderTexture.js @@ -94,7 +94,7 @@ */ this.filterFrame = null; - this._updateUvs(); + this.updateUvs(); } /** @@ -120,7 +120,7 @@ this.baseTexture.resize(width, height); } - this._updateUvs(); + this.updateUvs(); } /** diff --git a/packages/core/src/textures/RenderTexture.js b/packages/core/src/textures/RenderTexture.js deleted file mode 100644 index 34f09b2..0000000 --- a/packages/core/src/textures/RenderTexture.js +++ /dev/null @@ -1,153 +0,0 @@ -import BaseRenderTexture from './BaseRenderTexture'; -import Texture from './Texture'; - -/** - * A RenderTexture is a special texture that allows any PixiJS display object to be rendered to it. - * - * __Hint__: All DisplayObjects (i.e. Sprites) that render to a RenderTexture should be preloaded - * otherwise black rectangles will be drawn instead. - * - * A RenderTexture takes a snapshot of any Display Object given to its render method. For example: - * - * ```js - * let renderer = PIXI.autoDetectRenderer(1024, 1024, { view: canvas, ratio: 1 }); - * let renderTexture = PIXI.RenderTexture.create(800, 600); - * let sprite = PIXI.Sprite.fromImage("spinObj_01.png"); - * - * sprite.position.x = 800/2; - * sprite.position.y = 600/2; - * sprite.anchor.x = 0.5; - * sprite.anchor.y = 0.5; - * - * renderer.render(sprite, renderTexture); - * ``` - * - * The Sprite in this case will be rendered using its local transform. To render this sprite at 0,0 - * you can clear the transform - * - * ```js - * - * sprite.setTransform() - * - * let renderTexture = new PIXI.RenderTexture.create(100, 100); - * - * renderer.render(sprite, renderTexture); // Renders to center of RenderTexture - * ``` - * - * @class - * @extends PIXI.Texture - * @memberof PIXI - */ -export default class RenderTexture extends Texture -{ - /** - * @param {PIXI.BaseRenderTexture} baseRenderTexture - The renderer used for this RenderTexture - * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show - */ - constructor(baseRenderTexture, frame) - { - // support for legacy.. - let _legacyRenderer = null; - - if (!(baseRenderTexture instanceof BaseRenderTexture)) - { - /* eslint-disable prefer-rest-params, no-console */ - const width = arguments[1]; - const height = arguments[2]; - const scaleMode = arguments[3]; - const resolution = arguments[4]; - - // we have an old render texture.. - console.warn(`Please use RenderTexture.create(${width}, ${height}) instead of the ctor directly.`); - _legacyRenderer = arguments[0]; - /* eslint-enable prefer-rest-params, no-console */ - - frame = null; - baseRenderTexture = new BaseRenderTexture({ - width, - height, - scaleMode, - resolution, - }); - } - - /** - * The base texture object that this texture uses - * - * @member {BaseTexture} - */ - super(baseRenderTexture, frame); - - this.legacyRenderer = _legacyRenderer; - - /** - * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered. - * - * @member {boolean} - */ - this.valid = true; - - /** - * FilterSystem temporary storage - * @private - * @member {PIXI.Rectangle} - */ - this.filterFrame = null; - - this._updateUvs(); - } - - /** - * Resizes the RenderTexture. - * - * @param {number} width - The width to resize to. - * @param {number} height - The height to resize to. - * @param {boolean} [resizeBaseTexture=true] - Should the baseTexture.width and height values be resized as well? - */ - resize(width, height, resizeBaseTexture = true) - { - width = Math.ceil(width); - height = Math.ceil(height); - - // TODO - could be not required.. - this.valid = (width > 0 && height > 0); - - this._frame.width = this.orig.width = width; - this._frame.height = this.orig.height = height; - - if (resizeBaseTexture) - { - this.baseTexture.resize(width, height); - } - - this._updateUvs(); - } - - /** - * A short hand way of creating a render texture. - * - * @param {object} [options] - Options - * @param {number} [options.width=100] - The width of the render texture - * @param {number} [options.height=100] - The height of the render texture - * @param {number} [options.scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values - * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the texture being generated - * @return {PIXI.RenderTexture} The new render texture - */ - static create(options) - { - // fallback, old-style: create(width, height, scaleMode, resolution) - if (typeof options === 'number') - { - /* eslint-disable prefer-rest-params */ - options = { - width: options, - height: arguments[1], - scaleMode: arguments[2], - resolution: arguments[3], - }; - /* eslint-enable prefer-rest-params */ - } - - return new RenderTexture(new BaseRenderTexture(options)); - } -} diff --git a/packages/core/src/textures/Texture.js b/packages/core/src/textures/Texture.js index 5a22225..237dec3 100644 --- a/packages/core/src/textures/Texture.js +++ b/packages/core/src/textures/Texture.js @@ -81,7 +81,7 @@ /** * This is the trimmed area of original texture, before it was put in atlas - * Please call `_updateUvs()` after you change coordinates of `trim` manually. + * Please call `updateUvs()` after you change coordinates of `trim` manually. * * @member {PIXI.Rectangle} */ @@ -102,7 +102,7 @@ this.requiresUpdate = false; /** - * The WebGL UV data cache. + * The WebGL UV data cache. Can be used as quad UV * * @member {PIXI.TextureUvs} * @private @@ -110,6 +110,14 @@ this._uvs = null; /** + * Default TextureMatrix instance for this texture + * By default that object is not created because its heavy + * + * @member {PIXI.TextureMatrix} + */ + this.uvMatrix = null; + + /** * This is the area of original texture, before it was put in atlas * * @member {PIXI.Rectangle} @@ -145,24 +153,16 @@ } /** - * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. + * Update ID is observed by sprites and TextureMatrix instances. + * Call updateUvs() to increment it. * - * @event PIXI.Texture#update - * @protected - * @param {PIXI.Texture} texture - Instance of texture being updated. + * @member {number} + * @private */ this._updateID = 0; /** - * Contains data for uvs. May contain clamp settings and some matrices. - * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} - * @default null - */ - this.transform = null; - - /** * The ids under which this Texture has been added to the texture cache. This is * automatically set as long as Texture.addToCache is used, but may not be set if a * Texture is added directly to the TextureCache array. @@ -255,8 +255,9 @@ /** * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture. + * Call it after changing the frame */ - _updateUvs() + updateUvs() { if (!this._uvs) { @@ -446,7 +447,7 @@ /** * The frame specifies the region of the base texture that this texture uses. - * Please call `_updateUvs()` after you change coordinates of `frame` manually. + * Please call `updateUvs()` after you change coordinates of `frame` manually. * * @member {PIXI.Rectangle} */ @@ -484,7 +485,7 @@ if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } @@ -507,7 +508,7 @@ this._rotate = rotate; if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } diff --git a/packages/core/src/textures/TextureMatrix.js b/packages/core/src/textures/TextureMatrix.js index 2d4ecea..8198ed1 100644 --- a/packages/core/src/textures/TextureMatrix.js +++ b/packages/core/src/textures/TextureMatrix.js @@ -3,12 +3,18 @@ const tempMat = new Matrix(); /** - * Class controls uv transform and frame clamp for texture - * Can be used in Texture "transform" field, or separately, you can use different clamp settings on the same texture. + * Class controls uv mapping from Texture normal space to BaseTexture normal space. + * Takes `trim` and `rotate` into account. + * May contain clamp settings for Meshes and TilingSprite. + * + * Can be used in Texture `uvMatrix` field, or separately, you can use different clamp settings on the same texture. * If you want to add support for texture region of certain feature or filter, that's what you're looking for. * + * Takes track of Texture changes through `_lastTextureID` private field. + * Use `update()` method call to track it from outside. + * * @see PIXI.Texture - * @see PIXI.mesh.Mesh + * @see PIXI.Mesh * @see PIXI.TilingSprite * @class * @memberof PIXI @@ -31,6 +37,10 @@ this.uClampOffset = new Float32Array(2); + /** + * @member {number} Tracks Texture frame changes + * @private + */ this._lastTextureID = -1; /** diff --git a/packages/mesh/src/Mesh.js b/packages/mesh/src/Mesh.js index 55dc092..e7c8f9e 100644 --- a/packages/mesh/src/Mesh.js +++ b/packages/mesh/src/Mesh.js @@ -1,5 +1,5 @@ import RawMesh from './RawMesh'; -import { Geometry, Program, Shader, Texture } from '@pixi/core'; +import { Geometry, Program, Shader, Texture, TextureMatrix } from '@pixi/core'; import { Matrix } from '@pixi/math'; import { BLEND_MODES } from '@pixi/constants'; import { hex2rgb, premultiplyRgba } from '@pixi/utils'; @@ -23,7 +23,7 @@ * @param {Uint16Array} [indices] - if you want to specify the indices * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts */ - constructor(texture, vertices, uvs, indices, drawMode) + constructor(texture = Texture.EMPTY, vertices, uvs, indices, drawMode) { const geometry = new Geometry(); @@ -40,7 +40,7 @@ const uniforms = { uSampler: texture, - uTransform: Matrix.IDENTITY, + uTextureMatrix: Matrix.IDENTITY, alpha: 1, uColor: new Float32Array([1, 1, 1, 1]), }; @@ -70,7 +70,7 @@ * @default PIXI.Texture.EMPTY * @private */ - this.texture = texture || Texture.EMPTY; + this.texture = texture; /** * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any @@ -85,6 +85,31 @@ this.tint = 0xFFFFFF; this.blendMode = BLEND_MODES.NORMAL; + + /** + * TextureMatrix instance for this Mesh, used to track Texture changes + * + * @member {PIXI.TextureMatrix} + * @readonly + */ + this.uvMatrix = new TextureMatrix(this._texture); + + /** + * whether or not upload uvMatrix to shader + * if its false, then uvs should be pre-multiplied + * if you change it for generated mesh, please call 'refresh(true)' + * @member {boolean} + * @default false + */ + this.uploadUvMatrix = false; + + /** + * Uploads vertices buffer every frame, like in PixiJS V3 and V4. + * + * @member {boolean} + * @default true + */ + this.autoUpdate = true; } /** @@ -152,7 +177,7 @@ if (value) { // wait for the texture to load - if (value.baseTexture.hasLoaded) + if (value.baseTexture.valid) { this._onTextureUpdate(); } @@ -177,6 +202,62 @@ */ _onTextureUpdate() { + // constructor texture update stop + if (!this.uvMatrix) + { + return; + } + this.uvMatrix.texture = this._texture; + this.refresh(); + } + + /** + * multiplies uvs only if uploadUvMatrix is false + * call it after you change uvs manually + * make sure that texture is valid + */ + multiplyUvs() + { + if (!this.uploadUvMatrix) + { + this.uvMatrix.multiplyUvs(this.uvs); + } + } + + /** + * Refreshes uvs for generated meshes (rope, plane) + * sometimes refreshes vertices too + * + * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case + */ + refresh(forceUpdate) + { + if (this.uvMatrix.update(forceUpdate)) + { + this._refresh(); + } + } + + /** + * Updates the object transform for rendering + * + * @private + */ + updateTransform() + { + if (this.autoUpdate) + { + this.geometry.buffers[0].update(); + } + this.containerUpdateTransform(); + } + + /** + * re-calculates mesh coords + * @private + */ + _refresh() + { /* empty */ } } diff --git a/packages/mesh/src/MeshRenderer.js b/packages/mesh/src/MeshRenderer.js index ce6dfe6..12b159b 100644 --- a/packages/mesh/src/MeshRenderer.js +++ b/packages/mesh/src/MeshRenderer.js @@ -1,4 +1,5 @@ import { ObjectRenderer } from '@pixi/core'; +import { Matrix } from '@pixi/math'; /** * WebGL renderer plugin for tiling sprites @@ -44,12 +45,23 @@ // TODO // set the shader props.. // probably only need to set once! - // as its then a refference.. + // as its then a reference.. if (mesh.shader.program.uniformData.translationMatrix) { // the transform! mesh.shader.uniforms.translationMatrix = mesh.transform.worldTransform.toArray(true); } + if (mesh.shader.uniforms.uTextureMatrix) + { + if (mesh.uploadUvMatrix) + { + mesh.shader.uniforms.uTextureMatrix = mesh.uvMatrix.mapCoord.toArray(true); + } + else + { + mesh.shader.uniforms.uTextureMatrix = Matrix.IDENTITY.toArray(true); + } + } // bind and sync uniforms.. this.renderer.shader.bind(mesh.shader); diff --git a/packages/mesh/src/NineSlicePlane.js b/packages/mesh/src/NineSlicePlane.js index 44e3498..a392985 100644 --- a/packages/mesh/src/NineSlicePlane.js +++ b/packages/mesh/src/NineSlicePlane.js @@ -133,58 +133,6 @@ } /** - * Renders one segment of the plane. - * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure - * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. - * - * @private - * @param {CanvasRenderingContext2D} context - The context to draw with. - * @param {CanvasImageSource} textureSource - The source to draw. - * @param {number} w - width of the texture - * @param {number} h - height of the texture - * @param {number} x1 - x index 1 - * @param {number} y1 - y index 1 - * @param {number} x2 - x index 2 - * @param {number} y2 - y index 2 - */ - drawSegment(context, textureSource, w, h, x1, y1, x2, y2) - { - // otherwise you get weird results when using slices of that are 0 wide or high. - const uvs = this.uvs; - const vertices = this.vertices; - - let sw = (uvs[x2] - uvs[x1]) * w; - let sh = (uvs[y2] - uvs[y1]) * h; - let dw = vertices[x2] - vertices[x1]; - let dh = vertices[y2] - vertices[y1]; - - // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. - if (sw < 1) - { - sw = 1; - } - - if (sh < 1) - { - sh = 1; - } - - // make sure destination is at least 1 pixel wide and high, otherwise you get - // lines when rendering close to original size. - if (dw < 1) - { - dw = 1; - } - - if (dh < 1) - { - dh = 1; - } - - context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); - } - - /** * The width of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane * * @member {number} @@ -309,7 +257,7 @@ this.updateHorizontalVertices(); this.updateVerticalVertices(); - this.dirty++; + this.geometry.buffers[1].update(); this.multiplyUvs(); } diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index 2a9eba9..09eaf9c 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -42,7 +42,7 @@ } const base = this._texture.baseTexture; - const textureSource = base.source; + const textureSource = base.getDrawableSource(); const w = base.width * base.resolution; const h = base.height * base.resolution; @@ -56,3 +56,56 @@ this.drawSegment(context, textureSource, w, h, 18, 19, 28, 29); this.drawSegment(context, textureSource, w, h, 20, 21, 30, 31); }; + +/** + * Renders one segment of the plane. + * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure + * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. + * + * @method drawSegment + * @memberof PIXI.NineSlicePlane# + * @param {CanvasRenderingContext2D} context - The context to draw with. + * @param {CanvasImageSource} textureSource - The source to draw. + * @param {number} w - width of the texture + * @param {number} h - height of the texture + * @param {number} x1 - x index 1 + * @param {number} y1 - y index 1 + * @param {number} x2 - x index 2 + * @param {number} y2 - y index 2 + */ +NineSlicePlane.prototype.drawSegment = function drawSegment(context, textureSource, w, h, x1, y1, x2, y2) +{ + // otherwise you get weird results when using slices of that are 0 wide or high. + const uvs = this.uvs; + const vertices = this.vertices; + + let sw = (uvs[x2] - uvs[x1]) * w; + let sh = (uvs[y2] - uvs[y1]) * h; + let dw = vertices[x2] - vertices[x1]; + let dh = vertices[y2] - vertices[y1]; + + // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. + if (sw < 1) + { + sw = 1; + } + + if (sh < 1) + { + sh = 1; + } + + // make sure destination is at least 1 pixel wide and high, otherwise you get + // lines when rendering close to original size. + if (dw < 1) + { + dw = 1; + } + + if (dh < 1) + { + dh = 1; + } + + context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); +}; diff --git a/packages/core/src/renderTexture/RenderTexture.js b/packages/core/src/renderTexture/RenderTexture.js index f1ecf1f..a64b7e7 100644 --- a/packages/core/src/renderTexture/RenderTexture.js +++ b/packages/core/src/renderTexture/RenderTexture.js @@ -94,7 +94,7 @@ */ this.filterFrame = null; - this._updateUvs(); + this.updateUvs(); } /** @@ -120,7 +120,7 @@ this.baseTexture.resize(width, height); } - this._updateUvs(); + this.updateUvs(); } /** diff --git a/packages/core/src/textures/RenderTexture.js b/packages/core/src/textures/RenderTexture.js deleted file mode 100644 index 34f09b2..0000000 --- a/packages/core/src/textures/RenderTexture.js +++ /dev/null @@ -1,153 +0,0 @@ -import BaseRenderTexture from './BaseRenderTexture'; -import Texture from './Texture'; - -/** - * A RenderTexture is a special texture that allows any PixiJS display object to be rendered to it. - * - * __Hint__: All DisplayObjects (i.e. Sprites) that render to a RenderTexture should be preloaded - * otherwise black rectangles will be drawn instead. - * - * A RenderTexture takes a snapshot of any Display Object given to its render method. For example: - * - * ```js - * let renderer = PIXI.autoDetectRenderer(1024, 1024, { view: canvas, ratio: 1 }); - * let renderTexture = PIXI.RenderTexture.create(800, 600); - * let sprite = PIXI.Sprite.fromImage("spinObj_01.png"); - * - * sprite.position.x = 800/2; - * sprite.position.y = 600/2; - * sprite.anchor.x = 0.5; - * sprite.anchor.y = 0.5; - * - * renderer.render(sprite, renderTexture); - * ``` - * - * The Sprite in this case will be rendered using its local transform. To render this sprite at 0,0 - * you can clear the transform - * - * ```js - * - * sprite.setTransform() - * - * let renderTexture = new PIXI.RenderTexture.create(100, 100); - * - * renderer.render(sprite, renderTexture); // Renders to center of RenderTexture - * ``` - * - * @class - * @extends PIXI.Texture - * @memberof PIXI - */ -export default class RenderTexture extends Texture -{ - /** - * @param {PIXI.BaseRenderTexture} baseRenderTexture - The renderer used for this RenderTexture - * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show - */ - constructor(baseRenderTexture, frame) - { - // support for legacy.. - let _legacyRenderer = null; - - if (!(baseRenderTexture instanceof BaseRenderTexture)) - { - /* eslint-disable prefer-rest-params, no-console */ - const width = arguments[1]; - const height = arguments[2]; - const scaleMode = arguments[3]; - const resolution = arguments[4]; - - // we have an old render texture.. - console.warn(`Please use RenderTexture.create(${width}, ${height}) instead of the ctor directly.`); - _legacyRenderer = arguments[0]; - /* eslint-enable prefer-rest-params, no-console */ - - frame = null; - baseRenderTexture = new BaseRenderTexture({ - width, - height, - scaleMode, - resolution, - }); - } - - /** - * The base texture object that this texture uses - * - * @member {BaseTexture} - */ - super(baseRenderTexture, frame); - - this.legacyRenderer = _legacyRenderer; - - /** - * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered. - * - * @member {boolean} - */ - this.valid = true; - - /** - * FilterSystem temporary storage - * @private - * @member {PIXI.Rectangle} - */ - this.filterFrame = null; - - this._updateUvs(); - } - - /** - * Resizes the RenderTexture. - * - * @param {number} width - The width to resize to. - * @param {number} height - The height to resize to. - * @param {boolean} [resizeBaseTexture=true] - Should the baseTexture.width and height values be resized as well? - */ - resize(width, height, resizeBaseTexture = true) - { - width = Math.ceil(width); - height = Math.ceil(height); - - // TODO - could be not required.. - this.valid = (width > 0 && height > 0); - - this._frame.width = this.orig.width = width; - this._frame.height = this.orig.height = height; - - if (resizeBaseTexture) - { - this.baseTexture.resize(width, height); - } - - this._updateUvs(); - } - - /** - * A short hand way of creating a render texture. - * - * @param {object} [options] - Options - * @param {number} [options.width=100] - The width of the render texture - * @param {number} [options.height=100] - The height of the render texture - * @param {number} [options.scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values - * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the texture being generated - * @return {PIXI.RenderTexture} The new render texture - */ - static create(options) - { - // fallback, old-style: create(width, height, scaleMode, resolution) - if (typeof options === 'number') - { - /* eslint-disable prefer-rest-params */ - options = { - width: options, - height: arguments[1], - scaleMode: arguments[2], - resolution: arguments[3], - }; - /* eslint-enable prefer-rest-params */ - } - - return new RenderTexture(new BaseRenderTexture(options)); - } -} diff --git a/packages/core/src/textures/Texture.js b/packages/core/src/textures/Texture.js index 5a22225..237dec3 100644 --- a/packages/core/src/textures/Texture.js +++ b/packages/core/src/textures/Texture.js @@ -81,7 +81,7 @@ /** * This is the trimmed area of original texture, before it was put in atlas - * Please call `_updateUvs()` after you change coordinates of `trim` manually. + * Please call `updateUvs()` after you change coordinates of `trim` manually. * * @member {PIXI.Rectangle} */ @@ -102,7 +102,7 @@ this.requiresUpdate = false; /** - * The WebGL UV data cache. + * The WebGL UV data cache. Can be used as quad UV * * @member {PIXI.TextureUvs} * @private @@ -110,6 +110,14 @@ this._uvs = null; /** + * Default TextureMatrix instance for this texture + * By default that object is not created because its heavy + * + * @member {PIXI.TextureMatrix} + */ + this.uvMatrix = null; + + /** * This is the area of original texture, before it was put in atlas * * @member {PIXI.Rectangle} @@ -145,24 +153,16 @@ } /** - * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. + * Update ID is observed by sprites and TextureMatrix instances. + * Call updateUvs() to increment it. * - * @event PIXI.Texture#update - * @protected - * @param {PIXI.Texture} texture - Instance of texture being updated. + * @member {number} + * @private */ this._updateID = 0; /** - * Contains data for uvs. May contain clamp settings and some matrices. - * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} - * @default null - */ - this.transform = null; - - /** * The ids under which this Texture has been added to the texture cache. This is * automatically set as long as Texture.addToCache is used, but may not be set if a * Texture is added directly to the TextureCache array. @@ -255,8 +255,9 @@ /** * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture. + * Call it after changing the frame */ - _updateUvs() + updateUvs() { if (!this._uvs) { @@ -446,7 +447,7 @@ /** * The frame specifies the region of the base texture that this texture uses. - * Please call `_updateUvs()` after you change coordinates of `frame` manually. + * Please call `updateUvs()` after you change coordinates of `frame` manually. * * @member {PIXI.Rectangle} */ @@ -484,7 +485,7 @@ if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } @@ -507,7 +508,7 @@ this._rotate = rotate; if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } diff --git a/packages/core/src/textures/TextureMatrix.js b/packages/core/src/textures/TextureMatrix.js index 2d4ecea..8198ed1 100644 --- a/packages/core/src/textures/TextureMatrix.js +++ b/packages/core/src/textures/TextureMatrix.js @@ -3,12 +3,18 @@ const tempMat = new Matrix(); /** - * Class controls uv transform and frame clamp for texture - * Can be used in Texture "transform" field, or separately, you can use different clamp settings on the same texture. + * Class controls uv mapping from Texture normal space to BaseTexture normal space. + * Takes `trim` and `rotate` into account. + * May contain clamp settings for Meshes and TilingSprite. + * + * Can be used in Texture `uvMatrix` field, or separately, you can use different clamp settings on the same texture. * If you want to add support for texture region of certain feature or filter, that's what you're looking for. * + * Takes track of Texture changes through `_lastTextureID` private field. + * Use `update()` method call to track it from outside. + * * @see PIXI.Texture - * @see PIXI.mesh.Mesh + * @see PIXI.Mesh * @see PIXI.TilingSprite * @class * @memberof PIXI @@ -31,6 +37,10 @@ this.uClampOffset = new Float32Array(2); + /** + * @member {number} Tracks Texture frame changes + * @private + */ this._lastTextureID = -1; /** diff --git a/packages/mesh/src/Mesh.js b/packages/mesh/src/Mesh.js index 55dc092..e7c8f9e 100644 --- a/packages/mesh/src/Mesh.js +++ b/packages/mesh/src/Mesh.js @@ -1,5 +1,5 @@ import RawMesh from './RawMesh'; -import { Geometry, Program, Shader, Texture } from '@pixi/core'; +import { Geometry, Program, Shader, Texture, TextureMatrix } from '@pixi/core'; import { Matrix } from '@pixi/math'; import { BLEND_MODES } from '@pixi/constants'; import { hex2rgb, premultiplyRgba } from '@pixi/utils'; @@ -23,7 +23,7 @@ * @param {Uint16Array} [indices] - if you want to specify the indices * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts */ - constructor(texture, vertices, uvs, indices, drawMode) + constructor(texture = Texture.EMPTY, vertices, uvs, indices, drawMode) { const geometry = new Geometry(); @@ -40,7 +40,7 @@ const uniforms = { uSampler: texture, - uTransform: Matrix.IDENTITY, + uTextureMatrix: Matrix.IDENTITY, alpha: 1, uColor: new Float32Array([1, 1, 1, 1]), }; @@ -70,7 +70,7 @@ * @default PIXI.Texture.EMPTY * @private */ - this.texture = texture || Texture.EMPTY; + this.texture = texture; /** * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any @@ -85,6 +85,31 @@ this.tint = 0xFFFFFF; this.blendMode = BLEND_MODES.NORMAL; + + /** + * TextureMatrix instance for this Mesh, used to track Texture changes + * + * @member {PIXI.TextureMatrix} + * @readonly + */ + this.uvMatrix = new TextureMatrix(this._texture); + + /** + * whether or not upload uvMatrix to shader + * if its false, then uvs should be pre-multiplied + * if you change it for generated mesh, please call 'refresh(true)' + * @member {boolean} + * @default false + */ + this.uploadUvMatrix = false; + + /** + * Uploads vertices buffer every frame, like in PixiJS V3 and V4. + * + * @member {boolean} + * @default true + */ + this.autoUpdate = true; } /** @@ -152,7 +177,7 @@ if (value) { // wait for the texture to load - if (value.baseTexture.hasLoaded) + if (value.baseTexture.valid) { this._onTextureUpdate(); } @@ -177,6 +202,62 @@ */ _onTextureUpdate() { + // constructor texture update stop + if (!this.uvMatrix) + { + return; + } + this.uvMatrix.texture = this._texture; + this.refresh(); + } + + /** + * multiplies uvs only if uploadUvMatrix is false + * call it after you change uvs manually + * make sure that texture is valid + */ + multiplyUvs() + { + if (!this.uploadUvMatrix) + { + this.uvMatrix.multiplyUvs(this.uvs); + } + } + + /** + * Refreshes uvs for generated meshes (rope, plane) + * sometimes refreshes vertices too + * + * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case + */ + refresh(forceUpdate) + { + if (this.uvMatrix.update(forceUpdate)) + { + this._refresh(); + } + } + + /** + * Updates the object transform for rendering + * + * @private + */ + updateTransform() + { + if (this.autoUpdate) + { + this.geometry.buffers[0].update(); + } + this.containerUpdateTransform(); + } + + /** + * re-calculates mesh coords + * @private + */ + _refresh() + { /* empty */ } } diff --git a/packages/mesh/src/MeshRenderer.js b/packages/mesh/src/MeshRenderer.js index ce6dfe6..12b159b 100644 --- a/packages/mesh/src/MeshRenderer.js +++ b/packages/mesh/src/MeshRenderer.js @@ -1,4 +1,5 @@ import { ObjectRenderer } from '@pixi/core'; +import { Matrix } from '@pixi/math'; /** * WebGL renderer plugin for tiling sprites @@ -44,12 +45,23 @@ // TODO // set the shader props.. // probably only need to set once! - // as its then a refference.. + // as its then a reference.. if (mesh.shader.program.uniformData.translationMatrix) { // the transform! mesh.shader.uniforms.translationMatrix = mesh.transform.worldTransform.toArray(true); } + if (mesh.shader.uniforms.uTextureMatrix) + { + if (mesh.uploadUvMatrix) + { + mesh.shader.uniforms.uTextureMatrix = mesh.uvMatrix.mapCoord.toArray(true); + } + else + { + mesh.shader.uniforms.uTextureMatrix = Matrix.IDENTITY.toArray(true); + } + } // bind and sync uniforms.. this.renderer.shader.bind(mesh.shader); diff --git a/packages/mesh/src/NineSlicePlane.js b/packages/mesh/src/NineSlicePlane.js index 44e3498..a392985 100644 --- a/packages/mesh/src/NineSlicePlane.js +++ b/packages/mesh/src/NineSlicePlane.js @@ -133,58 +133,6 @@ } /** - * Renders one segment of the plane. - * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure - * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. - * - * @private - * @param {CanvasRenderingContext2D} context - The context to draw with. - * @param {CanvasImageSource} textureSource - The source to draw. - * @param {number} w - width of the texture - * @param {number} h - height of the texture - * @param {number} x1 - x index 1 - * @param {number} y1 - y index 1 - * @param {number} x2 - x index 2 - * @param {number} y2 - y index 2 - */ - drawSegment(context, textureSource, w, h, x1, y1, x2, y2) - { - // otherwise you get weird results when using slices of that are 0 wide or high. - const uvs = this.uvs; - const vertices = this.vertices; - - let sw = (uvs[x2] - uvs[x1]) * w; - let sh = (uvs[y2] - uvs[y1]) * h; - let dw = vertices[x2] - vertices[x1]; - let dh = vertices[y2] - vertices[y1]; - - // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. - if (sw < 1) - { - sw = 1; - } - - if (sh < 1) - { - sh = 1; - } - - // make sure destination is at least 1 pixel wide and high, otherwise you get - // lines when rendering close to original size. - if (dw < 1) - { - dw = 1; - } - - if (dh < 1) - { - dh = 1; - } - - context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); - } - - /** * The width of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane * * @member {number} @@ -309,7 +257,7 @@ this.updateHorizontalVertices(); this.updateVerticalVertices(); - this.dirty++; + this.geometry.buffers[1].update(); this.multiplyUvs(); } diff --git a/packages/mesh/src/Plane.js b/packages/mesh/src/Plane.js index b874a39..a50b2d7 100644 --- a/packages/mesh/src/Plane.js +++ b/packages/mesh/src/Plane.js @@ -21,61 +21,51 @@ * @param {PIXI.Texture} texture - The texture to use on the Plane. * @param {number} verticesX - The number of vertices in the x-axis * @param {number} verticesY - The number of vertices in the y-axis - * @param {object} opts - an options object - add meshWidth and meshHeight + * @param {object} [opts] - an options object - add meshWidth and meshHeight, temporary object for PixiJS v5 alpha */ constructor(texture, verticesX, verticesY, opts = {}) { super(texture, new Float32Array(1), new Float32Array(1), new Uint16Array(1), 4); - this.segmentsX = this.verticesX = verticesX || 10; - this.segmentsY = this.verticesY = verticesY || 10; + this.verticesX = verticesX || 10; + this.verticesY = verticesY || 10; - this.meshWidth = opts.meshWidth || texture.width; - this.meshHeight = opts.meshHeight || texture.height; + this.meshWidth = opts.meshWidth || 0; + this.meshHeight = opts.meshHeight || 0; this.refresh(); } /** - * Refreshes - * + * Refreshes plane coordinates + * @private */ - refresh() + _refresh() { + const texture = this._texture; const total = this.verticesX * this.verticesY; const verts = []; const uvs = []; const indices = []; - const texture = this.texture; const segmentsX = this.verticesX - 1; const segmentsY = this.verticesY - 1; - const sizeX = this.meshWidth / segmentsX; - const sizeY = this.meshHeight / segmentsY; + const sizeX = (this.meshWidth || texture.width) / segmentsX; + const sizeY = (this.meshHeight || texture.height) / segmentsY; for (let i = 0; i < total; i++) { - if (texture._uvs) - { - const x = (i % this.verticesX); - const y = ((i / this.verticesX) | 0); + const x = (i % this.verticesX); + const y = ((i / this.verticesX) | 0); - verts.push((x * sizeX), - (y * sizeY)); + verts.push(x * sizeX, y * sizeY); - // this works for rectangular textures. - uvs.push( - texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))), - texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1))) - ); - } - else - { - uvs.push(0); - } + uvs.push(x / segmentsX, y / segmentsY); } + // cons + const totalSub = segmentsX * segmentsY; for (let i = 0; i < totalSub; i++) @@ -92,9 +82,6 @@ indices.push(value2, value4, value3); } - this.shader.uniforms.alpha = 1; - this.shader.uniforms.uSampler2 = this.texture; - this.vertices = new Float32Array(verts); this.uvs = new Float32Array(uvs); this.indices = new Uint16Array(indices); @@ -107,16 +94,7 @@ this.geometry.buffers[0].update(); this.geometry.buffers[1].update(); this.geometry.indexBuffer.update(); - } - /** - * Updates the object transform for rendering - * - * @private - */ - updateTransform() - { - this.geometry.buffers[0].update(); - this.containerUpdateTransform(); + this.multiplyUvs(); } } diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index 2a9eba9..09eaf9c 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -42,7 +42,7 @@ } const base = this._texture.baseTexture; - const textureSource = base.source; + const textureSource = base.getDrawableSource(); const w = base.width * base.resolution; const h = base.height * base.resolution; @@ -56,3 +56,56 @@ this.drawSegment(context, textureSource, w, h, 18, 19, 28, 29); this.drawSegment(context, textureSource, w, h, 20, 21, 30, 31); }; + +/** + * Renders one segment of the plane. + * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure + * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. + * + * @method drawSegment + * @memberof PIXI.NineSlicePlane# + * @param {CanvasRenderingContext2D} context - The context to draw with. + * @param {CanvasImageSource} textureSource - The source to draw. + * @param {number} w - width of the texture + * @param {number} h - height of the texture + * @param {number} x1 - x index 1 + * @param {number} y1 - y index 1 + * @param {number} x2 - x index 2 + * @param {number} y2 - y index 2 + */ +NineSlicePlane.prototype.drawSegment = function drawSegment(context, textureSource, w, h, x1, y1, x2, y2) +{ + // otherwise you get weird results when using slices of that are 0 wide or high. + const uvs = this.uvs; + const vertices = this.vertices; + + let sw = (uvs[x2] - uvs[x1]) * w; + let sh = (uvs[y2] - uvs[y1]) * h; + let dw = vertices[x2] - vertices[x1]; + let dh = vertices[y2] - vertices[y1]; + + // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. + if (sw < 1) + { + sw = 1; + } + + if (sh < 1) + { + sh = 1; + } + + // make sure destination is at least 1 pixel wide and high, otherwise you get + // lines when rendering close to original size. + if (dw < 1) + { + dw = 1; + } + + if (dh < 1) + { + dh = 1; + } + + context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); +}; diff --git a/packages/core/src/renderTexture/RenderTexture.js b/packages/core/src/renderTexture/RenderTexture.js index f1ecf1f..a64b7e7 100644 --- a/packages/core/src/renderTexture/RenderTexture.js +++ b/packages/core/src/renderTexture/RenderTexture.js @@ -94,7 +94,7 @@ */ this.filterFrame = null; - this._updateUvs(); + this.updateUvs(); } /** @@ -120,7 +120,7 @@ this.baseTexture.resize(width, height); } - this._updateUvs(); + this.updateUvs(); } /** diff --git a/packages/core/src/textures/RenderTexture.js b/packages/core/src/textures/RenderTexture.js deleted file mode 100644 index 34f09b2..0000000 --- a/packages/core/src/textures/RenderTexture.js +++ /dev/null @@ -1,153 +0,0 @@ -import BaseRenderTexture from './BaseRenderTexture'; -import Texture from './Texture'; - -/** - * A RenderTexture is a special texture that allows any PixiJS display object to be rendered to it. - * - * __Hint__: All DisplayObjects (i.e. Sprites) that render to a RenderTexture should be preloaded - * otherwise black rectangles will be drawn instead. - * - * A RenderTexture takes a snapshot of any Display Object given to its render method. For example: - * - * ```js - * let renderer = PIXI.autoDetectRenderer(1024, 1024, { view: canvas, ratio: 1 }); - * let renderTexture = PIXI.RenderTexture.create(800, 600); - * let sprite = PIXI.Sprite.fromImage("spinObj_01.png"); - * - * sprite.position.x = 800/2; - * sprite.position.y = 600/2; - * sprite.anchor.x = 0.5; - * sprite.anchor.y = 0.5; - * - * renderer.render(sprite, renderTexture); - * ``` - * - * The Sprite in this case will be rendered using its local transform. To render this sprite at 0,0 - * you can clear the transform - * - * ```js - * - * sprite.setTransform() - * - * let renderTexture = new PIXI.RenderTexture.create(100, 100); - * - * renderer.render(sprite, renderTexture); // Renders to center of RenderTexture - * ``` - * - * @class - * @extends PIXI.Texture - * @memberof PIXI - */ -export default class RenderTexture extends Texture -{ - /** - * @param {PIXI.BaseRenderTexture} baseRenderTexture - The renderer used for this RenderTexture - * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show - */ - constructor(baseRenderTexture, frame) - { - // support for legacy.. - let _legacyRenderer = null; - - if (!(baseRenderTexture instanceof BaseRenderTexture)) - { - /* eslint-disable prefer-rest-params, no-console */ - const width = arguments[1]; - const height = arguments[2]; - const scaleMode = arguments[3]; - const resolution = arguments[4]; - - // we have an old render texture.. - console.warn(`Please use RenderTexture.create(${width}, ${height}) instead of the ctor directly.`); - _legacyRenderer = arguments[0]; - /* eslint-enable prefer-rest-params, no-console */ - - frame = null; - baseRenderTexture = new BaseRenderTexture({ - width, - height, - scaleMode, - resolution, - }); - } - - /** - * The base texture object that this texture uses - * - * @member {BaseTexture} - */ - super(baseRenderTexture, frame); - - this.legacyRenderer = _legacyRenderer; - - /** - * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered. - * - * @member {boolean} - */ - this.valid = true; - - /** - * FilterSystem temporary storage - * @private - * @member {PIXI.Rectangle} - */ - this.filterFrame = null; - - this._updateUvs(); - } - - /** - * Resizes the RenderTexture. - * - * @param {number} width - The width to resize to. - * @param {number} height - The height to resize to. - * @param {boolean} [resizeBaseTexture=true] - Should the baseTexture.width and height values be resized as well? - */ - resize(width, height, resizeBaseTexture = true) - { - width = Math.ceil(width); - height = Math.ceil(height); - - // TODO - could be not required.. - this.valid = (width > 0 && height > 0); - - this._frame.width = this.orig.width = width; - this._frame.height = this.orig.height = height; - - if (resizeBaseTexture) - { - this.baseTexture.resize(width, height); - } - - this._updateUvs(); - } - - /** - * A short hand way of creating a render texture. - * - * @param {object} [options] - Options - * @param {number} [options.width=100] - The width of the render texture - * @param {number} [options.height=100] - The height of the render texture - * @param {number} [options.scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values - * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the texture being generated - * @return {PIXI.RenderTexture} The new render texture - */ - static create(options) - { - // fallback, old-style: create(width, height, scaleMode, resolution) - if (typeof options === 'number') - { - /* eslint-disable prefer-rest-params */ - options = { - width: options, - height: arguments[1], - scaleMode: arguments[2], - resolution: arguments[3], - }; - /* eslint-enable prefer-rest-params */ - } - - return new RenderTexture(new BaseRenderTexture(options)); - } -} diff --git a/packages/core/src/textures/Texture.js b/packages/core/src/textures/Texture.js index 5a22225..237dec3 100644 --- a/packages/core/src/textures/Texture.js +++ b/packages/core/src/textures/Texture.js @@ -81,7 +81,7 @@ /** * This is the trimmed area of original texture, before it was put in atlas - * Please call `_updateUvs()` after you change coordinates of `trim` manually. + * Please call `updateUvs()` after you change coordinates of `trim` manually. * * @member {PIXI.Rectangle} */ @@ -102,7 +102,7 @@ this.requiresUpdate = false; /** - * The WebGL UV data cache. + * The WebGL UV data cache. Can be used as quad UV * * @member {PIXI.TextureUvs} * @private @@ -110,6 +110,14 @@ this._uvs = null; /** + * Default TextureMatrix instance for this texture + * By default that object is not created because its heavy + * + * @member {PIXI.TextureMatrix} + */ + this.uvMatrix = null; + + /** * This is the area of original texture, before it was put in atlas * * @member {PIXI.Rectangle} @@ -145,24 +153,16 @@ } /** - * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. + * Update ID is observed by sprites and TextureMatrix instances. + * Call updateUvs() to increment it. * - * @event PIXI.Texture#update - * @protected - * @param {PIXI.Texture} texture - Instance of texture being updated. + * @member {number} + * @private */ this._updateID = 0; /** - * Contains data for uvs. May contain clamp settings and some matrices. - * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} - * @default null - */ - this.transform = null; - - /** * The ids under which this Texture has been added to the texture cache. This is * automatically set as long as Texture.addToCache is used, but may not be set if a * Texture is added directly to the TextureCache array. @@ -255,8 +255,9 @@ /** * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture. + * Call it after changing the frame */ - _updateUvs() + updateUvs() { if (!this._uvs) { @@ -446,7 +447,7 @@ /** * The frame specifies the region of the base texture that this texture uses. - * Please call `_updateUvs()` after you change coordinates of `frame` manually. + * Please call `updateUvs()` after you change coordinates of `frame` manually. * * @member {PIXI.Rectangle} */ @@ -484,7 +485,7 @@ if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } @@ -507,7 +508,7 @@ this._rotate = rotate; if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } diff --git a/packages/core/src/textures/TextureMatrix.js b/packages/core/src/textures/TextureMatrix.js index 2d4ecea..8198ed1 100644 --- a/packages/core/src/textures/TextureMatrix.js +++ b/packages/core/src/textures/TextureMatrix.js @@ -3,12 +3,18 @@ const tempMat = new Matrix(); /** - * Class controls uv transform and frame clamp for texture - * Can be used in Texture "transform" field, or separately, you can use different clamp settings on the same texture. + * Class controls uv mapping from Texture normal space to BaseTexture normal space. + * Takes `trim` and `rotate` into account. + * May contain clamp settings for Meshes and TilingSprite. + * + * Can be used in Texture `uvMatrix` field, or separately, you can use different clamp settings on the same texture. * If you want to add support for texture region of certain feature or filter, that's what you're looking for. * + * Takes track of Texture changes through `_lastTextureID` private field. + * Use `update()` method call to track it from outside. + * * @see PIXI.Texture - * @see PIXI.mesh.Mesh + * @see PIXI.Mesh * @see PIXI.TilingSprite * @class * @memberof PIXI @@ -31,6 +37,10 @@ this.uClampOffset = new Float32Array(2); + /** + * @member {number} Tracks Texture frame changes + * @private + */ this._lastTextureID = -1; /** diff --git a/packages/mesh/src/Mesh.js b/packages/mesh/src/Mesh.js index 55dc092..e7c8f9e 100644 --- a/packages/mesh/src/Mesh.js +++ b/packages/mesh/src/Mesh.js @@ -1,5 +1,5 @@ import RawMesh from './RawMesh'; -import { Geometry, Program, Shader, Texture } from '@pixi/core'; +import { Geometry, Program, Shader, Texture, TextureMatrix } from '@pixi/core'; import { Matrix } from '@pixi/math'; import { BLEND_MODES } from '@pixi/constants'; import { hex2rgb, premultiplyRgba } from '@pixi/utils'; @@ -23,7 +23,7 @@ * @param {Uint16Array} [indices] - if you want to specify the indices * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts */ - constructor(texture, vertices, uvs, indices, drawMode) + constructor(texture = Texture.EMPTY, vertices, uvs, indices, drawMode) { const geometry = new Geometry(); @@ -40,7 +40,7 @@ const uniforms = { uSampler: texture, - uTransform: Matrix.IDENTITY, + uTextureMatrix: Matrix.IDENTITY, alpha: 1, uColor: new Float32Array([1, 1, 1, 1]), }; @@ -70,7 +70,7 @@ * @default PIXI.Texture.EMPTY * @private */ - this.texture = texture || Texture.EMPTY; + this.texture = texture; /** * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any @@ -85,6 +85,31 @@ this.tint = 0xFFFFFF; this.blendMode = BLEND_MODES.NORMAL; + + /** + * TextureMatrix instance for this Mesh, used to track Texture changes + * + * @member {PIXI.TextureMatrix} + * @readonly + */ + this.uvMatrix = new TextureMatrix(this._texture); + + /** + * whether or not upload uvMatrix to shader + * if its false, then uvs should be pre-multiplied + * if you change it for generated mesh, please call 'refresh(true)' + * @member {boolean} + * @default false + */ + this.uploadUvMatrix = false; + + /** + * Uploads vertices buffer every frame, like in PixiJS V3 and V4. + * + * @member {boolean} + * @default true + */ + this.autoUpdate = true; } /** @@ -152,7 +177,7 @@ if (value) { // wait for the texture to load - if (value.baseTexture.hasLoaded) + if (value.baseTexture.valid) { this._onTextureUpdate(); } @@ -177,6 +202,62 @@ */ _onTextureUpdate() { + // constructor texture update stop + if (!this.uvMatrix) + { + return; + } + this.uvMatrix.texture = this._texture; + this.refresh(); + } + + /** + * multiplies uvs only if uploadUvMatrix is false + * call it after you change uvs manually + * make sure that texture is valid + */ + multiplyUvs() + { + if (!this.uploadUvMatrix) + { + this.uvMatrix.multiplyUvs(this.uvs); + } + } + + /** + * Refreshes uvs for generated meshes (rope, plane) + * sometimes refreshes vertices too + * + * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case + */ + refresh(forceUpdate) + { + if (this.uvMatrix.update(forceUpdate)) + { + this._refresh(); + } + } + + /** + * Updates the object transform for rendering + * + * @private + */ + updateTransform() + { + if (this.autoUpdate) + { + this.geometry.buffers[0].update(); + } + this.containerUpdateTransform(); + } + + /** + * re-calculates mesh coords + * @private + */ + _refresh() + { /* empty */ } } diff --git a/packages/mesh/src/MeshRenderer.js b/packages/mesh/src/MeshRenderer.js index ce6dfe6..12b159b 100644 --- a/packages/mesh/src/MeshRenderer.js +++ b/packages/mesh/src/MeshRenderer.js @@ -1,4 +1,5 @@ import { ObjectRenderer } from '@pixi/core'; +import { Matrix } from '@pixi/math'; /** * WebGL renderer plugin for tiling sprites @@ -44,12 +45,23 @@ // TODO // set the shader props.. // probably only need to set once! - // as its then a refference.. + // as its then a reference.. if (mesh.shader.program.uniformData.translationMatrix) { // the transform! mesh.shader.uniforms.translationMatrix = mesh.transform.worldTransform.toArray(true); } + if (mesh.shader.uniforms.uTextureMatrix) + { + if (mesh.uploadUvMatrix) + { + mesh.shader.uniforms.uTextureMatrix = mesh.uvMatrix.mapCoord.toArray(true); + } + else + { + mesh.shader.uniforms.uTextureMatrix = Matrix.IDENTITY.toArray(true); + } + } // bind and sync uniforms.. this.renderer.shader.bind(mesh.shader); diff --git a/packages/mesh/src/NineSlicePlane.js b/packages/mesh/src/NineSlicePlane.js index 44e3498..a392985 100644 --- a/packages/mesh/src/NineSlicePlane.js +++ b/packages/mesh/src/NineSlicePlane.js @@ -133,58 +133,6 @@ } /** - * Renders one segment of the plane. - * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure - * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. - * - * @private - * @param {CanvasRenderingContext2D} context - The context to draw with. - * @param {CanvasImageSource} textureSource - The source to draw. - * @param {number} w - width of the texture - * @param {number} h - height of the texture - * @param {number} x1 - x index 1 - * @param {number} y1 - y index 1 - * @param {number} x2 - x index 2 - * @param {number} y2 - y index 2 - */ - drawSegment(context, textureSource, w, h, x1, y1, x2, y2) - { - // otherwise you get weird results when using slices of that are 0 wide or high. - const uvs = this.uvs; - const vertices = this.vertices; - - let sw = (uvs[x2] - uvs[x1]) * w; - let sh = (uvs[y2] - uvs[y1]) * h; - let dw = vertices[x2] - vertices[x1]; - let dh = vertices[y2] - vertices[y1]; - - // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. - if (sw < 1) - { - sw = 1; - } - - if (sh < 1) - { - sh = 1; - } - - // make sure destination is at least 1 pixel wide and high, otherwise you get - // lines when rendering close to original size. - if (dw < 1) - { - dw = 1; - } - - if (dh < 1) - { - dh = 1; - } - - context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); - } - - /** * The width of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane * * @member {number} @@ -309,7 +257,7 @@ this.updateHorizontalVertices(); this.updateVerticalVertices(); - this.dirty++; + this.geometry.buffers[1].update(); this.multiplyUvs(); } diff --git a/packages/mesh/src/Plane.js b/packages/mesh/src/Plane.js index b874a39..a50b2d7 100644 --- a/packages/mesh/src/Plane.js +++ b/packages/mesh/src/Plane.js @@ -21,61 +21,51 @@ * @param {PIXI.Texture} texture - The texture to use on the Plane. * @param {number} verticesX - The number of vertices in the x-axis * @param {number} verticesY - The number of vertices in the y-axis - * @param {object} opts - an options object - add meshWidth and meshHeight + * @param {object} [opts] - an options object - add meshWidth and meshHeight, temporary object for PixiJS v5 alpha */ constructor(texture, verticesX, verticesY, opts = {}) { super(texture, new Float32Array(1), new Float32Array(1), new Uint16Array(1), 4); - this.segmentsX = this.verticesX = verticesX || 10; - this.segmentsY = this.verticesY = verticesY || 10; + this.verticesX = verticesX || 10; + this.verticesY = verticesY || 10; - this.meshWidth = opts.meshWidth || texture.width; - this.meshHeight = opts.meshHeight || texture.height; + this.meshWidth = opts.meshWidth || 0; + this.meshHeight = opts.meshHeight || 0; this.refresh(); } /** - * Refreshes - * + * Refreshes plane coordinates + * @private */ - refresh() + _refresh() { + const texture = this._texture; const total = this.verticesX * this.verticesY; const verts = []; const uvs = []; const indices = []; - const texture = this.texture; const segmentsX = this.verticesX - 1; const segmentsY = this.verticesY - 1; - const sizeX = this.meshWidth / segmentsX; - const sizeY = this.meshHeight / segmentsY; + const sizeX = (this.meshWidth || texture.width) / segmentsX; + const sizeY = (this.meshHeight || texture.height) / segmentsY; for (let i = 0; i < total; i++) { - if (texture._uvs) - { - const x = (i % this.verticesX); - const y = ((i / this.verticesX) | 0); + const x = (i % this.verticesX); + const y = ((i / this.verticesX) | 0); - verts.push((x * sizeX), - (y * sizeY)); + verts.push(x * sizeX, y * sizeY); - // this works for rectangular textures. - uvs.push( - texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))), - texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1))) - ); - } - else - { - uvs.push(0); - } + uvs.push(x / segmentsX, y / segmentsY); } + // cons + const totalSub = segmentsX * segmentsY; for (let i = 0; i < totalSub; i++) @@ -92,9 +82,6 @@ indices.push(value2, value4, value3); } - this.shader.uniforms.alpha = 1; - this.shader.uniforms.uSampler2 = this.texture; - this.vertices = new Float32Array(verts); this.uvs = new Float32Array(uvs); this.indices = new Uint16Array(indices); @@ -107,16 +94,7 @@ this.geometry.buffers[0].update(); this.geometry.buffers[1].update(); this.geometry.indexBuffer.update(); - } - /** - * Updates the object transform for rendering - * - * @private - */ - updateTransform() - { - this.geometry.buffers[0].update(); - this.containerUpdateTransform(); + this.multiplyUvs(); } } diff --git a/packages/mesh/src/Rope.js b/packages/mesh/src/Rope.js index f88678a..8a409fe 100644 --- a/packages/mesh/src/Rope.js +++ b/packages/mesh/src/Rope.js @@ -1,5 +1,4 @@ import Mesh from './Mesh'; -import { Point } from '@pixi/math'; /** * The rope allows you to draw a texture across several points and them manipulate these points @@ -35,11 +34,11 @@ this.points = points; this.refresh(); } - /** - * Refreshes + * Refreshes Rope indices and uvs + * @private */ - refresh() + _refresh() { const points = this.points; @@ -66,14 +65,10 @@ const uvs = uvBuffer.data; const indices = indexBuffer.data; - const textureUvs = this.texture._uvs; - const offset = new Point(textureUvs.x0, textureUvs.y0); - const factor = new Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0)); - - uvs[0] = 0 + offset.x; - uvs[1] = 0 + offset.y; - uvs[2] = 0 + offset.x; - uvs[3] = factor.y + offset.y; + uvs[0] = 0; + uvs[1] = 0; + uvs[2] = 0; + uvs[3] = 1; indices[0] = 0; indices[1] = 1; @@ -86,11 +81,11 @@ let index = i * 4; const amount = i / (total - 1); - uvs[index] = (amount * factor.x) + offset.x; - uvs[index + 1] = 0 + offset.y; + uvs[index] = amount; + uvs[index + 1] = 0; - uvs[index + 2] = (amount * factor.x) + offset.x; - uvs[index + 3] = factor.y + offset.y; + uvs[index + 2] = amount; + uvs[index + 3] = 1; index = i * 2; indices[index] = index; @@ -98,17 +93,17 @@ } // ensure that the changes are uploaded - vertexBuffer.update(); uvBuffer.update(); indexBuffer.update(); + + this.multiplyUvs(); + this.refreshVertices(); } /** - * Updates the object transform for rendering - * - * @private + * refreshes vertices of Rope mesh */ - updateTransform() + refreshVertices() { const points = this.points; @@ -124,9 +119,7 @@ // this.count -= 0.2; - const vertexBuffer = this.geometry.getAttribute('aVertexPosition'); - const vertices = vertexBuffer.data; - + const vertices = this.vertices; const total = points.length; for (let i = 0; i < total; i++) @@ -152,8 +145,9 @@ { ratio = 1; } + const perpLength = Math.sqrt((perpX * perpX) + (perpY * perpY)); - const num = this.texture.height / 2; // (20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; + const num = this._texture.height / 2; // (20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perpX /= perpLength; perpY /= perpLength; @@ -169,21 +163,20 @@ lastPoint = point; } - // mark the buffer as requiring an upload.. - vertexBuffer.update(); - - this.uniforms.alpha = this.worldAlpha; - - this.containerUpdateTransform(); + this.geometry.buffers[0].update(); } /** - * When the texture is updated, this event will fire to update the scale and frame + * Updates the object transform for rendering * * @private */ - _onTextureUpdate() + updateTransform() { - this.refresh(); + if (this.autoUpdate) + { + this.refreshVertices(); + } + this.containerUpdateTransform(); } } diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index 2a9eba9..09eaf9c 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -42,7 +42,7 @@ } const base = this._texture.baseTexture; - const textureSource = base.source; + const textureSource = base.getDrawableSource(); const w = base.width * base.resolution; const h = base.height * base.resolution; @@ -56,3 +56,56 @@ this.drawSegment(context, textureSource, w, h, 18, 19, 28, 29); this.drawSegment(context, textureSource, w, h, 20, 21, 30, 31); }; + +/** + * Renders one segment of the plane. + * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure + * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. + * + * @method drawSegment + * @memberof PIXI.NineSlicePlane# + * @param {CanvasRenderingContext2D} context - The context to draw with. + * @param {CanvasImageSource} textureSource - The source to draw. + * @param {number} w - width of the texture + * @param {number} h - height of the texture + * @param {number} x1 - x index 1 + * @param {number} y1 - y index 1 + * @param {number} x2 - x index 2 + * @param {number} y2 - y index 2 + */ +NineSlicePlane.prototype.drawSegment = function drawSegment(context, textureSource, w, h, x1, y1, x2, y2) +{ + // otherwise you get weird results when using slices of that are 0 wide or high. + const uvs = this.uvs; + const vertices = this.vertices; + + let sw = (uvs[x2] - uvs[x1]) * w; + let sh = (uvs[y2] - uvs[y1]) * h; + let dw = vertices[x2] - vertices[x1]; + let dh = vertices[y2] - vertices[y1]; + + // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. + if (sw < 1) + { + sw = 1; + } + + if (sh < 1) + { + sh = 1; + } + + // make sure destination is at least 1 pixel wide and high, otherwise you get + // lines when rendering close to original size. + if (dw < 1) + { + dw = 1; + } + + if (dh < 1) + { + dh = 1; + } + + context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); +}; diff --git a/packages/core/src/renderTexture/RenderTexture.js b/packages/core/src/renderTexture/RenderTexture.js index f1ecf1f..a64b7e7 100644 --- a/packages/core/src/renderTexture/RenderTexture.js +++ b/packages/core/src/renderTexture/RenderTexture.js @@ -94,7 +94,7 @@ */ this.filterFrame = null; - this._updateUvs(); + this.updateUvs(); } /** @@ -120,7 +120,7 @@ this.baseTexture.resize(width, height); } - this._updateUvs(); + this.updateUvs(); } /** diff --git a/packages/core/src/textures/RenderTexture.js b/packages/core/src/textures/RenderTexture.js deleted file mode 100644 index 34f09b2..0000000 --- a/packages/core/src/textures/RenderTexture.js +++ /dev/null @@ -1,153 +0,0 @@ -import BaseRenderTexture from './BaseRenderTexture'; -import Texture from './Texture'; - -/** - * A RenderTexture is a special texture that allows any PixiJS display object to be rendered to it. - * - * __Hint__: All DisplayObjects (i.e. Sprites) that render to a RenderTexture should be preloaded - * otherwise black rectangles will be drawn instead. - * - * A RenderTexture takes a snapshot of any Display Object given to its render method. For example: - * - * ```js - * let renderer = PIXI.autoDetectRenderer(1024, 1024, { view: canvas, ratio: 1 }); - * let renderTexture = PIXI.RenderTexture.create(800, 600); - * let sprite = PIXI.Sprite.fromImage("spinObj_01.png"); - * - * sprite.position.x = 800/2; - * sprite.position.y = 600/2; - * sprite.anchor.x = 0.5; - * sprite.anchor.y = 0.5; - * - * renderer.render(sprite, renderTexture); - * ``` - * - * The Sprite in this case will be rendered using its local transform. To render this sprite at 0,0 - * you can clear the transform - * - * ```js - * - * sprite.setTransform() - * - * let renderTexture = new PIXI.RenderTexture.create(100, 100); - * - * renderer.render(sprite, renderTexture); // Renders to center of RenderTexture - * ``` - * - * @class - * @extends PIXI.Texture - * @memberof PIXI - */ -export default class RenderTexture extends Texture -{ - /** - * @param {PIXI.BaseRenderTexture} baseRenderTexture - The renderer used for this RenderTexture - * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show - */ - constructor(baseRenderTexture, frame) - { - // support for legacy.. - let _legacyRenderer = null; - - if (!(baseRenderTexture instanceof BaseRenderTexture)) - { - /* eslint-disable prefer-rest-params, no-console */ - const width = arguments[1]; - const height = arguments[2]; - const scaleMode = arguments[3]; - const resolution = arguments[4]; - - // we have an old render texture.. - console.warn(`Please use RenderTexture.create(${width}, ${height}) instead of the ctor directly.`); - _legacyRenderer = arguments[0]; - /* eslint-enable prefer-rest-params, no-console */ - - frame = null; - baseRenderTexture = new BaseRenderTexture({ - width, - height, - scaleMode, - resolution, - }); - } - - /** - * The base texture object that this texture uses - * - * @member {BaseTexture} - */ - super(baseRenderTexture, frame); - - this.legacyRenderer = _legacyRenderer; - - /** - * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered. - * - * @member {boolean} - */ - this.valid = true; - - /** - * FilterSystem temporary storage - * @private - * @member {PIXI.Rectangle} - */ - this.filterFrame = null; - - this._updateUvs(); - } - - /** - * Resizes the RenderTexture. - * - * @param {number} width - The width to resize to. - * @param {number} height - The height to resize to. - * @param {boolean} [resizeBaseTexture=true] - Should the baseTexture.width and height values be resized as well? - */ - resize(width, height, resizeBaseTexture = true) - { - width = Math.ceil(width); - height = Math.ceil(height); - - // TODO - could be not required.. - this.valid = (width > 0 && height > 0); - - this._frame.width = this.orig.width = width; - this._frame.height = this.orig.height = height; - - if (resizeBaseTexture) - { - this.baseTexture.resize(width, height); - } - - this._updateUvs(); - } - - /** - * A short hand way of creating a render texture. - * - * @param {object} [options] - Options - * @param {number} [options.width=100] - The width of the render texture - * @param {number} [options.height=100] - The height of the render texture - * @param {number} [options.scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values - * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the texture being generated - * @return {PIXI.RenderTexture} The new render texture - */ - static create(options) - { - // fallback, old-style: create(width, height, scaleMode, resolution) - if (typeof options === 'number') - { - /* eslint-disable prefer-rest-params */ - options = { - width: options, - height: arguments[1], - scaleMode: arguments[2], - resolution: arguments[3], - }; - /* eslint-enable prefer-rest-params */ - } - - return new RenderTexture(new BaseRenderTexture(options)); - } -} diff --git a/packages/core/src/textures/Texture.js b/packages/core/src/textures/Texture.js index 5a22225..237dec3 100644 --- a/packages/core/src/textures/Texture.js +++ b/packages/core/src/textures/Texture.js @@ -81,7 +81,7 @@ /** * This is the trimmed area of original texture, before it was put in atlas - * Please call `_updateUvs()` after you change coordinates of `trim` manually. + * Please call `updateUvs()` after you change coordinates of `trim` manually. * * @member {PIXI.Rectangle} */ @@ -102,7 +102,7 @@ this.requiresUpdate = false; /** - * The WebGL UV data cache. + * The WebGL UV data cache. Can be used as quad UV * * @member {PIXI.TextureUvs} * @private @@ -110,6 +110,14 @@ this._uvs = null; /** + * Default TextureMatrix instance for this texture + * By default that object is not created because its heavy + * + * @member {PIXI.TextureMatrix} + */ + this.uvMatrix = null; + + /** * This is the area of original texture, before it was put in atlas * * @member {PIXI.Rectangle} @@ -145,24 +153,16 @@ } /** - * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. + * Update ID is observed by sprites and TextureMatrix instances. + * Call updateUvs() to increment it. * - * @event PIXI.Texture#update - * @protected - * @param {PIXI.Texture} texture - Instance of texture being updated. + * @member {number} + * @private */ this._updateID = 0; /** - * Contains data for uvs. May contain clamp settings and some matrices. - * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} - * @default null - */ - this.transform = null; - - /** * The ids under which this Texture has been added to the texture cache. This is * automatically set as long as Texture.addToCache is used, but may not be set if a * Texture is added directly to the TextureCache array. @@ -255,8 +255,9 @@ /** * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture. + * Call it after changing the frame */ - _updateUvs() + updateUvs() { if (!this._uvs) { @@ -446,7 +447,7 @@ /** * The frame specifies the region of the base texture that this texture uses. - * Please call `_updateUvs()` after you change coordinates of `frame` manually. + * Please call `updateUvs()` after you change coordinates of `frame` manually. * * @member {PIXI.Rectangle} */ @@ -484,7 +485,7 @@ if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } @@ -507,7 +508,7 @@ this._rotate = rotate; if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } diff --git a/packages/core/src/textures/TextureMatrix.js b/packages/core/src/textures/TextureMatrix.js index 2d4ecea..8198ed1 100644 --- a/packages/core/src/textures/TextureMatrix.js +++ b/packages/core/src/textures/TextureMatrix.js @@ -3,12 +3,18 @@ const tempMat = new Matrix(); /** - * Class controls uv transform and frame clamp for texture - * Can be used in Texture "transform" field, or separately, you can use different clamp settings on the same texture. + * Class controls uv mapping from Texture normal space to BaseTexture normal space. + * Takes `trim` and `rotate` into account. + * May contain clamp settings for Meshes and TilingSprite. + * + * Can be used in Texture `uvMatrix` field, or separately, you can use different clamp settings on the same texture. * If you want to add support for texture region of certain feature or filter, that's what you're looking for. * + * Takes track of Texture changes through `_lastTextureID` private field. + * Use `update()` method call to track it from outside. + * * @see PIXI.Texture - * @see PIXI.mesh.Mesh + * @see PIXI.Mesh * @see PIXI.TilingSprite * @class * @memberof PIXI @@ -31,6 +37,10 @@ this.uClampOffset = new Float32Array(2); + /** + * @member {number} Tracks Texture frame changes + * @private + */ this._lastTextureID = -1; /** diff --git a/packages/mesh/src/Mesh.js b/packages/mesh/src/Mesh.js index 55dc092..e7c8f9e 100644 --- a/packages/mesh/src/Mesh.js +++ b/packages/mesh/src/Mesh.js @@ -1,5 +1,5 @@ import RawMesh from './RawMesh'; -import { Geometry, Program, Shader, Texture } from '@pixi/core'; +import { Geometry, Program, Shader, Texture, TextureMatrix } from '@pixi/core'; import { Matrix } from '@pixi/math'; import { BLEND_MODES } from '@pixi/constants'; import { hex2rgb, premultiplyRgba } from '@pixi/utils'; @@ -23,7 +23,7 @@ * @param {Uint16Array} [indices] - if you want to specify the indices * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts */ - constructor(texture, vertices, uvs, indices, drawMode) + constructor(texture = Texture.EMPTY, vertices, uvs, indices, drawMode) { const geometry = new Geometry(); @@ -40,7 +40,7 @@ const uniforms = { uSampler: texture, - uTransform: Matrix.IDENTITY, + uTextureMatrix: Matrix.IDENTITY, alpha: 1, uColor: new Float32Array([1, 1, 1, 1]), }; @@ -70,7 +70,7 @@ * @default PIXI.Texture.EMPTY * @private */ - this.texture = texture || Texture.EMPTY; + this.texture = texture; /** * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any @@ -85,6 +85,31 @@ this.tint = 0xFFFFFF; this.blendMode = BLEND_MODES.NORMAL; + + /** + * TextureMatrix instance for this Mesh, used to track Texture changes + * + * @member {PIXI.TextureMatrix} + * @readonly + */ + this.uvMatrix = new TextureMatrix(this._texture); + + /** + * whether or not upload uvMatrix to shader + * if its false, then uvs should be pre-multiplied + * if you change it for generated mesh, please call 'refresh(true)' + * @member {boolean} + * @default false + */ + this.uploadUvMatrix = false; + + /** + * Uploads vertices buffer every frame, like in PixiJS V3 and V4. + * + * @member {boolean} + * @default true + */ + this.autoUpdate = true; } /** @@ -152,7 +177,7 @@ if (value) { // wait for the texture to load - if (value.baseTexture.hasLoaded) + if (value.baseTexture.valid) { this._onTextureUpdate(); } @@ -177,6 +202,62 @@ */ _onTextureUpdate() { + // constructor texture update stop + if (!this.uvMatrix) + { + return; + } + this.uvMatrix.texture = this._texture; + this.refresh(); + } + + /** + * multiplies uvs only if uploadUvMatrix is false + * call it after you change uvs manually + * make sure that texture is valid + */ + multiplyUvs() + { + if (!this.uploadUvMatrix) + { + this.uvMatrix.multiplyUvs(this.uvs); + } + } + + /** + * Refreshes uvs for generated meshes (rope, plane) + * sometimes refreshes vertices too + * + * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case + */ + refresh(forceUpdate) + { + if (this.uvMatrix.update(forceUpdate)) + { + this._refresh(); + } + } + + /** + * Updates the object transform for rendering + * + * @private + */ + updateTransform() + { + if (this.autoUpdate) + { + this.geometry.buffers[0].update(); + } + this.containerUpdateTransform(); + } + + /** + * re-calculates mesh coords + * @private + */ + _refresh() + { /* empty */ } } diff --git a/packages/mesh/src/MeshRenderer.js b/packages/mesh/src/MeshRenderer.js index ce6dfe6..12b159b 100644 --- a/packages/mesh/src/MeshRenderer.js +++ b/packages/mesh/src/MeshRenderer.js @@ -1,4 +1,5 @@ import { ObjectRenderer } from '@pixi/core'; +import { Matrix } from '@pixi/math'; /** * WebGL renderer plugin for tiling sprites @@ -44,12 +45,23 @@ // TODO // set the shader props.. // probably only need to set once! - // as its then a refference.. + // as its then a reference.. if (mesh.shader.program.uniformData.translationMatrix) { // the transform! mesh.shader.uniforms.translationMatrix = mesh.transform.worldTransform.toArray(true); } + if (mesh.shader.uniforms.uTextureMatrix) + { + if (mesh.uploadUvMatrix) + { + mesh.shader.uniforms.uTextureMatrix = mesh.uvMatrix.mapCoord.toArray(true); + } + else + { + mesh.shader.uniforms.uTextureMatrix = Matrix.IDENTITY.toArray(true); + } + } // bind and sync uniforms.. this.renderer.shader.bind(mesh.shader); diff --git a/packages/mesh/src/NineSlicePlane.js b/packages/mesh/src/NineSlicePlane.js index 44e3498..a392985 100644 --- a/packages/mesh/src/NineSlicePlane.js +++ b/packages/mesh/src/NineSlicePlane.js @@ -133,58 +133,6 @@ } /** - * Renders one segment of the plane. - * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure - * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. - * - * @private - * @param {CanvasRenderingContext2D} context - The context to draw with. - * @param {CanvasImageSource} textureSource - The source to draw. - * @param {number} w - width of the texture - * @param {number} h - height of the texture - * @param {number} x1 - x index 1 - * @param {number} y1 - y index 1 - * @param {number} x2 - x index 2 - * @param {number} y2 - y index 2 - */ - drawSegment(context, textureSource, w, h, x1, y1, x2, y2) - { - // otherwise you get weird results when using slices of that are 0 wide or high. - const uvs = this.uvs; - const vertices = this.vertices; - - let sw = (uvs[x2] - uvs[x1]) * w; - let sh = (uvs[y2] - uvs[y1]) * h; - let dw = vertices[x2] - vertices[x1]; - let dh = vertices[y2] - vertices[y1]; - - // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. - if (sw < 1) - { - sw = 1; - } - - if (sh < 1) - { - sh = 1; - } - - // make sure destination is at least 1 pixel wide and high, otherwise you get - // lines when rendering close to original size. - if (dw < 1) - { - dw = 1; - } - - if (dh < 1) - { - dh = 1; - } - - context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); - } - - /** * The width of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane * * @member {number} @@ -309,7 +257,7 @@ this.updateHorizontalVertices(); this.updateVerticalVertices(); - this.dirty++; + this.geometry.buffers[1].update(); this.multiplyUvs(); } diff --git a/packages/mesh/src/Plane.js b/packages/mesh/src/Plane.js index b874a39..a50b2d7 100644 --- a/packages/mesh/src/Plane.js +++ b/packages/mesh/src/Plane.js @@ -21,61 +21,51 @@ * @param {PIXI.Texture} texture - The texture to use on the Plane. * @param {number} verticesX - The number of vertices in the x-axis * @param {number} verticesY - The number of vertices in the y-axis - * @param {object} opts - an options object - add meshWidth and meshHeight + * @param {object} [opts] - an options object - add meshWidth and meshHeight, temporary object for PixiJS v5 alpha */ constructor(texture, verticesX, verticesY, opts = {}) { super(texture, new Float32Array(1), new Float32Array(1), new Uint16Array(1), 4); - this.segmentsX = this.verticesX = verticesX || 10; - this.segmentsY = this.verticesY = verticesY || 10; + this.verticesX = verticesX || 10; + this.verticesY = verticesY || 10; - this.meshWidth = opts.meshWidth || texture.width; - this.meshHeight = opts.meshHeight || texture.height; + this.meshWidth = opts.meshWidth || 0; + this.meshHeight = opts.meshHeight || 0; this.refresh(); } /** - * Refreshes - * + * Refreshes plane coordinates + * @private */ - refresh() + _refresh() { + const texture = this._texture; const total = this.verticesX * this.verticesY; const verts = []; const uvs = []; const indices = []; - const texture = this.texture; const segmentsX = this.verticesX - 1; const segmentsY = this.verticesY - 1; - const sizeX = this.meshWidth / segmentsX; - const sizeY = this.meshHeight / segmentsY; + const sizeX = (this.meshWidth || texture.width) / segmentsX; + const sizeY = (this.meshHeight || texture.height) / segmentsY; for (let i = 0; i < total; i++) { - if (texture._uvs) - { - const x = (i % this.verticesX); - const y = ((i / this.verticesX) | 0); + const x = (i % this.verticesX); + const y = ((i / this.verticesX) | 0); - verts.push((x * sizeX), - (y * sizeY)); + verts.push(x * sizeX, y * sizeY); - // this works for rectangular textures. - uvs.push( - texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))), - texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1))) - ); - } - else - { - uvs.push(0); - } + uvs.push(x / segmentsX, y / segmentsY); } + // cons + const totalSub = segmentsX * segmentsY; for (let i = 0; i < totalSub; i++) @@ -92,9 +82,6 @@ indices.push(value2, value4, value3); } - this.shader.uniforms.alpha = 1; - this.shader.uniforms.uSampler2 = this.texture; - this.vertices = new Float32Array(verts); this.uvs = new Float32Array(uvs); this.indices = new Uint16Array(indices); @@ -107,16 +94,7 @@ this.geometry.buffers[0].update(); this.geometry.buffers[1].update(); this.geometry.indexBuffer.update(); - } - /** - * Updates the object transform for rendering - * - * @private - */ - updateTransform() - { - this.geometry.buffers[0].update(); - this.containerUpdateTransform(); + this.multiplyUvs(); } } diff --git a/packages/mesh/src/Rope.js b/packages/mesh/src/Rope.js index f88678a..8a409fe 100644 --- a/packages/mesh/src/Rope.js +++ b/packages/mesh/src/Rope.js @@ -1,5 +1,4 @@ import Mesh from './Mesh'; -import { Point } from '@pixi/math'; /** * The rope allows you to draw a texture across several points and them manipulate these points @@ -35,11 +34,11 @@ this.points = points; this.refresh(); } - /** - * Refreshes + * Refreshes Rope indices and uvs + * @private */ - refresh() + _refresh() { const points = this.points; @@ -66,14 +65,10 @@ const uvs = uvBuffer.data; const indices = indexBuffer.data; - const textureUvs = this.texture._uvs; - const offset = new Point(textureUvs.x0, textureUvs.y0); - const factor = new Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0)); - - uvs[0] = 0 + offset.x; - uvs[1] = 0 + offset.y; - uvs[2] = 0 + offset.x; - uvs[3] = factor.y + offset.y; + uvs[0] = 0; + uvs[1] = 0; + uvs[2] = 0; + uvs[3] = 1; indices[0] = 0; indices[1] = 1; @@ -86,11 +81,11 @@ let index = i * 4; const amount = i / (total - 1); - uvs[index] = (amount * factor.x) + offset.x; - uvs[index + 1] = 0 + offset.y; + uvs[index] = amount; + uvs[index + 1] = 0; - uvs[index + 2] = (amount * factor.x) + offset.x; - uvs[index + 3] = factor.y + offset.y; + uvs[index + 2] = amount; + uvs[index + 3] = 1; index = i * 2; indices[index] = index; @@ -98,17 +93,17 @@ } // ensure that the changes are uploaded - vertexBuffer.update(); uvBuffer.update(); indexBuffer.update(); + + this.multiplyUvs(); + this.refreshVertices(); } /** - * Updates the object transform for rendering - * - * @private + * refreshes vertices of Rope mesh */ - updateTransform() + refreshVertices() { const points = this.points; @@ -124,9 +119,7 @@ // this.count -= 0.2; - const vertexBuffer = this.geometry.getAttribute('aVertexPosition'); - const vertices = vertexBuffer.data; - + const vertices = this.vertices; const total = points.length; for (let i = 0; i < total; i++) @@ -152,8 +145,9 @@ { ratio = 1; } + const perpLength = Math.sqrt((perpX * perpX) + (perpY * perpY)); - const num = this.texture.height / 2; // (20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; + const num = this._texture.height / 2; // (20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perpX /= perpLength; perpY /= perpLength; @@ -169,21 +163,20 @@ lastPoint = point; } - // mark the buffer as requiring an upload.. - vertexBuffer.update(); - - this.uniforms.alpha = this.worldAlpha; - - this.containerUpdateTransform(); + this.geometry.buffers[0].update(); } /** - * When the texture is updated, this event will fire to update the scale and frame + * Updates the object transform for rendering * * @private */ - _onTextureUpdate() + updateTransform() { - this.refresh(); + if (this.autoUpdate) + { + this.refreshVertices(); + } + this.containerUpdateTransform(); } } diff --git a/packages/mesh/src/mesh.vert b/packages/mesh/src/mesh.vert index acc096c..0526df9 100644 --- a/packages/mesh/src/mesh.vert +++ b/packages/mesh/src/mesh.vert @@ -3,7 +3,7 @@ uniform mat3 projectionMatrix; uniform mat3 translationMatrix; -uniform mat3 uTransform; +uniform mat3 uTextureMatrix; varying vec2 vTextureCoord; @@ -11,5 +11,5 @@ { gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); - vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy; + vTextureCoord = (uTextureMatrix * vec3(aTextureCoord, 1.0)).xy; } diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index 2a9eba9..09eaf9c 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -42,7 +42,7 @@ } const base = this._texture.baseTexture; - const textureSource = base.source; + const textureSource = base.getDrawableSource(); const w = base.width * base.resolution; const h = base.height * base.resolution; @@ -56,3 +56,56 @@ this.drawSegment(context, textureSource, w, h, 18, 19, 28, 29); this.drawSegment(context, textureSource, w, h, 20, 21, 30, 31); }; + +/** + * Renders one segment of the plane. + * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure + * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. + * + * @method drawSegment + * @memberof PIXI.NineSlicePlane# + * @param {CanvasRenderingContext2D} context - The context to draw with. + * @param {CanvasImageSource} textureSource - The source to draw. + * @param {number} w - width of the texture + * @param {number} h - height of the texture + * @param {number} x1 - x index 1 + * @param {number} y1 - y index 1 + * @param {number} x2 - x index 2 + * @param {number} y2 - y index 2 + */ +NineSlicePlane.prototype.drawSegment = function drawSegment(context, textureSource, w, h, x1, y1, x2, y2) +{ + // otherwise you get weird results when using slices of that are 0 wide or high. + const uvs = this.uvs; + const vertices = this.vertices; + + let sw = (uvs[x2] - uvs[x1]) * w; + let sh = (uvs[y2] - uvs[y1]) * h; + let dw = vertices[x2] - vertices[x1]; + let dh = vertices[y2] - vertices[y1]; + + // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. + if (sw < 1) + { + sw = 1; + } + + if (sh < 1) + { + sh = 1; + } + + // make sure destination is at least 1 pixel wide and high, otherwise you get + // lines when rendering close to original size. + if (dw < 1) + { + dw = 1; + } + + if (dh < 1) + { + dh = 1; + } + + context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); +}; diff --git a/packages/core/src/renderTexture/RenderTexture.js b/packages/core/src/renderTexture/RenderTexture.js index f1ecf1f..a64b7e7 100644 --- a/packages/core/src/renderTexture/RenderTexture.js +++ b/packages/core/src/renderTexture/RenderTexture.js @@ -94,7 +94,7 @@ */ this.filterFrame = null; - this._updateUvs(); + this.updateUvs(); } /** @@ -120,7 +120,7 @@ this.baseTexture.resize(width, height); } - this._updateUvs(); + this.updateUvs(); } /** diff --git a/packages/core/src/textures/RenderTexture.js b/packages/core/src/textures/RenderTexture.js deleted file mode 100644 index 34f09b2..0000000 --- a/packages/core/src/textures/RenderTexture.js +++ /dev/null @@ -1,153 +0,0 @@ -import BaseRenderTexture from './BaseRenderTexture'; -import Texture from './Texture'; - -/** - * A RenderTexture is a special texture that allows any PixiJS display object to be rendered to it. - * - * __Hint__: All DisplayObjects (i.e. Sprites) that render to a RenderTexture should be preloaded - * otherwise black rectangles will be drawn instead. - * - * A RenderTexture takes a snapshot of any Display Object given to its render method. For example: - * - * ```js - * let renderer = PIXI.autoDetectRenderer(1024, 1024, { view: canvas, ratio: 1 }); - * let renderTexture = PIXI.RenderTexture.create(800, 600); - * let sprite = PIXI.Sprite.fromImage("spinObj_01.png"); - * - * sprite.position.x = 800/2; - * sprite.position.y = 600/2; - * sprite.anchor.x = 0.5; - * sprite.anchor.y = 0.5; - * - * renderer.render(sprite, renderTexture); - * ``` - * - * The Sprite in this case will be rendered using its local transform. To render this sprite at 0,0 - * you can clear the transform - * - * ```js - * - * sprite.setTransform() - * - * let renderTexture = new PIXI.RenderTexture.create(100, 100); - * - * renderer.render(sprite, renderTexture); // Renders to center of RenderTexture - * ``` - * - * @class - * @extends PIXI.Texture - * @memberof PIXI - */ -export default class RenderTexture extends Texture -{ - /** - * @param {PIXI.BaseRenderTexture} baseRenderTexture - The renderer used for this RenderTexture - * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show - */ - constructor(baseRenderTexture, frame) - { - // support for legacy.. - let _legacyRenderer = null; - - if (!(baseRenderTexture instanceof BaseRenderTexture)) - { - /* eslint-disable prefer-rest-params, no-console */ - const width = arguments[1]; - const height = arguments[2]; - const scaleMode = arguments[3]; - const resolution = arguments[4]; - - // we have an old render texture.. - console.warn(`Please use RenderTexture.create(${width}, ${height}) instead of the ctor directly.`); - _legacyRenderer = arguments[0]; - /* eslint-enable prefer-rest-params, no-console */ - - frame = null; - baseRenderTexture = new BaseRenderTexture({ - width, - height, - scaleMode, - resolution, - }); - } - - /** - * The base texture object that this texture uses - * - * @member {BaseTexture} - */ - super(baseRenderTexture, frame); - - this.legacyRenderer = _legacyRenderer; - - /** - * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered. - * - * @member {boolean} - */ - this.valid = true; - - /** - * FilterSystem temporary storage - * @private - * @member {PIXI.Rectangle} - */ - this.filterFrame = null; - - this._updateUvs(); - } - - /** - * Resizes the RenderTexture. - * - * @param {number} width - The width to resize to. - * @param {number} height - The height to resize to. - * @param {boolean} [resizeBaseTexture=true] - Should the baseTexture.width and height values be resized as well? - */ - resize(width, height, resizeBaseTexture = true) - { - width = Math.ceil(width); - height = Math.ceil(height); - - // TODO - could be not required.. - this.valid = (width > 0 && height > 0); - - this._frame.width = this.orig.width = width; - this._frame.height = this.orig.height = height; - - if (resizeBaseTexture) - { - this.baseTexture.resize(width, height); - } - - this._updateUvs(); - } - - /** - * A short hand way of creating a render texture. - * - * @param {object} [options] - Options - * @param {number} [options.width=100] - The width of the render texture - * @param {number} [options.height=100] - The height of the render texture - * @param {number} [options.scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values - * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the texture being generated - * @return {PIXI.RenderTexture} The new render texture - */ - static create(options) - { - // fallback, old-style: create(width, height, scaleMode, resolution) - if (typeof options === 'number') - { - /* eslint-disable prefer-rest-params */ - options = { - width: options, - height: arguments[1], - scaleMode: arguments[2], - resolution: arguments[3], - }; - /* eslint-enable prefer-rest-params */ - } - - return new RenderTexture(new BaseRenderTexture(options)); - } -} diff --git a/packages/core/src/textures/Texture.js b/packages/core/src/textures/Texture.js index 5a22225..237dec3 100644 --- a/packages/core/src/textures/Texture.js +++ b/packages/core/src/textures/Texture.js @@ -81,7 +81,7 @@ /** * This is the trimmed area of original texture, before it was put in atlas - * Please call `_updateUvs()` after you change coordinates of `trim` manually. + * Please call `updateUvs()` after you change coordinates of `trim` manually. * * @member {PIXI.Rectangle} */ @@ -102,7 +102,7 @@ this.requiresUpdate = false; /** - * The WebGL UV data cache. + * The WebGL UV data cache. Can be used as quad UV * * @member {PIXI.TextureUvs} * @private @@ -110,6 +110,14 @@ this._uvs = null; /** + * Default TextureMatrix instance for this texture + * By default that object is not created because its heavy + * + * @member {PIXI.TextureMatrix} + */ + this.uvMatrix = null; + + /** * This is the area of original texture, before it was put in atlas * * @member {PIXI.Rectangle} @@ -145,24 +153,16 @@ } /** - * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. + * Update ID is observed by sprites and TextureMatrix instances. + * Call updateUvs() to increment it. * - * @event PIXI.Texture#update - * @protected - * @param {PIXI.Texture} texture - Instance of texture being updated. + * @member {number} + * @private */ this._updateID = 0; /** - * Contains data for uvs. May contain clamp settings and some matrices. - * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} - * @default null - */ - this.transform = null; - - /** * The ids under which this Texture has been added to the texture cache. This is * automatically set as long as Texture.addToCache is used, but may not be set if a * Texture is added directly to the TextureCache array. @@ -255,8 +255,9 @@ /** * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture. + * Call it after changing the frame */ - _updateUvs() + updateUvs() { if (!this._uvs) { @@ -446,7 +447,7 @@ /** * The frame specifies the region of the base texture that this texture uses. - * Please call `_updateUvs()` after you change coordinates of `frame` manually. + * Please call `updateUvs()` after you change coordinates of `frame` manually. * * @member {PIXI.Rectangle} */ @@ -484,7 +485,7 @@ if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } @@ -507,7 +508,7 @@ this._rotate = rotate; if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } diff --git a/packages/core/src/textures/TextureMatrix.js b/packages/core/src/textures/TextureMatrix.js index 2d4ecea..8198ed1 100644 --- a/packages/core/src/textures/TextureMatrix.js +++ b/packages/core/src/textures/TextureMatrix.js @@ -3,12 +3,18 @@ const tempMat = new Matrix(); /** - * Class controls uv transform and frame clamp for texture - * Can be used in Texture "transform" field, or separately, you can use different clamp settings on the same texture. + * Class controls uv mapping from Texture normal space to BaseTexture normal space. + * Takes `trim` and `rotate` into account. + * May contain clamp settings for Meshes and TilingSprite. + * + * Can be used in Texture `uvMatrix` field, or separately, you can use different clamp settings on the same texture. * If you want to add support for texture region of certain feature or filter, that's what you're looking for. * + * Takes track of Texture changes through `_lastTextureID` private field. + * Use `update()` method call to track it from outside. + * * @see PIXI.Texture - * @see PIXI.mesh.Mesh + * @see PIXI.Mesh * @see PIXI.TilingSprite * @class * @memberof PIXI @@ -31,6 +37,10 @@ this.uClampOffset = new Float32Array(2); + /** + * @member {number} Tracks Texture frame changes + * @private + */ this._lastTextureID = -1; /** diff --git a/packages/mesh/src/Mesh.js b/packages/mesh/src/Mesh.js index 55dc092..e7c8f9e 100644 --- a/packages/mesh/src/Mesh.js +++ b/packages/mesh/src/Mesh.js @@ -1,5 +1,5 @@ import RawMesh from './RawMesh'; -import { Geometry, Program, Shader, Texture } from '@pixi/core'; +import { Geometry, Program, Shader, Texture, TextureMatrix } from '@pixi/core'; import { Matrix } from '@pixi/math'; import { BLEND_MODES } from '@pixi/constants'; import { hex2rgb, premultiplyRgba } from '@pixi/utils'; @@ -23,7 +23,7 @@ * @param {Uint16Array} [indices] - if you want to specify the indices * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts */ - constructor(texture, vertices, uvs, indices, drawMode) + constructor(texture = Texture.EMPTY, vertices, uvs, indices, drawMode) { const geometry = new Geometry(); @@ -40,7 +40,7 @@ const uniforms = { uSampler: texture, - uTransform: Matrix.IDENTITY, + uTextureMatrix: Matrix.IDENTITY, alpha: 1, uColor: new Float32Array([1, 1, 1, 1]), }; @@ -70,7 +70,7 @@ * @default PIXI.Texture.EMPTY * @private */ - this.texture = texture || Texture.EMPTY; + this.texture = texture; /** * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any @@ -85,6 +85,31 @@ this.tint = 0xFFFFFF; this.blendMode = BLEND_MODES.NORMAL; + + /** + * TextureMatrix instance for this Mesh, used to track Texture changes + * + * @member {PIXI.TextureMatrix} + * @readonly + */ + this.uvMatrix = new TextureMatrix(this._texture); + + /** + * whether or not upload uvMatrix to shader + * if its false, then uvs should be pre-multiplied + * if you change it for generated mesh, please call 'refresh(true)' + * @member {boolean} + * @default false + */ + this.uploadUvMatrix = false; + + /** + * Uploads vertices buffer every frame, like in PixiJS V3 and V4. + * + * @member {boolean} + * @default true + */ + this.autoUpdate = true; } /** @@ -152,7 +177,7 @@ if (value) { // wait for the texture to load - if (value.baseTexture.hasLoaded) + if (value.baseTexture.valid) { this._onTextureUpdate(); } @@ -177,6 +202,62 @@ */ _onTextureUpdate() { + // constructor texture update stop + if (!this.uvMatrix) + { + return; + } + this.uvMatrix.texture = this._texture; + this.refresh(); + } + + /** + * multiplies uvs only if uploadUvMatrix is false + * call it after you change uvs manually + * make sure that texture is valid + */ + multiplyUvs() + { + if (!this.uploadUvMatrix) + { + this.uvMatrix.multiplyUvs(this.uvs); + } + } + + /** + * Refreshes uvs for generated meshes (rope, plane) + * sometimes refreshes vertices too + * + * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case + */ + refresh(forceUpdate) + { + if (this.uvMatrix.update(forceUpdate)) + { + this._refresh(); + } + } + + /** + * Updates the object transform for rendering + * + * @private + */ + updateTransform() + { + if (this.autoUpdate) + { + this.geometry.buffers[0].update(); + } + this.containerUpdateTransform(); + } + + /** + * re-calculates mesh coords + * @private + */ + _refresh() + { /* empty */ } } diff --git a/packages/mesh/src/MeshRenderer.js b/packages/mesh/src/MeshRenderer.js index ce6dfe6..12b159b 100644 --- a/packages/mesh/src/MeshRenderer.js +++ b/packages/mesh/src/MeshRenderer.js @@ -1,4 +1,5 @@ import { ObjectRenderer } from '@pixi/core'; +import { Matrix } from '@pixi/math'; /** * WebGL renderer plugin for tiling sprites @@ -44,12 +45,23 @@ // TODO // set the shader props.. // probably only need to set once! - // as its then a refference.. + // as its then a reference.. if (mesh.shader.program.uniformData.translationMatrix) { // the transform! mesh.shader.uniforms.translationMatrix = mesh.transform.worldTransform.toArray(true); } + if (mesh.shader.uniforms.uTextureMatrix) + { + if (mesh.uploadUvMatrix) + { + mesh.shader.uniforms.uTextureMatrix = mesh.uvMatrix.mapCoord.toArray(true); + } + else + { + mesh.shader.uniforms.uTextureMatrix = Matrix.IDENTITY.toArray(true); + } + } // bind and sync uniforms.. this.renderer.shader.bind(mesh.shader); diff --git a/packages/mesh/src/NineSlicePlane.js b/packages/mesh/src/NineSlicePlane.js index 44e3498..a392985 100644 --- a/packages/mesh/src/NineSlicePlane.js +++ b/packages/mesh/src/NineSlicePlane.js @@ -133,58 +133,6 @@ } /** - * Renders one segment of the plane. - * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure - * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. - * - * @private - * @param {CanvasRenderingContext2D} context - The context to draw with. - * @param {CanvasImageSource} textureSource - The source to draw. - * @param {number} w - width of the texture - * @param {number} h - height of the texture - * @param {number} x1 - x index 1 - * @param {number} y1 - y index 1 - * @param {number} x2 - x index 2 - * @param {number} y2 - y index 2 - */ - drawSegment(context, textureSource, w, h, x1, y1, x2, y2) - { - // otherwise you get weird results when using slices of that are 0 wide or high. - const uvs = this.uvs; - const vertices = this.vertices; - - let sw = (uvs[x2] - uvs[x1]) * w; - let sh = (uvs[y2] - uvs[y1]) * h; - let dw = vertices[x2] - vertices[x1]; - let dh = vertices[y2] - vertices[y1]; - - // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. - if (sw < 1) - { - sw = 1; - } - - if (sh < 1) - { - sh = 1; - } - - // make sure destination is at least 1 pixel wide and high, otherwise you get - // lines when rendering close to original size. - if (dw < 1) - { - dw = 1; - } - - if (dh < 1) - { - dh = 1; - } - - context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); - } - - /** * The width of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane * * @member {number} @@ -309,7 +257,7 @@ this.updateHorizontalVertices(); this.updateVerticalVertices(); - this.dirty++; + this.geometry.buffers[1].update(); this.multiplyUvs(); } diff --git a/packages/mesh/src/Plane.js b/packages/mesh/src/Plane.js index b874a39..a50b2d7 100644 --- a/packages/mesh/src/Plane.js +++ b/packages/mesh/src/Plane.js @@ -21,61 +21,51 @@ * @param {PIXI.Texture} texture - The texture to use on the Plane. * @param {number} verticesX - The number of vertices in the x-axis * @param {number} verticesY - The number of vertices in the y-axis - * @param {object} opts - an options object - add meshWidth and meshHeight + * @param {object} [opts] - an options object - add meshWidth and meshHeight, temporary object for PixiJS v5 alpha */ constructor(texture, verticesX, verticesY, opts = {}) { super(texture, new Float32Array(1), new Float32Array(1), new Uint16Array(1), 4); - this.segmentsX = this.verticesX = verticesX || 10; - this.segmentsY = this.verticesY = verticesY || 10; + this.verticesX = verticesX || 10; + this.verticesY = verticesY || 10; - this.meshWidth = opts.meshWidth || texture.width; - this.meshHeight = opts.meshHeight || texture.height; + this.meshWidth = opts.meshWidth || 0; + this.meshHeight = opts.meshHeight || 0; this.refresh(); } /** - * Refreshes - * + * Refreshes plane coordinates + * @private */ - refresh() + _refresh() { + const texture = this._texture; const total = this.verticesX * this.verticesY; const verts = []; const uvs = []; const indices = []; - const texture = this.texture; const segmentsX = this.verticesX - 1; const segmentsY = this.verticesY - 1; - const sizeX = this.meshWidth / segmentsX; - const sizeY = this.meshHeight / segmentsY; + const sizeX = (this.meshWidth || texture.width) / segmentsX; + const sizeY = (this.meshHeight || texture.height) / segmentsY; for (let i = 0; i < total; i++) { - if (texture._uvs) - { - const x = (i % this.verticesX); - const y = ((i / this.verticesX) | 0); + const x = (i % this.verticesX); + const y = ((i / this.verticesX) | 0); - verts.push((x * sizeX), - (y * sizeY)); + verts.push(x * sizeX, y * sizeY); - // this works for rectangular textures. - uvs.push( - texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))), - texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1))) - ); - } - else - { - uvs.push(0); - } + uvs.push(x / segmentsX, y / segmentsY); } + // cons + const totalSub = segmentsX * segmentsY; for (let i = 0; i < totalSub; i++) @@ -92,9 +82,6 @@ indices.push(value2, value4, value3); } - this.shader.uniforms.alpha = 1; - this.shader.uniforms.uSampler2 = this.texture; - this.vertices = new Float32Array(verts); this.uvs = new Float32Array(uvs); this.indices = new Uint16Array(indices); @@ -107,16 +94,7 @@ this.geometry.buffers[0].update(); this.geometry.buffers[1].update(); this.geometry.indexBuffer.update(); - } - /** - * Updates the object transform for rendering - * - * @private - */ - updateTransform() - { - this.geometry.buffers[0].update(); - this.containerUpdateTransform(); + this.multiplyUvs(); } } diff --git a/packages/mesh/src/Rope.js b/packages/mesh/src/Rope.js index f88678a..8a409fe 100644 --- a/packages/mesh/src/Rope.js +++ b/packages/mesh/src/Rope.js @@ -1,5 +1,4 @@ import Mesh from './Mesh'; -import { Point } from '@pixi/math'; /** * The rope allows you to draw a texture across several points and them manipulate these points @@ -35,11 +34,11 @@ this.points = points; this.refresh(); } - /** - * Refreshes + * Refreshes Rope indices and uvs + * @private */ - refresh() + _refresh() { const points = this.points; @@ -66,14 +65,10 @@ const uvs = uvBuffer.data; const indices = indexBuffer.data; - const textureUvs = this.texture._uvs; - const offset = new Point(textureUvs.x0, textureUvs.y0); - const factor = new Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0)); - - uvs[0] = 0 + offset.x; - uvs[1] = 0 + offset.y; - uvs[2] = 0 + offset.x; - uvs[3] = factor.y + offset.y; + uvs[0] = 0; + uvs[1] = 0; + uvs[2] = 0; + uvs[3] = 1; indices[0] = 0; indices[1] = 1; @@ -86,11 +81,11 @@ let index = i * 4; const amount = i / (total - 1); - uvs[index] = (amount * factor.x) + offset.x; - uvs[index + 1] = 0 + offset.y; + uvs[index] = amount; + uvs[index + 1] = 0; - uvs[index + 2] = (amount * factor.x) + offset.x; - uvs[index + 3] = factor.y + offset.y; + uvs[index + 2] = amount; + uvs[index + 3] = 1; index = i * 2; indices[index] = index; @@ -98,17 +93,17 @@ } // ensure that the changes are uploaded - vertexBuffer.update(); uvBuffer.update(); indexBuffer.update(); + + this.multiplyUvs(); + this.refreshVertices(); } /** - * Updates the object transform for rendering - * - * @private + * refreshes vertices of Rope mesh */ - updateTransform() + refreshVertices() { const points = this.points; @@ -124,9 +119,7 @@ // this.count -= 0.2; - const vertexBuffer = this.geometry.getAttribute('aVertexPosition'); - const vertices = vertexBuffer.data; - + const vertices = this.vertices; const total = points.length; for (let i = 0; i < total; i++) @@ -152,8 +145,9 @@ { ratio = 1; } + const perpLength = Math.sqrt((perpX * perpX) + (perpY * perpY)); - const num = this.texture.height / 2; // (20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; + const num = this._texture.height / 2; // (20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perpX /= perpLength; perpY /= perpLength; @@ -169,21 +163,20 @@ lastPoint = point; } - // mark the buffer as requiring an upload.. - vertexBuffer.update(); - - this.uniforms.alpha = this.worldAlpha; - - this.containerUpdateTransform(); + this.geometry.buffers[0].update(); } /** - * When the texture is updated, this event will fire to update the scale and frame + * Updates the object transform for rendering * * @private */ - _onTextureUpdate() + updateTransform() { - this.refresh(); + if (this.autoUpdate) + { + this.refreshVertices(); + } + this.containerUpdateTransform(); } } diff --git a/packages/mesh/src/mesh.vert b/packages/mesh/src/mesh.vert index acc096c..0526df9 100644 --- a/packages/mesh/src/mesh.vert +++ b/packages/mesh/src/mesh.vert @@ -3,7 +3,7 @@ uniform mat3 projectionMatrix; uniform mat3 translationMatrix; -uniform mat3 uTransform; +uniform mat3 uTextureMatrix; varying vec2 vTextureCoord; @@ -11,5 +11,5 @@ { gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); - vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy; + vTextureCoord = (uTextureMatrix * vec3(aTextureCoord, 1.0)).xy; } diff --git a/packages/particles/src/ParticleContainer.js b/packages/particles/src/ParticleContainer.js index 3a36e98..22db6f5 100644 --- a/packages/particles/src/ParticleContainer.js +++ b/packages/particles/src/ParticleContainer.js @@ -214,7 +214,7 @@ if (!this.baseTexture) { this.baseTexture = this.children[0]._texture.baseTexture; - if (!this.baseTexture.hasLoaded) + if (!this.baseTexture.valid) { this.baseTexture.once('update', () => this.onChildrenChange(0)); } diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index 2a9eba9..09eaf9c 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -42,7 +42,7 @@ } const base = this._texture.baseTexture; - const textureSource = base.source; + const textureSource = base.getDrawableSource(); const w = base.width * base.resolution; const h = base.height * base.resolution; @@ -56,3 +56,56 @@ this.drawSegment(context, textureSource, w, h, 18, 19, 28, 29); this.drawSegment(context, textureSource, w, h, 20, 21, 30, 31); }; + +/** + * Renders one segment of the plane. + * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure + * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. + * + * @method drawSegment + * @memberof PIXI.NineSlicePlane# + * @param {CanvasRenderingContext2D} context - The context to draw with. + * @param {CanvasImageSource} textureSource - The source to draw. + * @param {number} w - width of the texture + * @param {number} h - height of the texture + * @param {number} x1 - x index 1 + * @param {number} y1 - y index 1 + * @param {number} x2 - x index 2 + * @param {number} y2 - y index 2 + */ +NineSlicePlane.prototype.drawSegment = function drawSegment(context, textureSource, w, h, x1, y1, x2, y2) +{ + // otherwise you get weird results when using slices of that are 0 wide or high. + const uvs = this.uvs; + const vertices = this.vertices; + + let sw = (uvs[x2] - uvs[x1]) * w; + let sh = (uvs[y2] - uvs[y1]) * h; + let dw = vertices[x2] - vertices[x1]; + let dh = vertices[y2] - vertices[y1]; + + // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. + if (sw < 1) + { + sw = 1; + } + + if (sh < 1) + { + sh = 1; + } + + // make sure destination is at least 1 pixel wide and high, otherwise you get + // lines when rendering close to original size. + if (dw < 1) + { + dw = 1; + } + + if (dh < 1) + { + dh = 1; + } + + context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); +}; diff --git a/packages/core/src/renderTexture/RenderTexture.js b/packages/core/src/renderTexture/RenderTexture.js index f1ecf1f..a64b7e7 100644 --- a/packages/core/src/renderTexture/RenderTexture.js +++ b/packages/core/src/renderTexture/RenderTexture.js @@ -94,7 +94,7 @@ */ this.filterFrame = null; - this._updateUvs(); + this.updateUvs(); } /** @@ -120,7 +120,7 @@ this.baseTexture.resize(width, height); } - this._updateUvs(); + this.updateUvs(); } /** diff --git a/packages/core/src/textures/RenderTexture.js b/packages/core/src/textures/RenderTexture.js deleted file mode 100644 index 34f09b2..0000000 --- a/packages/core/src/textures/RenderTexture.js +++ /dev/null @@ -1,153 +0,0 @@ -import BaseRenderTexture from './BaseRenderTexture'; -import Texture from './Texture'; - -/** - * A RenderTexture is a special texture that allows any PixiJS display object to be rendered to it. - * - * __Hint__: All DisplayObjects (i.e. Sprites) that render to a RenderTexture should be preloaded - * otherwise black rectangles will be drawn instead. - * - * A RenderTexture takes a snapshot of any Display Object given to its render method. For example: - * - * ```js - * let renderer = PIXI.autoDetectRenderer(1024, 1024, { view: canvas, ratio: 1 }); - * let renderTexture = PIXI.RenderTexture.create(800, 600); - * let sprite = PIXI.Sprite.fromImage("spinObj_01.png"); - * - * sprite.position.x = 800/2; - * sprite.position.y = 600/2; - * sprite.anchor.x = 0.5; - * sprite.anchor.y = 0.5; - * - * renderer.render(sprite, renderTexture); - * ``` - * - * The Sprite in this case will be rendered using its local transform. To render this sprite at 0,0 - * you can clear the transform - * - * ```js - * - * sprite.setTransform() - * - * let renderTexture = new PIXI.RenderTexture.create(100, 100); - * - * renderer.render(sprite, renderTexture); // Renders to center of RenderTexture - * ``` - * - * @class - * @extends PIXI.Texture - * @memberof PIXI - */ -export default class RenderTexture extends Texture -{ - /** - * @param {PIXI.BaseRenderTexture} baseRenderTexture - The renderer used for this RenderTexture - * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show - */ - constructor(baseRenderTexture, frame) - { - // support for legacy.. - let _legacyRenderer = null; - - if (!(baseRenderTexture instanceof BaseRenderTexture)) - { - /* eslint-disable prefer-rest-params, no-console */ - const width = arguments[1]; - const height = arguments[2]; - const scaleMode = arguments[3]; - const resolution = arguments[4]; - - // we have an old render texture.. - console.warn(`Please use RenderTexture.create(${width}, ${height}) instead of the ctor directly.`); - _legacyRenderer = arguments[0]; - /* eslint-enable prefer-rest-params, no-console */ - - frame = null; - baseRenderTexture = new BaseRenderTexture({ - width, - height, - scaleMode, - resolution, - }); - } - - /** - * The base texture object that this texture uses - * - * @member {BaseTexture} - */ - super(baseRenderTexture, frame); - - this.legacyRenderer = _legacyRenderer; - - /** - * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered. - * - * @member {boolean} - */ - this.valid = true; - - /** - * FilterSystem temporary storage - * @private - * @member {PIXI.Rectangle} - */ - this.filterFrame = null; - - this._updateUvs(); - } - - /** - * Resizes the RenderTexture. - * - * @param {number} width - The width to resize to. - * @param {number} height - The height to resize to. - * @param {boolean} [resizeBaseTexture=true] - Should the baseTexture.width and height values be resized as well? - */ - resize(width, height, resizeBaseTexture = true) - { - width = Math.ceil(width); - height = Math.ceil(height); - - // TODO - could be not required.. - this.valid = (width > 0 && height > 0); - - this._frame.width = this.orig.width = width; - this._frame.height = this.orig.height = height; - - if (resizeBaseTexture) - { - this.baseTexture.resize(width, height); - } - - this._updateUvs(); - } - - /** - * A short hand way of creating a render texture. - * - * @param {object} [options] - Options - * @param {number} [options.width=100] - The width of the render texture - * @param {number} [options.height=100] - The height of the render texture - * @param {number} [options.scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values - * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the texture being generated - * @return {PIXI.RenderTexture} The new render texture - */ - static create(options) - { - // fallback, old-style: create(width, height, scaleMode, resolution) - if (typeof options === 'number') - { - /* eslint-disable prefer-rest-params */ - options = { - width: options, - height: arguments[1], - scaleMode: arguments[2], - resolution: arguments[3], - }; - /* eslint-enable prefer-rest-params */ - } - - return new RenderTexture(new BaseRenderTexture(options)); - } -} diff --git a/packages/core/src/textures/Texture.js b/packages/core/src/textures/Texture.js index 5a22225..237dec3 100644 --- a/packages/core/src/textures/Texture.js +++ b/packages/core/src/textures/Texture.js @@ -81,7 +81,7 @@ /** * This is the trimmed area of original texture, before it was put in atlas - * Please call `_updateUvs()` after you change coordinates of `trim` manually. + * Please call `updateUvs()` after you change coordinates of `trim` manually. * * @member {PIXI.Rectangle} */ @@ -102,7 +102,7 @@ this.requiresUpdate = false; /** - * The WebGL UV data cache. + * The WebGL UV data cache. Can be used as quad UV * * @member {PIXI.TextureUvs} * @private @@ -110,6 +110,14 @@ this._uvs = null; /** + * Default TextureMatrix instance for this texture + * By default that object is not created because its heavy + * + * @member {PIXI.TextureMatrix} + */ + this.uvMatrix = null; + + /** * This is the area of original texture, before it was put in atlas * * @member {PIXI.Rectangle} @@ -145,24 +153,16 @@ } /** - * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. + * Update ID is observed by sprites and TextureMatrix instances. + * Call updateUvs() to increment it. * - * @event PIXI.Texture#update - * @protected - * @param {PIXI.Texture} texture - Instance of texture being updated. + * @member {number} + * @private */ this._updateID = 0; /** - * Contains data for uvs. May contain clamp settings and some matrices. - * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} - * @default null - */ - this.transform = null; - - /** * The ids under which this Texture has been added to the texture cache. This is * automatically set as long as Texture.addToCache is used, but may not be set if a * Texture is added directly to the TextureCache array. @@ -255,8 +255,9 @@ /** * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture. + * Call it after changing the frame */ - _updateUvs() + updateUvs() { if (!this._uvs) { @@ -446,7 +447,7 @@ /** * The frame specifies the region of the base texture that this texture uses. - * Please call `_updateUvs()` after you change coordinates of `frame` manually. + * Please call `updateUvs()` after you change coordinates of `frame` manually. * * @member {PIXI.Rectangle} */ @@ -484,7 +485,7 @@ if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } @@ -507,7 +508,7 @@ this._rotate = rotate; if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } diff --git a/packages/core/src/textures/TextureMatrix.js b/packages/core/src/textures/TextureMatrix.js index 2d4ecea..8198ed1 100644 --- a/packages/core/src/textures/TextureMatrix.js +++ b/packages/core/src/textures/TextureMatrix.js @@ -3,12 +3,18 @@ const tempMat = new Matrix(); /** - * Class controls uv transform and frame clamp for texture - * Can be used in Texture "transform" field, or separately, you can use different clamp settings on the same texture. + * Class controls uv mapping from Texture normal space to BaseTexture normal space. + * Takes `trim` and `rotate` into account. + * May contain clamp settings for Meshes and TilingSprite. + * + * Can be used in Texture `uvMatrix` field, or separately, you can use different clamp settings on the same texture. * If you want to add support for texture region of certain feature or filter, that's what you're looking for. * + * Takes track of Texture changes through `_lastTextureID` private field. + * Use `update()` method call to track it from outside. + * * @see PIXI.Texture - * @see PIXI.mesh.Mesh + * @see PIXI.Mesh * @see PIXI.TilingSprite * @class * @memberof PIXI @@ -31,6 +37,10 @@ this.uClampOffset = new Float32Array(2); + /** + * @member {number} Tracks Texture frame changes + * @private + */ this._lastTextureID = -1; /** diff --git a/packages/mesh/src/Mesh.js b/packages/mesh/src/Mesh.js index 55dc092..e7c8f9e 100644 --- a/packages/mesh/src/Mesh.js +++ b/packages/mesh/src/Mesh.js @@ -1,5 +1,5 @@ import RawMesh from './RawMesh'; -import { Geometry, Program, Shader, Texture } from '@pixi/core'; +import { Geometry, Program, Shader, Texture, TextureMatrix } from '@pixi/core'; import { Matrix } from '@pixi/math'; import { BLEND_MODES } from '@pixi/constants'; import { hex2rgb, premultiplyRgba } from '@pixi/utils'; @@ -23,7 +23,7 @@ * @param {Uint16Array} [indices] - if you want to specify the indices * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts */ - constructor(texture, vertices, uvs, indices, drawMode) + constructor(texture = Texture.EMPTY, vertices, uvs, indices, drawMode) { const geometry = new Geometry(); @@ -40,7 +40,7 @@ const uniforms = { uSampler: texture, - uTransform: Matrix.IDENTITY, + uTextureMatrix: Matrix.IDENTITY, alpha: 1, uColor: new Float32Array([1, 1, 1, 1]), }; @@ -70,7 +70,7 @@ * @default PIXI.Texture.EMPTY * @private */ - this.texture = texture || Texture.EMPTY; + this.texture = texture; /** * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any @@ -85,6 +85,31 @@ this.tint = 0xFFFFFF; this.blendMode = BLEND_MODES.NORMAL; + + /** + * TextureMatrix instance for this Mesh, used to track Texture changes + * + * @member {PIXI.TextureMatrix} + * @readonly + */ + this.uvMatrix = new TextureMatrix(this._texture); + + /** + * whether or not upload uvMatrix to shader + * if its false, then uvs should be pre-multiplied + * if you change it for generated mesh, please call 'refresh(true)' + * @member {boolean} + * @default false + */ + this.uploadUvMatrix = false; + + /** + * Uploads vertices buffer every frame, like in PixiJS V3 and V4. + * + * @member {boolean} + * @default true + */ + this.autoUpdate = true; } /** @@ -152,7 +177,7 @@ if (value) { // wait for the texture to load - if (value.baseTexture.hasLoaded) + if (value.baseTexture.valid) { this._onTextureUpdate(); } @@ -177,6 +202,62 @@ */ _onTextureUpdate() { + // constructor texture update stop + if (!this.uvMatrix) + { + return; + } + this.uvMatrix.texture = this._texture; + this.refresh(); + } + + /** + * multiplies uvs only if uploadUvMatrix is false + * call it after you change uvs manually + * make sure that texture is valid + */ + multiplyUvs() + { + if (!this.uploadUvMatrix) + { + this.uvMatrix.multiplyUvs(this.uvs); + } + } + + /** + * Refreshes uvs for generated meshes (rope, plane) + * sometimes refreshes vertices too + * + * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case + */ + refresh(forceUpdate) + { + if (this.uvMatrix.update(forceUpdate)) + { + this._refresh(); + } + } + + /** + * Updates the object transform for rendering + * + * @private + */ + updateTransform() + { + if (this.autoUpdate) + { + this.geometry.buffers[0].update(); + } + this.containerUpdateTransform(); + } + + /** + * re-calculates mesh coords + * @private + */ + _refresh() + { /* empty */ } } diff --git a/packages/mesh/src/MeshRenderer.js b/packages/mesh/src/MeshRenderer.js index ce6dfe6..12b159b 100644 --- a/packages/mesh/src/MeshRenderer.js +++ b/packages/mesh/src/MeshRenderer.js @@ -1,4 +1,5 @@ import { ObjectRenderer } from '@pixi/core'; +import { Matrix } from '@pixi/math'; /** * WebGL renderer plugin for tiling sprites @@ -44,12 +45,23 @@ // TODO // set the shader props.. // probably only need to set once! - // as its then a refference.. + // as its then a reference.. if (mesh.shader.program.uniformData.translationMatrix) { // the transform! mesh.shader.uniforms.translationMatrix = mesh.transform.worldTransform.toArray(true); } + if (mesh.shader.uniforms.uTextureMatrix) + { + if (mesh.uploadUvMatrix) + { + mesh.shader.uniforms.uTextureMatrix = mesh.uvMatrix.mapCoord.toArray(true); + } + else + { + mesh.shader.uniforms.uTextureMatrix = Matrix.IDENTITY.toArray(true); + } + } // bind and sync uniforms.. this.renderer.shader.bind(mesh.shader); diff --git a/packages/mesh/src/NineSlicePlane.js b/packages/mesh/src/NineSlicePlane.js index 44e3498..a392985 100644 --- a/packages/mesh/src/NineSlicePlane.js +++ b/packages/mesh/src/NineSlicePlane.js @@ -133,58 +133,6 @@ } /** - * Renders one segment of the plane. - * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure - * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. - * - * @private - * @param {CanvasRenderingContext2D} context - The context to draw with. - * @param {CanvasImageSource} textureSource - The source to draw. - * @param {number} w - width of the texture - * @param {number} h - height of the texture - * @param {number} x1 - x index 1 - * @param {number} y1 - y index 1 - * @param {number} x2 - x index 2 - * @param {number} y2 - y index 2 - */ - drawSegment(context, textureSource, w, h, x1, y1, x2, y2) - { - // otherwise you get weird results when using slices of that are 0 wide or high. - const uvs = this.uvs; - const vertices = this.vertices; - - let sw = (uvs[x2] - uvs[x1]) * w; - let sh = (uvs[y2] - uvs[y1]) * h; - let dw = vertices[x2] - vertices[x1]; - let dh = vertices[y2] - vertices[y1]; - - // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. - if (sw < 1) - { - sw = 1; - } - - if (sh < 1) - { - sh = 1; - } - - // make sure destination is at least 1 pixel wide and high, otherwise you get - // lines when rendering close to original size. - if (dw < 1) - { - dw = 1; - } - - if (dh < 1) - { - dh = 1; - } - - context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); - } - - /** * The width of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane * * @member {number} @@ -309,7 +257,7 @@ this.updateHorizontalVertices(); this.updateVerticalVertices(); - this.dirty++; + this.geometry.buffers[1].update(); this.multiplyUvs(); } diff --git a/packages/mesh/src/Plane.js b/packages/mesh/src/Plane.js index b874a39..a50b2d7 100644 --- a/packages/mesh/src/Plane.js +++ b/packages/mesh/src/Plane.js @@ -21,61 +21,51 @@ * @param {PIXI.Texture} texture - The texture to use on the Plane. * @param {number} verticesX - The number of vertices in the x-axis * @param {number} verticesY - The number of vertices in the y-axis - * @param {object} opts - an options object - add meshWidth and meshHeight + * @param {object} [opts] - an options object - add meshWidth and meshHeight, temporary object for PixiJS v5 alpha */ constructor(texture, verticesX, verticesY, opts = {}) { super(texture, new Float32Array(1), new Float32Array(1), new Uint16Array(1), 4); - this.segmentsX = this.verticesX = verticesX || 10; - this.segmentsY = this.verticesY = verticesY || 10; + this.verticesX = verticesX || 10; + this.verticesY = verticesY || 10; - this.meshWidth = opts.meshWidth || texture.width; - this.meshHeight = opts.meshHeight || texture.height; + this.meshWidth = opts.meshWidth || 0; + this.meshHeight = opts.meshHeight || 0; this.refresh(); } /** - * Refreshes - * + * Refreshes plane coordinates + * @private */ - refresh() + _refresh() { + const texture = this._texture; const total = this.verticesX * this.verticesY; const verts = []; const uvs = []; const indices = []; - const texture = this.texture; const segmentsX = this.verticesX - 1; const segmentsY = this.verticesY - 1; - const sizeX = this.meshWidth / segmentsX; - const sizeY = this.meshHeight / segmentsY; + const sizeX = (this.meshWidth || texture.width) / segmentsX; + const sizeY = (this.meshHeight || texture.height) / segmentsY; for (let i = 0; i < total; i++) { - if (texture._uvs) - { - const x = (i % this.verticesX); - const y = ((i / this.verticesX) | 0); + const x = (i % this.verticesX); + const y = ((i / this.verticesX) | 0); - verts.push((x * sizeX), - (y * sizeY)); + verts.push(x * sizeX, y * sizeY); - // this works for rectangular textures. - uvs.push( - texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))), - texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1))) - ); - } - else - { - uvs.push(0); - } + uvs.push(x / segmentsX, y / segmentsY); } + // cons + const totalSub = segmentsX * segmentsY; for (let i = 0; i < totalSub; i++) @@ -92,9 +82,6 @@ indices.push(value2, value4, value3); } - this.shader.uniforms.alpha = 1; - this.shader.uniforms.uSampler2 = this.texture; - this.vertices = new Float32Array(verts); this.uvs = new Float32Array(uvs); this.indices = new Uint16Array(indices); @@ -107,16 +94,7 @@ this.geometry.buffers[0].update(); this.geometry.buffers[1].update(); this.geometry.indexBuffer.update(); - } - /** - * Updates the object transform for rendering - * - * @private - */ - updateTransform() - { - this.geometry.buffers[0].update(); - this.containerUpdateTransform(); + this.multiplyUvs(); } } diff --git a/packages/mesh/src/Rope.js b/packages/mesh/src/Rope.js index f88678a..8a409fe 100644 --- a/packages/mesh/src/Rope.js +++ b/packages/mesh/src/Rope.js @@ -1,5 +1,4 @@ import Mesh from './Mesh'; -import { Point } from '@pixi/math'; /** * The rope allows you to draw a texture across several points and them manipulate these points @@ -35,11 +34,11 @@ this.points = points; this.refresh(); } - /** - * Refreshes + * Refreshes Rope indices and uvs + * @private */ - refresh() + _refresh() { const points = this.points; @@ -66,14 +65,10 @@ const uvs = uvBuffer.data; const indices = indexBuffer.data; - const textureUvs = this.texture._uvs; - const offset = new Point(textureUvs.x0, textureUvs.y0); - const factor = new Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0)); - - uvs[0] = 0 + offset.x; - uvs[1] = 0 + offset.y; - uvs[2] = 0 + offset.x; - uvs[3] = factor.y + offset.y; + uvs[0] = 0; + uvs[1] = 0; + uvs[2] = 0; + uvs[3] = 1; indices[0] = 0; indices[1] = 1; @@ -86,11 +81,11 @@ let index = i * 4; const amount = i / (total - 1); - uvs[index] = (amount * factor.x) + offset.x; - uvs[index + 1] = 0 + offset.y; + uvs[index] = amount; + uvs[index + 1] = 0; - uvs[index + 2] = (amount * factor.x) + offset.x; - uvs[index + 3] = factor.y + offset.y; + uvs[index + 2] = amount; + uvs[index + 3] = 1; index = i * 2; indices[index] = index; @@ -98,17 +93,17 @@ } // ensure that the changes are uploaded - vertexBuffer.update(); uvBuffer.update(); indexBuffer.update(); + + this.multiplyUvs(); + this.refreshVertices(); } /** - * Updates the object transform for rendering - * - * @private + * refreshes vertices of Rope mesh */ - updateTransform() + refreshVertices() { const points = this.points; @@ -124,9 +119,7 @@ // this.count -= 0.2; - const vertexBuffer = this.geometry.getAttribute('aVertexPosition'); - const vertices = vertexBuffer.data; - + const vertices = this.vertices; const total = points.length; for (let i = 0; i < total; i++) @@ -152,8 +145,9 @@ { ratio = 1; } + const perpLength = Math.sqrt((perpX * perpX) + (perpY * perpY)); - const num = this.texture.height / 2; // (20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; + const num = this._texture.height / 2; // (20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perpX /= perpLength; perpY /= perpLength; @@ -169,21 +163,20 @@ lastPoint = point; } - // mark the buffer as requiring an upload.. - vertexBuffer.update(); - - this.uniforms.alpha = this.worldAlpha; - - this.containerUpdateTransform(); + this.geometry.buffers[0].update(); } /** - * When the texture is updated, this event will fire to update the scale and frame + * Updates the object transform for rendering * * @private */ - _onTextureUpdate() + updateTransform() { - this.refresh(); + if (this.autoUpdate) + { + this.refreshVertices(); + } + this.containerUpdateTransform(); } } diff --git a/packages/mesh/src/mesh.vert b/packages/mesh/src/mesh.vert index acc096c..0526df9 100644 --- a/packages/mesh/src/mesh.vert +++ b/packages/mesh/src/mesh.vert @@ -3,7 +3,7 @@ uniform mat3 projectionMatrix; uniform mat3 translationMatrix; -uniform mat3 uTransform; +uniform mat3 uTextureMatrix; varying vec2 vTextureCoord; @@ -11,5 +11,5 @@ { gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); - vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy; + vTextureCoord = (uTextureMatrix * vec3(aTextureCoord, 1.0)).xy; } diff --git a/packages/particles/src/ParticleContainer.js b/packages/particles/src/ParticleContainer.js index 3a36e98..22db6f5 100644 --- a/packages/particles/src/ParticleContainer.js +++ b/packages/particles/src/ParticleContainer.js @@ -214,7 +214,7 @@ if (!this.baseTexture) { this.baseTexture = this.children[0]._texture.baseTexture; - if (!this.baseTexture.hasLoaded) + if (!this.baseTexture.valid) { this.baseTexture.once('update', () => this.onChildrenChange(0)); } diff --git a/packages/sprite-tiling/src/TilingSprite.js b/packages/sprite-tiling/src/TilingSprite.js index dc8dab6..b3cba22 100644 --- a/packages/sprite-tiling/src/TilingSprite.js +++ b/packages/sprite-tiling/src/TilingSprite.js @@ -57,11 +57,11 @@ this._canvasPattern = null; /** - * transform that is applied to UV to get the texture coords + * matrix that is applied to UV to get the coords in Texture normalized space to coords in BaseTexture space * * @member {PIXI.TextureMatrix} */ - this.uvTransform = texture.transform || new TextureMatrix(texture); + this.uvMatrix = texture.uvMatrix || new TextureMatrix(texture); /** * Plugin that is responsible for rendering this element. @@ -89,13 +89,13 @@ */ get clampMargin() { - return this.uvTransform.clampMargin; + return this.uvMatrix.clampMargin; } set clampMargin(value) // eslint-disable-line require-jsdoc { - this.uvTransform.clampMargin = value; - this.uvTransform.update(true); + this.uvMatrix.clampMargin = value; + this.uvMatrix.update(true); } /** @@ -133,9 +133,9 @@ */ _onTextureUpdate() { - if (this.uvTransform) + if (this.uvMatrix) { - this.uvTransform.texture = this._texture; + this.uvMatrix.texture = this._texture; } this.cachedTint = 0xFFFFFF; } @@ -157,7 +157,7 @@ } this.tileTransform.updateLocalTransform(); - this.uvTransform.update(); + this.uvMatrix.update(); renderer.batch.setObjectRenderer(renderer.plugins[this.pluginName]); renderer.plugins[this.pluginName].render(this); @@ -252,7 +252,7 @@ super.destroy(options); this.tileTransform = null; - this.uvTransform = null; + this.uvMatrix = null; } /** diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index 2a9eba9..09eaf9c 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -42,7 +42,7 @@ } const base = this._texture.baseTexture; - const textureSource = base.source; + const textureSource = base.getDrawableSource(); const w = base.width * base.resolution; const h = base.height * base.resolution; @@ -56,3 +56,56 @@ this.drawSegment(context, textureSource, w, h, 18, 19, 28, 29); this.drawSegment(context, textureSource, w, h, 20, 21, 30, 31); }; + +/** + * Renders one segment of the plane. + * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure + * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. + * + * @method drawSegment + * @memberof PIXI.NineSlicePlane# + * @param {CanvasRenderingContext2D} context - The context to draw with. + * @param {CanvasImageSource} textureSource - The source to draw. + * @param {number} w - width of the texture + * @param {number} h - height of the texture + * @param {number} x1 - x index 1 + * @param {number} y1 - y index 1 + * @param {number} x2 - x index 2 + * @param {number} y2 - y index 2 + */ +NineSlicePlane.prototype.drawSegment = function drawSegment(context, textureSource, w, h, x1, y1, x2, y2) +{ + // otherwise you get weird results when using slices of that are 0 wide or high. + const uvs = this.uvs; + const vertices = this.vertices; + + let sw = (uvs[x2] - uvs[x1]) * w; + let sh = (uvs[y2] - uvs[y1]) * h; + let dw = vertices[x2] - vertices[x1]; + let dh = vertices[y2] - vertices[y1]; + + // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. + if (sw < 1) + { + sw = 1; + } + + if (sh < 1) + { + sh = 1; + } + + // make sure destination is at least 1 pixel wide and high, otherwise you get + // lines when rendering close to original size. + if (dw < 1) + { + dw = 1; + } + + if (dh < 1) + { + dh = 1; + } + + context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); +}; diff --git a/packages/core/src/renderTexture/RenderTexture.js b/packages/core/src/renderTexture/RenderTexture.js index f1ecf1f..a64b7e7 100644 --- a/packages/core/src/renderTexture/RenderTexture.js +++ b/packages/core/src/renderTexture/RenderTexture.js @@ -94,7 +94,7 @@ */ this.filterFrame = null; - this._updateUvs(); + this.updateUvs(); } /** @@ -120,7 +120,7 @@ this.baseTexture.resize(width, height); } - this._updateUvs(); + this.updateUvs(); } /** diff --git a/packages/core/src/textures/RenderTexture.js b/packages/core/src/textures/RenderTexture.js deleted file mode 100644 index 34f09b2..0000000 --- a/packages/core/src/textures/RenderTexture.js +++ /dev/null @@ -1,153 +0,0 @@ -import BaseRenderTexture from './BaseRenderTexture'; -import Texture from './Texture'; - -/** - * A RenderTexture is a special texture that allows any PixiJS display object to be rendered to it. - * - * __Hint__: All DisplayObjects (i.e. Sprites) that render to a RenderTexture should be preloaded - * otherwise black rectangles will be drawn instead. - * - * A RenderTexture takes a snapshot of any Display Object given to its render method. For example: - * - * ```js - * let renderer = PIXI.autoDetectRenderer(1024, 1024, { view: canvas, ratio: 1 }); - * let renderTexture = PIXI.RenderTexture.create(800, 600); - * let sprite = PIXI.Sprite.fromImage("spinObj_01.png"); - * - * sprite.position.x = 800/2; - * sprite.position.y = 600/2; - * sprite.anchor.x = 0.5; - * sprite.anchor.y = 0.5; - * - * renderer.render(sprite, renderTexture); - * ``` - * - * The Sprite in this case will be rendered using its local transform. To render this sprite at 0,0 - * you can clear the transform - * - * ```js - * - * sprite.setTransform() - * - * let renderTexture = new PIXI.RenderTexture.create(100, 100); - * - * renderer.render(sprite, renderTexture); // Renders to center of RenderTexture - * ``` - * - * @class - * @extends PIXI.Texture - * @memberof PIXI - */ -export default class RenderTexture extends Texture -{ - /** - * @param {PIXI.BaseRenderTexture} baseRenderTexture - The renderer used for this RenderTexture - * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show - */ - constructor(baseRenderTexture, frame) - { - // support for legacy.. - let _legacyRenderer = null; - - if (!(baseRenderTexture instanceof BaseRenderTexture)) - { - /* eslint-disable prefer-rest-params, no-console */ - const width = arguments[1]; - const height = arguments[2]; - const scaleMode = arguments[3]; - const resolution = arguments[4]; - - // we have an old render texture.. - console.warn(`Please use RenderTexture.create(${width}, ${height}) instead of the ctor directly.`); - _legacyRenderer = arguments[0]; - /* eslint-enable prefer-rest-params, no-console */ - - frame = null; - baseRenderTexture = new BaseRenderTexture({ - width, - height, - scaleMode, - resolution, - }); - } - - /** - * The base texture object that this texture uses - * - * @member {BaseTexture} - */ - super(baseRenderTexture, frame); - - this.legacyRenderer = _legacyRenderer; - - /** - * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered. - * - * @member {boolean} - */ - this.valid = true; - - /** - * FilterSystem temporary storage - * @private - * @member {PIXI.Rectangle} - */ - this.filterFrame = null; - - this._updateUvs(); - } - - /** - * Resizes the RenderTexture. - * - * @param {number} width - The width to resize to. - * @param {number} height - The height to resize to. - * @param {boolean} [resizeBaseTexture=true] - Should the baseTexture.width and height values be resized as well? - */ - resize(width, height, resizeBaseTexture = true) - { - width = Math.ceil(width); - height = Math.ceil(height); - - // TODO - could be not required.. - this.valid = (width > 0 && height > 0); - - this._frame.width = this.orig.width = width; - this._frame.height = this.orig.height = height; - - if (resizeBaseTexture) - { - this.baseTexture.resize(width, height); - } - - this._updateUvs(); - } - - /** - * A short hand way of creating a render texture. - * - * @param {object} [options] - Options - * @param {number} [options.width=100] - The width of the render texture - * @param {number} [options.height=100] - The height of the render texture - * @param {number} [options.scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values - * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the texture being generated - * @return {PIXI.RenderTexture} The new render texture - */ - static create(options) - { - // fallback, old-style: create(width, height, scaleMode, resolution) - if (typeof options === 'number') - { - /* eslint-disable prefer-rest-params */ - options = { - width: options, - height: arguments[1], - scaleMode: arguments[2], - resolution: arguments[3], - }; - /* eslint-enable prefer-rest-params */ - } - - return new RenderTexture(new BaseRenderTexture(options)); - } -} diff --git a/packages/core/src/textures/Texture.js b/packages/core/src/textures/Texture.js index 5a22225..237dec3 100644 --- a/packages/core/src/textures/Texture.js +++ b/packages/core/src/textures/Texture.js @@ -81,7 +81,7 @@ /** * This is the trimmed area of original texture, before it was put in atlas - * Please call `_updateUvs()` after you change coordinates of `trim` manually. + * Please call `updateUvs()` after you change coordinates of `trim` manually. * * @member {PIXI.Rectangle} */ @@ -102,7 +102,7 @@ this.requiresUpdate = false; /** - * The WebGL UV data cache. + * The WebGL UV data cache. Can be used as quad UV * * @member {PIXI.TextureUvs} * @private @@ -110,6 +110,14 @@ this._uvs = null; /** + * Default TextureMatrix instance for this texture + * By default that object is not created because its heavy + * + * @member {PIXI.TextureMatrix} + */ + this.uvMatrix = null; + + /** * This is the area of original texture, before it was put in atlas * * @member {PIXI.Rectangle} @@ -145,24 +153,16 @@ } /** - * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. + * Update ID is observed by sprites and TextureMatrix instances. + * Call updateUvs() to increment it. * - * @event PIXI.Texture#update - * @protected - * @param {PIXI.Texture} texture - Instance of texture being updated. + * @member {number} + * @private */ this._updateID = 0; /** - * Contains data for uvs. May contain clamp settings and some matrices. - * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} - * @default null - */ - this.transform = null; - - /** * The ids under which this Texture has been added to the texture cache. This is * automatically set as long as Texture.addToCache is used, but may not be set if a * Texture is added directly to the TextureCache array. @@ -255,8 +255,9 @@ /** * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture. + * Call it after changing the frame */ - _updateUvs() + updateUvs() { if (!this._uvs) { @@ -446,7 +447,7 @@ /** * The frame specifies the region of the base texture that this texture uses. - * Please call `_updateUvs()` after you change coordinates of `frame` manually. + * Please call `updateUvs()` after you change coordinates of `frame` manually. * * @member {PIXI.Rectangle} */ @@ -484,7 +485,7 @@ if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } @@ -507,7 +508,7 @@ this._rotate = rotate; if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } diff --git a/packages/core/src/textures/TextureMatrix.js b/packages/core/src/textures/TextureMatrix.js index 2d4ecea..8198ed1 100644 --- a/packages/core/src/textures/TextureMatrix.js +++ b/packages/core/src/textures/TextureMatrix.js @@ -3,12 +3,18 @@ const tempMat = new Matrix(); /** - * Class controls uv transform and frame clamp for texture - * Can be used in Texture "transform" field, or separately, you can use different clamp settings on the same texture. + * Class controls uv mapping from Texture normal space to BaseTexture normal space. + * Takes `trim` and `rotate` into account. + * May contain clamp settings for Meshes and TilingSprite. + * + * Can be used in Texture `uvMatrix` field, or separately, you can use different clamp settings on the same texture. * If you want to add support for texture region of certain feature or filter, that's what you're looking for. * + * Takes track of Texture changes through `_lastTextureID` private field. + * Use `update()` method call to track it from outside. + * * @see PIXI.Texture - * @see PIXI.mesh.Mesh + * @see PIXI.Mesh * @see PIXI.TilingSprite * @class * @memberof PIXI @@ -31,6 +37,10 @@ this.uClampOffset = new Float32Array(2); + /** + * @member {number} Tracks Texture frame changes + * @private + */ this._lastTextureID = -1; /** diff --git a/packages/mesh/src/Mesh.js b/packages/mesh/src/Mesh.js index 55dc092..e7c8f9e 100644 --- a/packages/mesh/src/Mesh.js +++ b/packages/mesh/src/Mesh.js @@ -1,5 +1,5 @@ import RawMesh from './RawMesh'; -import { Geometry, Program, Shader, Texture } from '@pixi/core'; +import { Geometry, Program, Shader, Texture, TextureMatrix } from '@pixi/core'; import { Matrix } from '@pixi/math'; import { BLEND_MODES } from '@pixi/constants'; import { hex2rgb, premultiplyRgba } from '@pixi/utils'; @@ -23,7 +23,7 @@ * @param {Uint16Array} [indices] - if you want to specify the indices * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts */ - constructor(texture, vertices, uvs, indices, drawMode) + constructor(texture = Texture.EMPTY, vertices, uvs, indices, drawMode) { const geometry = new Geometry(); @@ -40,7 +40,7 @@ const uniforms = { uSampler: texture, - uTransform: Matrix.IDENTITY, + uTextureMatrix: Matrix.IDENTITY, alpha: 1, uColor: new Float32Array([1, 1, 1, 1]), }; @@ -70,7 +70,7 @@ * @default PIXI.Texture.EMPTY * @private */ - this.texture = texture || Texture.EMPTY; + this.texture = texture; /** * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any @@ -85,6 +85,31 @@ this.tint = 0xFFFFFF; this.blendMode = BLEND_MODES.NORMAL; + + /** + * TextureMatrix instance for this Mesh, used to track Texture changes + * + * @member {PIXI.TextureMatrix} + * @readonly + */ + this.uvMatrix = new TextureMatrix(this._texture); + + /** + * whether or not upload uvMatrix to shader + * if its false, then uvs should be pre-multiplied + * if you change it for generated mesh, please call 'refresh(true)' + * @member {boolean} + * @default false + */ + this.uploadUvMatrix = false; + + /** + * Uploads vertices buffer every frame, like in PixiJS V3 and V4. + * + * @member {boolean} + * @default true + */ + this.autoUpdate = true; } /** @@ -152,7 +177,7 @@ if (value) { // wait for the texture to load - if (value.baseTexture.hasLoaded) + if (value.baseTexture.valid) { this._onTextureUpdate(); } @@ -177,6 +202,62 @@ */ _onTextureUpdate() { + // constructor texture update stop + if (!this.uvMatrix) + { + return; + } + this.uvMatrix.texture = this._texture; + this.refresh(); + } + + /** + * multiplies uvs only if uploadUvMatrix is false + * call it after you change uvs manually + * make sure that texture is valid + */ + multiplyUvs() + { + if (!this.uploadUvMatrix) + { + this.uvMatrix.multiplyUvs(this.uvs); + } + } + + /** + * Refreshes uvs for generated meshes (rope, plane) + * sometimes refreshes vertices too + * + * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case + */ + refresh(forceUpdate) + { + if (this.uvMatrix.update(forceUpdate)) + { + this._refresh(); + } + } + + /** + * Updates the object transform for rendering + * + * @private + */ + updateTransform() + { + if (this.autoUpdate) + { + this.geometry.buffers[0].update(); + } + this.containerUpdateTransform(); + } + + /** + * re-calculates mesh coords + * @private + */ + _refresh() + { /* empty */ } } diff --git a/packages/mesh/src/MeshRenderer.js b/packages/mesh/src/MeshRenderer.js index ce6dfe6..12b159b 100644 --- a/packages/mesh/src/MeshRenderer.js +++ b/packages/mesh/src/MeshRenderer.js @@ -1,4 +1,5 @@ import { ObjectRenderer } from '@pixi/core'; +import { Matrix } from '@pixi/math'; /** * WebGL renderer plugin for tiling sprites @@ -44,12 +45,23 @@ // TODO // set the shader props.. // probably only need to set once! - // as its then a refference.. + // as its then a reference.. if (mesh.shader.program.uniformData.translationMatrix) { // the transform! mesh.shader.uniforms.translationMatrix = mesh.transform.worldTransform.toArray(true); } + if (mesh.shader.uniforms.uTextureMatrix) + { + if (mesh.uploadUvMatrix) + { + mesh.shader.uniforms.uTextureMatrix = mesh.uvMatrix.mapCoord.toArray(true); + } + else + { + mesh.shader.uniforms.uTextureMatrix = Matrix.IDENTITY.toArray(true); + } + } // bind and sync uniforms.. this.renderer.shader.bind(mesh.shader); diff --git a/packages/mesh/src/NineSlicePlane.js b/packages/mesh/src/NineSlicePlane.js index 44e3498..a392985 100644 --- a/packages/mesh/src/NineSlicePlane.js +++ b/packages/mesh/src/NineSlicePlane.js @@ -133,58 +133,6 @@ } /** - * Renders one segment of the plane. - * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure - * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. - * - * @private - * @param {CanvasRenderingContext2D} context - The context to draw with. - * @param {CanvasImageSource} textureSource - The source to draw. - * @param {number} w - width of the texture - * @param {number} h - height of the texture - * @param {number} x1 - x index 1 - * @param {number} y1 - y index 1 - * @param {number} x2 - x index 2 - * @param {number} y2 - y index 2 - */ - drawSegment(context, textureSource, w, h, x1, y1, x2, y2) - { - // otherwise you get weird results when using slices of that are 0 wide or high. - const uvs = this.uvs; - const vertices = this.vertices; - - let sw = (uvs[x2] - uvs[x1]) * w; - let sh = (uvs[y2] - uvs[y1]) * h; - let dw = vertices[x2] - vertices[x1]; - let dh = vertices[y2] - vertices[y1]; - - // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. - if (sw < 1) - { - sw = 1; - } - - if (sh < 1) - { - sh = 1; - } - - // make sure destination is at least 1 pixel wide and high, otherwise you get - // lines when rendering close to original size. - if (dw < 1) - { - dw = 1; - } - - if (dh < 1) - { - dh = 1; - } - - context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); - } - - /** * The width of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane * * @member {number} @@ -309,7 +257,7 @@ this.updateHorizontalVertices(); this.updateVerticalVertices(); - this.dirty++; + this.geometry.buffers[1].update(); this.multiplyUvs(); } diff --git a/packages/mesh/src/Plane.js b/packages/mesh/src/Plane.js index b874a39..a50b2d7 100644 --- a/packages/mesh/src/Plane.js +++ b/packages/mesh/src/Plane.js @@ -21,61 +21,51 @@ * @param {PIXI.Texture} texture - The texture to use on the Plane. * @param {number} verticesX - The number of vertices in the x-axis * @param {number} verticesY - The number of vertices in the y-axis - * @param {object} opts - an options object - add meshWidth and meshHeight + * @param {object} [opts] - an options object - add meshWidth and meshHeight, temporary object for PixiJS v5 alpha */ constructor(texture, verticesX, verticesY, opts = {}) { super(texture, new Float32Array(1), new Float32Array(1), new Uint16Array(1), 4); - this.segmentsX = this.verticesX = verticesX || 10; - this.segmentsY = this.verticesY = verticesY || 10; + this.verticesX = verticesX || 10; + this.verticesY = verticesY || 10; - this.meshWidth = opts.meshWidth || texture.width; - this.meshHeight = opts.meshHeight || texture.height; + this.meshWidth = opts.meshWidth || 0; + this.meshHeight = opts.meshHeight || 0; this.refresh(); } /** - * Refreshes - * + * Refreshes plane coordinates + * @private */ - refresh() + _refresh() { + const texture = this._texture; const total = this.verticesX * this.verticesY; const verts = []; const uvs = []; const indices = []; - const texture = this.texture; const segmentsX = this.verticesX - 1; const segmentsY = this.verticesY - 1; - const sizeX = this.meshWidth / segmentsX; - const sizeY = this.meshHeight / segmentsY; + const sizeX = (this.meshWidth || texture.width) / segmentsX; + const sizeY = (this.meshHeight || texture.height) / segmentsY; for (let i = 0; i < total; i++) { - if (texture._uvs) - { - const x = (i % this.verticesX); - const y = ((i / this.verticesX) | 0); + const x = (i % this.verticesX); + const y = ((i / this.verticesX) | 0); - verts.push((x * sizeX), - (y * sizeY)); + verts.push(x * sizeX, y * sizeY); - // this works for rectangular textures. - uvs.push( - texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))), - texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1))) - ); - } - else - { - uvs.push(0); - } + uvs.push(x / segmentsX, y / segmentsY); } + // cons + const totalSub = segmentsX * segmentsY; for (let i = 0; i < totalSub; i++) @@ -92,9 +82,6 @@ indices.push(value2, value4, value3); } - this.shader.uniforms.alpha = 1; - this.shader.uniforms.uSampler2 = this.texture; - this.vertices = new Float32Array(verts); this.uvs = new Float32Array(uvs); this.indices = new Uint16Array(indices); @@ -107,16 +94,7 @@ this.geometry.buffers[0].update(); this.geometry.buffers[1].update(); this.geometry.indexBuffer.update(); - } - /** - * Updates the object transform for rendering - * - * @private - */ - updateTransform() - { - this.geometry.buffers[0].update(); - this.containerUpdateTransform(); + this.multiplyUvs(); } } diff --git a/packages/mesh/src/Rope.js b/packages/mesh/src/Rope.js index f88678a..8a409fe 100644 --- a/packages/mesh/src/Rope.js +++ b/packages/mesh/src/Rope.js @@ -1,5 +1,4 @@ import Mesh from './Mesh'; -import { Point } from '@pixi/math'; /** * The rope allows you to draw a texture across several points and them manipulate these points @@ -35,11 +34,11 @@ this.points = points; this.refresh(); } - /** - * Refreshes + * Refreshes Rope indices and uvs + * @private */ - refresh() + _refresh() { const points = this.points; @@ -66,14 +65,10 @@ const uvs = uvBuffer.data; const indices = indexBuffer.data; - const textureUvs = this.texture._uvs; - const offset = new Point(textureUvs.x0, textureUvs.y0); - const factor = new Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0)); - - uvs[0] = 0 + offset.x; - uvs[1] = 0 + offset.y; - uvs[2] = 0 + offset.x; - uvs[3] = factor.y + offset.y; + uvs[0] = 0; + uvs[1] = 0; + uvs[2] = 0; + uvs[3] = 1; indices[0] = 0; indices[1] = 1; @@ -86,11 +81,11 @@ let index = i * 4; const amount = i / (total - 1); - uvs[index] = (amount * factor.x) + offset.x; - uvs[index + 1] = 0 + offset.y; + uvs[index] = amount; + uvs[index + 1] = 0; - uvs[index + 2] = (amount * factor.x) + offset.x; - uvs[index + 3] = factor.y + offset.y; + uvs[index + 2] = amount; + uvs[index + 3] = 1; index = i * 2; indices[index] = index; @@ -98,17 +93,17 @@ } // ensure that the changes are uploaded - vertexBuffer.update(); uvBuffer.update(); indexBuffer.update(); + + this.multiplyUvs(); + this.refreshVertices(); } /** - * Updates the object transform for rendering - * - * @private + * refreshes vertices of Rope mesh */ - updateTransform() + refreshVertices() { const points = this.points; @@ -124,9 +119,7 @@ // this.count -= 0.2; - const vertexBuffer = this.geometry.getAttribute('aVertexPosition'); - const vertices = vertexBuffer.data; - + const vertices = this.vertices; const total = points.length; for (let i = 0; i < total; i++) @@ -152,8 +145,9 @@ { ratio = 1; } + const perpLength = Math.sqrt((perpX * perpX) + (perpY * perpY)); - const num = this.texture.height / 2; // (20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; + const num = this._texture.height / 2; // (20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perpX /= perpLength; perpY /= perpLength; @@ -169,21 +163,20 @@ lastPoint = point; } - // mark the buffer as requiring an upload.. - vertexBuffer.update(); - - this.uniforms.alpha = this.worldAlpha; - - this.containerUpdateTransform(); + this.geometry.buffers[0].update(); } /** - * When the texture is updated, this event will fire to update the scale and frame + * Updates the object transform for rendering * * @private */ - _onTextureUpdate() + updateTransform() { - this.refresh(); + if (this.autoUpdate) + { + this.refreshVertices(); + } + this.containerUpdateTransform(); } } diff --git a/packages/mesh/src/mesh.vert b/packages/mesh/src/mesh.vert index acc096c..0526df9 100644 --- a/packages/mesh/src/mesh.vert +++ b/packages/mesh/src/mesh.vert @@ -3,7 +3,7 @@ uniform mat3 projectionMatrix; uniform mat3 translationMatrix; -uniform mat3 uTransform; +uniform mat3 uTextureMatrix; varying vec2 vTextureCoord; @@ -11,5 +11,5 @@ { gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); - vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy; + vTextureCoord = (uTextureMatrix * vec3(aTextureCoord, 1.0)).xy; } diff --git a/packages/particles/src/ParticleContainer.js b/packages/particles/src/ParticleContainer.js index 3a36e98..22db6f5 100644 --- a/packages/particles/src/ParticleContainer.js +++ b/packages/particles/src/ParticleContainer.js @@ -214,7 +214,7 @@ if (!this.baseTexture) { this.baseTexture = this.children[0]._texture.baseTexture; - if (!this.baseTexture.hasLoaded) + if (!this.baseTexture.valid) { this.baseTexture.once('update', () => this.onChildrenChange(0)); } diff --git a/packages/sprite-tiling/src/TilingSprite.js b/packages/sprite-tiling/src/TilingSprite.js index dc8dab6..b3cba22 100644 --- a/packages/sprite-tiling/src/TilingSprite.js +++ b/packages/sprite-tiling/src/TilingSprite.js @@ -57,11 +57,11 @@ this._canvasPattern = null; /** - * transform that is applied to UV to get the texture coords + * matrix that is applied to UV to get the coords in Texture normalized space to coords in BaseTexture space * * @member {PIXI.TextureMatrix} */ - this.uvTransform = texture.transform || new TextureMatrix(texture); + this.uvMatrix = texture.uvMatrix || new TextureMatrix(texture); /** * Plugin that is responsible for rendering this element. @@ -89,13 +89,13 @@ */ get clampMargin() { - return this.uvTransform.clampMargin; + return this.uvMatrix.clampMargin; } set clampMargin(value) // eslint-disable-line require-jsdoc { - this.uvTransform.clampMargin = value; - this.uvTransform.update(true); + this.uvMatrix.clampMargin = value; + this.uvMatrix.update(true); } /** @@ -133,9 +133,9 @@ */ _onTextureUpdate() { - if (this.uvTransform) + if (this.uvMatrix) { - this.uvTransform.texture = this._texture; + this.uvMatrix.texture = this._texture; } this.cachedTint = 0xFFFFFF; } @@ -157,7 +157,7 @@ } this.tileTransform.updateLocalTransform(); - this.uvTransform.update(); + this.uvMatrix.update(); renderer.batch.setObjectRenderer(renderer.plugins[this.pluginName]); renderer.plugins[this.pluginName].render(this); @@ -252,7 +252,7 @@ super.destroy(options); this.tileTransform = null; - this.uvTransform = null; + this.uvMatrix = null; } /** diff --git a/packages/sprite-tiling/src/TilingSpriteRenderer.js b/packages/sprite-tiling/src/TilingSpriteRenderer.js index 8b79e13..dca7270 100644 --- a/packages/sprite-tiling/src/TilingSpriteRenderer.js +++ b/packages/sprite-tiling/src/TilingSpriteRenderer.js @@ -69,7 +69,7 @@ const tex = ts._texture; const baseTex = tex.baseTexture; const lt = ts.tileTransform.localTransform; - const uv = ts.uvTransform; + const uv = ts.uvMatrix; let isSimple = baseTex.isPowerOfTwo && tex.frame.width === baseTex.width && tex.frame.height === baseTex.height; diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index 2a9eba9..09eaf9c 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -42,7 +42,7 @@ } const base = this._texture.baseTexture; - const textureSource = base.source; + const textureSource = base.getDrawableSource(); const w = base.width * base.resolution; const h = base.height * base.resolution; @@ -56,3 +56,56 @@ this.drawSegment(context, textureSource, w, h, 18, 19, 28, 29); this.drawSegment(context, textureSource, w, h, 20, 21, 30, 31); }; + +/** + * Renders one segment of the plane. + * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure + * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. + * + * @method drawSegment + * @memberof PIXI.NineSlicePlane# + * @param {CanvasRenderingContext2D} context - The context to draw with. + * @param {CanvasImageSource} textureSource - The source to draw. + * @param {number} w - width of the texture + * @param {number} h - height of the texture + * @param {number} x1 - x index 1 + * @param {number} y1 - y index 1 + * @param {number} x2 - x index 2 + * @param {number} y2 - y index 2 + */ +NineSlicePlane.prototype.drawSegment = function drawSegment(context, textureSource, w, h, x1, y1, x2, y2) +{ + // otherwise you get weird results when using slices of that are 0 wide or high. + const uvs = this.uvs; + const vertices = this.vertices; + + let sw = (uvs[x2] - uvs[x1]) * w; + let sh = (uvs[y2] - uvs[y1]) * h; + let dw = vertices[x2] - vertices[x1]; + let dh = vertices[y2] - vertices[y1]; + + // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. + if (sw < 1) + { + sw = 1; + } + + if (sh < 1) + { + sh = 1; + } + + // make sure destination is at least 1 pixel wide and high, otherwise you get + // lines when rendering close to original size. + if (dw < 1) + { + dw = 1; + } + + if (dh < 1) + { + dh = 1; + } + + context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); +}; diff --git a/packages/core/src/renderTexture/RenderTexture.js b/packages/core/src/renderTexture/RenderTexture.js index f1ecf1f..a64b7e7 100644 --- a/packages/core/src/renderTexture/RenderTexture.js +++ b/packages/core/src/renderTexture/RenderTexture.js @@ -94,7 +94,7 @@ */ this.filterFrame = null; - this._updateUvs(); + this.updateUvs(); } /** @@ -120,7 +120,7 @@ this.baseTexture.resize(width, height); } - this._updateUvs(); + this.updateUvs(); } /** diff --git a/packages/core/src/textures/RenderTexture.js b/packages/core/src/textures/RenderTexture.js deleted file mode 100644 index 34f09b2..0000000 --- a/packages/core/src/textures/RenderTexture.js +++ /dev/null @@ -1,153 +0,0 @@ -import BaseRenderTexture from './BaseRenderTexture'; -import Texture from './Texture'; - -/** - * A RenderTexture is a special texture that allows any PixiJS display object to be rendered to it. - * - * __Hint__: All DisplayObjects (i.e. Sprites) that render to a RenderTexture should be preloaded - * otherwise black rectangles will be drawn instead. - * - * A RenderTexture takes a snapshot of any Display Object given to its render method. For example: - * - * ```js - * let renderer = PIXI.autoDetectRenderer(1024, 1024, { view: canvas, ratio: 1 }); - * let renderTexture = PIXI.RenderTexture.create(800, 600); - * let sprite = PIXI.Sprite.fromImage("spinObj_01.png"); - * - * sprite.position.x = 800/2; - * sprite.position.y = 600/2; - * sprite.anchor.x = 0.5; - * sprite.anchor.y = 0.5; - * - * renderer.render(sprite, renderTexture); - * ``` - * - * The Sprite in this case will be rendered using its local transform. To render this sprite at 0,0 - * you can clear the transform - * - * ```js - * - * sprite.setTransform() - * - * let renderTexture = new PIXI.RenderTexture.create(100, 100); - * - * renderer.render(sprite, renderTexture); // Renders to center of RenderTexture - * ``` - * - * @class - * @extends PIXI.Texture - * @memberof PIXI - */ -export default class RenderTexture extends Texture -{ - /** - * @param {PIXI.BaseRenderTexture} baseRenderTexture - The renderer used for this RenderTexture - * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show - */ - constructor(baseRenderTexture, frame) - { - // support for legacy.. - let _legacyRenderer = null; - - if (!(baseRenderTexture instanceof BaseRenderTexture)) - { - /* eslint-disable prefer-rest-params, no-console */ - const width = arguments[1]; - const height = arguments[2]; - const scaleMode = arguments[3]; - const resolution = arguments[4]; - - // we have an old render texture.. - console.warn(`Please use RenderTexture.create(${width}, ${height}) instead of the ctor directly.`); - _legacyRenderer = arguments[0]; - /* eslint-enable prefer-rest-params, no-console */ - - frame = null; - baseRenderTexture = new BaseRenderTexture({ - width, - height, - scaleMode, - resolution, - }); - } - - /** - * The base texture object that this texture uses - * - * @member {BaseTexture} - */ - super(baseRenderTexture, frame); - - this.legacyRenderer = _legacyRenderer; - - /** - * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered. - * - * @member {boolean} - */ - this.valid = true; - - /** - * FilterSystem temporary storage - * @private - * @member {PIXI.Rectangle} - */ - this.filterFrame = null; - - this._updateUvs(); - } - - /** - * Resizes the RenderTexture. - * - * @param {number} width - The width to resize to. - * @param {number} height - The height to resize to. - * @param {boolean} [resizeBaseTexture=true] - Should the baseTexture.width and height values be resized as well? - */ - resize(width, height, resizeBaseTexture = true) - { - width = Math.ceil(width); - height = Math.ceil(height); - - // TODO - could be not required.. - this.valid = (width > 0 && height > 0); - - this._frame.width = this.orig.width = width; - this._frame.height = this.orig.height = height; - - if (resizeBaseTexture) - { - this.baseTexture.resize(width, height); - } - - this._updateUvs(); - } - - /** - * A short hand way of creating a render texture. - * - * @param {object} [options] - Options - * @param {number} [options.width=100] - The width of the render texture - * @param {number} [options.height=100] - The height of the render texture - * @param {number} [options.scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values - * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the texture being generated - * @return {PIXI.RenderTexture} The new render texture - */ - static create(options) - { - // fallback, old-style: create(width, height, scaleMode, resolution) - if (typeof options === 'number') - { - /* eslint-disable prefer-rest-params */ - options = { - width: options, - height: arguments[1], - scaleMode: arguments[2], - resolution: arguments[3], - }; - /* eslint-enable prefer-rest-params */ - } - - return new RenderTexture(new BaseRenderTexture(options)); - } -} diff --git a/packages/core/src/textures/Texture.js b/packages/core/src/textures/Texture.js index 5a22225..237dec3 100644 --- a/packages/core/src/textures/Texture.js +++ b/packages/core/src/textures/Texture.js @@ -81,7 +81,7 @@ /** * This is the trimmed area of original texture, before it was put in atlas - * Please call `_updateUvs()` after you change coordinates of `trim` manually. + * Please call `updateUvs()` after you change coordinates of `trim` manually. * * @member {PIXI.Rectangle} */ @@ -102,7 +102,7 @@ this.requiresUpdate = false; /** - * The WebGL UV data cache. + * The WebGL UV data cache. Can be used as quad UV * * @member {PIXI.TextureUvs} * @private @@ -110,6 +110,14 @@ this._uvs = null; /** + * Default TextureMatrix instance for this texture + * By default that object is not created because its heavy + * + * @member {PIXI.TextureMatrix} + */ + this.uvMatrix = null; + + /** * This is the area of original texture, before it was put in atlas * * @member {PIXI.Rectangle} @@ -145,24 +153,16 @@ } /** - * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. + * Update ID is observed by sprites and TextureMatrix instances. + * Call updateUvs() to increment it. * - * @event PIXI.Texture#update - * @protected - * @param {PIXI.Texture} texture - Instance of texture being updated. + * @member {number} + * @private */ this._updateID = 0; /** - * Contains data for uvs. May contain clamp settings and some matrices. - * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} - * @default null - */ - this.transform = null; - - /** * The ids under which this Texture has been added to the texture cache. This is * automatically set as long as Texture.addToCache is used, but may not be set if a * Texture is added directly to the TextureCache array. @@ -255,8 +255,9 @@ /** * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture. + * Call it after changing the frame */ - _updateUvs() + updateUvs() { if (!this._uvs) { @@ -446,7 +447,7 @@ /** * The frame specifies the region of the base texture that this texture uses. - * Please call `_updateUvs()` after you change coordinates of `frame` manually. + * Please call `updateUvs()` after you change coordinates of `frame` manually. * * @member {PIXI.Rectangle} */ @@ -484,7 +485,7 @@ if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } @@ -507,7 +508,7 @@ this._rotate = rotate; if (this.valid) { - this._updateUvs(); + this.updateUvs(); } } diff --git a/packages/core/src/textures/TextureMatrix.js b/packages/core/src/textures/TextureMatrix.js index 2d4ecea..8198ed1 100644 --- a/packages/core/src/textures/TextureMatrix.js +++ b/packages/core/src/textures/TextureMatrix.js @@ -3,12 +3,18 @@ const tempMat = new Matrix(); /** - * Class controls uv transform and frame clamp for texture - * Can be used in Texture "transform" field, or separately, you can use different clamp settings on the same texture. + * Class controls uv mapping from Texture normal space to BaseTexture normal space. + * Takes `trim` and `rotate` into account. + * May contain clamp settings for Meshes and TilingSprite. + * + * Can be used in Texture `uvMatrix` field, or separately, you can use different clamp settings on the same texture. * If you want to add support for texture region of certain feature or filter, that's what you're looking for. * + * Takes track of Texture changes through `_lastTextureID` private field. + * Use `update()` method call to track it from outside. + * * @see PIXI.Texture - * @see PIXI.mesh.Mesh + * @see PIXI.Mesh * @see PIXI.TilingSprite * @class * @memberof PIXI @@ -31,6 +37,10 @@ this.uClampOffset = new Float32Array(2); + /** + * @member {number} Tracks Texture frame changes + * @private + */ this._lastTextureID = -1; /** diff --git a/packages/mesh/src/Mesh.js b/packages/mesh/src/Mesh.js index 55dc092..e7c8f9e 100644 --- a/packages/mesh/src/Mesh.js +++ b/packages/mesh/src/Mesh.js @@ -1,5 +1,5 @@ import RawMesh from './RawMesh'; -import { Geometry, Program, Shader, Texture } from '@pixi/core'; +import { Geometry, Program, Shader, Texture, TextureMatrix } from '@pixi/core'; import { Matrix } from '@pixi/math'; import { BLEND_MODES } from '@pixi/constants'; import { hex2rgb, premultiplyRgba } from '@pixi/utils'; @@ -23,7 +23,7 @@ * @param {Uint16Array} [indices] - if you want to specify the indices * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts */ - constructor(texture, vertices, uvs, indices, drawMode) + constructor(texture = Texture.EMPTY, vertices, uvs, indices, drawMode) { const geometry = new Geometry(); @@ -40,7 +40,7 @@ const uniforms = { uSampler: texture, - uTransform: Matrix.IDENTITY, + uTextureMatrix: Matrix.IDENTITY, alpha: 1, uColor: new Float32Array([1, 1, 1, 1]), }; @@ -70,7 +70,7 @@ * @default PIXI.Texture.EMPTY * @private */ - this.texture = texture || Texture.EMPTY; + this.texture = texture; /** * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any @@ -85,6 +85,31 @@ this.tint = 0xFFFFFF; this.blendMode = BLEND_MODES.NORMAL; + + /** + * TextureMatrix instance for this Mesh, used to track Texture changes + * + * @member {PIXI.TextureMatrix} + * @readonly + */ + this.uvMatrix = new TextureMatrix(this._texture); + + /** + * whether or not upload uvMatrix to shader + * if its false, then uvs should be pre-multiplied + * if you change it for generated mesh, please call 'refresh(true)' + * @member {boolean} + * @default false + */ + this.uploadUvMatrix = false; + + /** + * Uploads vertices buffer every frame, like in PixiJS V3 and V4. + * + * @member {boolean} + * @default true + */ + this.autoUpdate = true; } /** @@ -152,7 +177,7 @@ if (value) { // wait for the texture to load - if (value.baseTexture.hasLoaded) + if (value.baseTexture.valid) { this._onTextureUpdate(); } @@ -177,6 +202,62 @@ */ _onTextureUpdate() { + // constructor texture update stop + if (!this.uvMatrix) + { + return; + } + this.uvMatrix.texture = this._texture; + this.refresh(); + } + + /** + * multiplies uvs only if uploadUvMatrix is false + * call it after you change uvs manually + * make sure that texture is valid + */ + multiplyUvs() + { + if (!this.uploadUvMatrix) + { + this.uvMatrix.multiplyUvs(this.uvs); + } + } + + /** + * Refreshes uvs for generated meshes (rope, plane) + * sometimes refreshes vertices too + * + * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case + */ + refresh(forceUpdate) + { + if (this.uvMatrix.update(forceUpdate)) + { + this._refresh(); + } + } + + /** + * Updates the object transform for rendering + * + * @private + */ + updateTransform() + { + if (this.autoUpdate) + { + this.geometry.buffers[0].update(); + } + this.containerUpdateTransform(); + } + + /** + * re-calculates mesh coords + * @private + */ + _refresh() + { /* empty */ } } diff --git a/packages/mesh/src/MeshRenderer.js b/packages/mesh/src/MeshRenderer.js index ce6dfe6..12b159b 100644 --- a/packages/mesh/src/MeshRenderer.js +++ b/packages/mesh/src/MeshRenderer.js @@ -1,4 +1,5 @@ import { ObjectRenderer } from '@pixi/core'; +import { Matrix } from '@pixi/math'; /** * WebGL renderer plugin for tiling sprites @@ -44,12 +45,23 @@ // TODO // set the shader props.. // probably only need to set once! - // as its then a refference.. + // as its then a reference.. if (mesh.shader.program.uniformData.translationMatrix) { // the transform! mesh.shader.uniforms.translationMatrix = mesh.transform.worldTransform.toArray(true); } + if (mesh.shader.uniforms.uTextureMatrix) + { + if (mesh.uploadUvMatrix) + { + mesh.shader.uniforms.uTextureMatrix = mesh.uvMatrix.mapCoord.toArray(true); + } + else + { + mesh.shader.uniforms.uTextureMatrix = Matrix.IDENTITY.toArray(true); + } + } // bind and sync uniforms.. this.renderer.shader.bind(mesh.shader); diff --git a/packages/mesh/src/NineSlicePlane.js b/packages/mesh/src/NineSlicePlane.js index 44e3498..a392985 100644 --- a/packages/mesh/src/NineSlicePlane.js +++ b/packages/mesh/src/NineSlicePlane.js @@ -133,58 +133,6 @@ } /** - * Renders one segment of the plane. - * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure - * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used. - * - * @private - * @param {CanvasRenderingContext2D} context - The context to draw with. - * @param {CanvasImageSource} textureSource - The source to draw. - * @param {number} w - width of the texture - * @param {number} h - height of the texture - * @param {number} x1 - x index 1 - * @param {number} y1 - y index 1 - * @param {number} x2 - x index 2 - * @param {number} y2 - y index 2 - */ - drawSegment(context, textureSource, w, h, x1, y1, x2, y2) - { - // otherwise you get weird results when using slices of that are 0 wide or high. - const uvs = this.uvs; - const vertices = this.vertices; - - let sw = (uvs[x2] - uvs[x1]) * w; - let sh = (uvs[y2] - uvs[y1]) * h; - let dw = vertices[x2] - vertices[x1]; - let dh = vertices[y2] - vertices[y1]; - - // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn. - if (sw < 1) - { - sw = 1; - } - - if (sh < 1) - { - sh = 1; - } - - // make sure destination is at least 1 pixel wide and high, otherwise you get - // lines when rendering close to original size. - if (dw < 1) - { - dw = 1; - } - - if (dh < 1) - { - dh = 1; - } - - context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh); - } - - /** * The width of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane * * @member {number} @@ -309,7 +257,7 @@ this.updateHorizontalVertices(); this.updateVerticalVertices(); - this.dirty++; + this.geometry.buffers[1].update(); this.multiplyUvs(); } diff --git a/packages/mesh/src/Plane.js b/packages/mesh/src/Plane.js index b874a39..a50b2d7 100644 --- a/packages/mesh/src/Plane.js +++ b/packages/mesh/src/Plane.js @@ -21,61 +21,51 @@ * @param {PIXI.Texture} texture - The texture to use on the Plane. * @param {number} verticesX - The number of vertices in the x-axis * @param {number} verticesY - The number of vertices in the y-axis - * @param {object} opts - an options object - add meshWidth and meshHeight + * @param {object} [opts] - an options object - add meshWidth and meshHeight, temporary object for PixiJS v5 alpha */ constructor(texture, verticesX, verticesY, opts = {}) { super(texture, new Float32Array(1), new Float32Array(1), new Uint16Array(1), 4); - this.segmentsX = this.verticesX = verticesX || 10; - this.segmentsY = this.verticesY = verticesY || 10; + this.verticesX = verticesX || 10; + this.verticesY = verticesY || 10; - this.meshWidth = opts.meshWidth || texture.width; - this.meshHeight = opts.meshHeight || texture.height; + this.meshWidth = opts.meshWidth || 0; + this.meshHeight = opts.meshHeight || 0; this.refresh(); } /** - * Refreshes - * + * Refreshes plane coordinates + * @private */ - refresh() + _refresh() { + const texture = this._texture; const total = this.verticesX * this.verticesY; const verts = []; const uvs = []; const indices = []; - const texture = this.texture; const segmentsX = this.verticesX - 1; const segmentsY = this.verticesY - 1; - const sizeX = this.meshWidth / segmentsX; - const sizeY = this.meshHeight / segmentsY; + const sizeX = (this.meshWidth || texture.width) / segmentsX; + const sizeY = (this.meshHeight || texture.height) / segmentsY; for (let i = 0; i < total; i++) { - if (texture._uvs) - { - const x = (i % this.verticesX); - const y = ((i / this.verticesX) | 0); + const x = (i % this.verticesX); + const y = ((i / this.verticesX) | 0); - verts.push((x * sizeX), - (y * sizeY)); + verts.push(x * sizeX, y * sizeY); - // this works for rectangular textures. - uvs.push( - texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))), - texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1))) - ); - } - else - { - uvs.push(0); - } + uvs.push(x / segmentsX, y / segmentsY); } + // cons + const totalSub = segmentsX * segmentsY; for (let i = 0; i < totalSub; i++) @@ -92,9 +82,6 @@ indices.push(value2, value4, value3); } - this.shader.uniforms.alpha = 1; - this.shader.uniforms.uSampler2 = this.texture; - this.vertices = new Float32Array(verts); this.uvs = new Float32Array(uvs); this.indices = new Uint16Array(indices); @@ -107,16 +94,7 @@ this.geometry.buffers[0].update(); this.geometry.buffers[1].update(); this.geometry.indexBuffer.update(); - } - /** - * Updates the object transform for rendering - * - * @private - */ - updateTransform() - { - this.geometry.buffers[0].update(); - this.containerUpdateTransform(); + this.multiplyUvs(); } } diff --git a/packages/mesh/src/Rope.js b/packages/mesh/src/Rope.js index f88678a..8a409fe 100644 --- a/packages/mesh/src/Rope.js +++ b/packages/mesh/src/Rope.js @@ -1,5 +1,4 @@ import Mesh from './Mesh'; -import { Point } from '@pixi/math'; /** * The rope allows you to draw a texture across several points and them manipulate these points @@ -35,11 +34,11 @@ this.points = points; this.refresh(); } - /** - * Refreshes + * Refreshes Rope indices and uvs + * @private */ - refresh() + _refresh() { const points = this.points; @@ -66,14 +65,10 @@ const uvs = uvBuffer.data; const indices = indexBuffer.data; - const textureUvs = this.texture._uvs; - const offset = new Point(textureUvs.x0, textureUvs.y0); - const factor = new Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0)); - - uvs[0] = 0 + offset.x; - uvs[1] = 0 + offset.y; - uvs[2] = 0 + offset.x; - uvs[3] = factor.y + offset.y; + uvs[0] = 0; + uvs[1] = 0; + uvs[2] = 0; + uvs[3] = 1; indices[0] = 0; indices[1] = 1; @@ -86,11 +81,11 @@ let index = i * 4; const amount = i / (total - 1); - uvs[index] = (amount * factor.x) + offset.x; - uvs[index + 1] = 0 + offset.y; + uvs[index] = amount; + uvs[index + 1] = 0; - uvs[index + 2] = (amount * factor.x) + offset.x; - uvs[index + 3] = factor.y + offset.y; + uvs[index + 2] = amount; + uvs[index + 3] = 1; index = i * 2; indices[index] = index; @@ -98,17 +93,17 @@ } // ensure that the changes are uploaded - vertexBuffer.update(); uvBuffer.update(); indexBuffer.update(); + + this.multiplyUvs(); + this.refreshVertices(); } /** - * Updates the object transform for rendering - * - * @private + * refreshes vertices of Rope mesh */ - updateTransform() + refreshVertices() { const points = this.points; @@ -124,9 +119,7 @@ // this.count -= 0.2; - const vertexBuffer = this.geometry.getAttribute('aVertexPosition'); - const vertices = vertexBuffer.data; - + const vertices = this.vertices; const total = points.length; for (let i = 0; i < total; i++) @@ -152,8 +145,9 @@ { ratio = 1; } + const perpLength = Math.sqrt((perpX * perpX) + (perpY * perpY)); - const num = this.texture.height / 2; // (20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; + const num = this._texture.height / 2; // (20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perpX /= perpLength; perpY /= perpLength; @@ -169,21 +163,20 @@ lastPoint = point; } - // mark the buffer as requiring an upload.. - vertexBuffer.update(); - - this.uniforms.alpha = this.worldAlpha; - - this.containerUpdateTransform(); + this.geometry.buffers[0].update(); } /** - * When the texture is updated, this event will fire to update the scale and frame + * Updates the object transform for rendering * * @private */ - _onTextureUpdate() + updateTransform() { - this.refresh(); + if (this.autoUpdate) + { + this.refreshVertices(); + } + this.containerUpdateTransform(); } } diff --git a/packages/mesh/src/mesh.vert b/packages/mesh/src/mesh.vert index acc096c..0526df9 100644 --- a/packages/mesh/src/mesh.vert +++ b/packages/mesh/src/mesh.vert @@ -3,7 +3,7 @@ uniform mat3 projectionMatrix; uniform mat3 translationMatrix; -uniform mat3 uTransform; +uniform mat3 uTextureMatrix; varying vec2 vTextureCoord; @@ -11,5 +11,5 @@ { gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); - vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy; + vTextureCoord = (uTextureMatrix * vec3(aTextureCoord, 1.0)).xy; } diff --git a/packages/particles/src/ParticleContainer.js b/packages/particles/src/ParticleContainer.js index 3a36e98..22db6f5 100644 --- a/packages/particles/src/ParticleContainer.js +++ b/packages/particles/src/ParticleContainer.js @@ -214,7 +214,7 @@ if (!this.baseTexture) { this.baseTexture = this.children[0]._texture.baseTexture; - if (!this.baseTexture.hasLoaded) + if (!this.baseTexture.valid) { this.baseTexture.once('update', () => this.onChildrenChange(0)); } diff --git a/packages/sprite-tiling/src/TilingSprite.js b/packages/sprite-tiling/src/TilingSprite.js index dc8dab6..b3cba22 100644 --- a/packages/sprite-tiling/src/TilingSprite.js +++ b/packages/sprite-tiling/src/TilingSprite.js @@ -57,11 +57,11 @@ this._canvasPattern = null; /** - * transform that is applied to UV to get the texture coords + * matrix that is applied to UV to get the coords in Texture normalized space to coords in BaseTexture space * * @member {PIXI.TextureMatrix} */ - this.uvTransform = texture.transform || new TextureMatrix(texture); + this.uvMatrix = texture.uvMatrix || new TextureMatrix(texture); /** * Plugin that is responsible for rendering this element. @@ -89,13 +89,13 @@ */ get clampMargin() { - return this.uvTransform.clampMargin; + return this.uvMatrix.clampMargin; } set clampMargin(value) // eslint-disable-line require-jsdoc { - this.uvTransform.clampMargin = value; - this.uvTransform.update(true); + this.uvMatrix.clampMargin = value; + this.uvMatrix.update(true); } /** @@ -133,9 +133,9 @@ */ _onTextureUpdate() { - if (this.uvTransform) + if (this.uvMatrix) { - this.uvTransform.texture = this._texture; + this.uvMatrix.texture = this._texture; } this.cachedTint = 0xFFFFFF; } @@ -157,7 +157,7 @@ } this.tileTransform.updateLocalTransform(); - this.uvTransform.update(); + this.uvMatrix.update(); renderer.batch.setObjectRenderer(renderer.plugins[this.pluginName]); renderer.plugins[this.pluginName].render(this); @@ -252,7 +252,7 @@ super.destroy(options); this.tileTransform = null; - this.uvTransform = null; + this.uvMatrix = null; } /** diff --git a/packages/sprite-tiling/src/TilingSpriteRenderer.js b/packages/sprite-tiling/src/TilingSpriteRenderer.js index 8b79e13..dca7270 100644 --- a/packages/sprite-tiling/src/TilingSpriteRenderer.js +++ b/packages/sprite-tiling/src/TilingSpriteRenderer.js @@ -69,7 +69,7 @@ const tex = ts._texture; const baseTex = tex.baseTexture; const lt = ts.tileTransform.localTransform; - const uv = ts.uvTransform; + const uv = ts.uvMatrix; let isSimple = baseTex.isPowerOfTwo && tex.frame.width === baseTex.width && tex.frame.height === baseTex.height; diff --git a/packages/sprite/src/Sprite.js b/packages/sprite/src/Sprite.js index d9b6dbc..3ccd7c5 100644 --- a/packages/sprite/src/Sprite.js +++ b/packages/sprite/src/Sprite.js @@ -579,7 +579,7 @@ if (value) { // wait for the texture to load - if (value.baseTexture.hasLoaded) + if (value.baseTexture.valid) { this._onTextureUpdate(); }