diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 78e6e40..16536d2 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -65,8 +65,36 @@ } /** + * Multiplies uvs array to transform + * @param {Float32Array} uvs mesh uvs + * @param {Float32Array} [out=uvs] output + * @returns {Float32Array} output + */ + multiplyUvs(uvs, out) + { + if (out === undefined) + { + out = uvs; + } + + const mat = this.mapCoord; + + for (let i = 0; i < uvs.length; i += 2) + { + const x = uvs[i]; + const y = uvs[i + 1]; + + out[i] = (x * mat.a) + (y * mat.c) + mat.tx; + out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; + } + + return out; + } + + /** * updates matrices if texture was changed * @param {boolean} forceUpdate if true, matrices will be updated any case + * @returns {boolean} whether or not it was updated */ update(forceUpdate) { @@ -74,13 +102,13 @@ if (!tex || !tex.valid) { - return; + return false; } if (!forceUpdate && this._lastTextureID === tex._updateID) { - return; + return false; } this._lastTextureID = tex._updateID; @@ -110,5 +138,7 @@ frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; this.uClampOffset[0] = offset / texBase.realWidth; this.uClampOffset[1] = offset / texBase.realHeight; + + return true; } } diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 78e6e40..16536d2 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -65,8 +65,36 @@ } /** + * Multiplies uvs array to transform + * @param {Float32Array} uvs mesh uvs + * @param {Float32Array} [out=uvs] output + * @returns {Float32Array} output + */ + multiplyUvs(uvs, out) + { + if (out === undefined) + { + out = uvs; + } + + const mat = this.mapCoord; + + for (let i = 0; i < uvs.length; i += 2) + { + const x = uvs[i]; + const y = uvs[i + 1]; + + out[i] = (x * mat.a) + (y * mat.c) + mat.tx; + out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; + } + + return out; + } + + /** * updates matrices if texture was changed * @param {boolean} forceUpdate if true, matrices will be updated any case + * @returns {boolean} whether or not it was updated */ update(forceUpdate) { @@ -74,13 +102,13 @@ if (!tex || !tex.valid) { - return; + return false; } if (!forceUpdate && this._lastTextureID === tex._updateID) { - return; + return false; } this._lastTextureID = tex._updateID; @@ -110,5 +138,7 @@ frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; this.uClampOffset[0] = offset / texBase.realWidth; this.uClampOffset[1] = offset / texBase.realHeight; + + return true; } } diff --git a/src/extras/index.js b/src/extras/index.js index bbd8bbf..6e8d039 100644 --- a/src/extras/index.js +++ b/src/extras/index.js @@ -1,7 +1,6 @@ /** * @namespace PIXI.extras */ -export { default as TextureTransform } from './TextureTransform'; export { default as AnimatedSprite } from './AnimatedSprite'; export { default as TilingSprite } from './TilingSprite'; export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer'; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 78e6e40..16536d2 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -65,8 +65,36 @@ } /** + * Multiplies uvs array to transform + * @param {Float32Array} uvs mesh uvs + * @param {Float32Array} [out=uvs] output + * @returns {Float32Array} output + */ + multiplyUvs(uvs, out) + { + if (out === undefined) + { + out = uvs; + } + + const mat = this.mapCoord; + + for (let i = 0; i < uvs.length; i += 2) + { + const x = uvs[i]; + const y = uvs[i + 1]; + + out[i] = (x * mat.a) + (y * mat.c) + mat.tx; + out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; + } + + return out; + } + + /** * updates matrices if texture was changed * @param {boolean} forceUpdate if true, matrices will be updated any case + * @returns {boolean} whether or not it was updated */ update(forceUpdate) { @@ -74,13 +102,13 @@ if (!tex || !tex.valid) { - return; + return false; } if (!forceUpdate && this._lastTextureID === tex._updateID) { - return; + return false; } this._lastTextureID = tex._updateID; @@ -110,5 +138,7 @@ frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; this.uClampOffset[0] = offset / texBase.realWidth; this.uClampOffset[1] = offset / texBase.realHeight; + + return true; } } diff --git a/src/extras/index.js b/src/extras/index.js index bbd8bbf..6e8d039 100644 --- a/src/extras/index.js +++ b/src/extras/index.js @@ -1,7 +1,6 @@ /** * @namespace PIXI.extras */ -export { default as TextureTransform } from './TextureTransform'; export { default as AnimatedSprite } from './AnimatedSprite'; export { default as TilingSprite } from './TilingSprite'; export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer'; diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 6568231..5734fc0 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -10,7 +10,7 @@ * WebGL renderer plugin for tiling sprites * * @class - * @memberof PIXI + * @memberof PIXI.extras * @extends PIXI.ObjectRenderer */ export default class TilingSpriteRenderer extends core.ObjectRenderer diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 78e6e40..16536d2 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -65,8 +65,36 @@ } /** + * Multiplies uvs array to transform + * @param {Float32Array} uvs mesh uvs + * @param {Float32Array} [out=uvs] output + * @returns {Float32Array} output + */ + multiplyUvs(uvs, out) + { + if (out === undefined) + { + out = uvs; + } + + const mat = this.mapCoord; + + for (let i = 0; i < uvs.length; i += 2) + { + const x = uvs[i]; + const y = uvs[i + 1]; + + out[i] = (x * mat.a) + (y * mat.c) + mat.tx; + out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; + } + + return out; + } + + /** * updates matrices if texture was changed * @param {boolean} forceUpdate if true, matrices will be updated any case + * @returns {boolean} whether or not it was updated */ update(forceUpdate) { @@ -74,13 +102,13 @@ if (!tex || !tex.valid) { - return; + return false; } if (!forceUpdate && this._lastTextureID === tex._updateID) { - return; + return false; } this._lastTextureID = tex._updateID; @@ -110,5 +138,7 @@ frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; this.uClampOffset[0] = offset / texBase.realWidth; this.uClampOffset[1] = offset / texBase.realHeight; + + return true; } } diff --git a/src/extras/index.js b/src/extras/index.js index bbd8bbf..6e8d039 100644 --- a/src/extras/index.js +++ b/src/extras/index.js @@ -1,7 +1,6 @@ /** * @namespace PIXI.extras */ -export { default as TextureTransform } from './TextureTransform'; export { default as AnimatedSprite } from './AnimatedSprite'; export { default as TilingSprite } from './TilingSprite'; export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer'; diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 6568231..5734fc0 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -10,7 +10,7 @@ * WebGL renderer plugin for tiling sprites * * @class - * @memberof PIXI + * @memberof PIXI.extras * @extends PIXI.ObjectRenderer */ export default class TilingSpriteRenderer extends core.ObjectRenderer diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 02c04a0..dc8c88d 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,4 +1,5 @@ import * as core from '../core'; +import { default as TextureTransform } from '../extras/TextureTransform'; const tempPoint = new core.Point(); const tempPolygon = new core.Polygon(); @@ -28,7 +29,7 @@ * @member {PIXI.Texture} * @private */ - this._texture = null; + this._texture = texture; /** * The Uvs of the Mesh @@ -98,9 +99,6 @@ */ this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; - // run texture setter; - this.texture = texture; - /** * The default shader that is used if a mesh doesn't have a more specific one. * @@ -125,9 +123,27 @@ this._glDatas = {}; /** + * transform that is applied to UV to get the texture coords + * its updated independently from texture uvTransform + * updates of uvs are tied to that thing + * + * @member {PIXI.extras.TextureTransform} + * @private + */ + this._uvTransform = new TextureTransform(texture); + + /** + * whether or not upload uvTransform 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.uploadUvTransform = false; + + /** * Plugin that is responsible for rendering this element. * Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods. - * * @member {string} * @default 'mesh' */ @@ -142,6 +158,7 @@ */ _renderWebGL(renderer) { + this.refresh(); renderer.setObjectRenderer(renderer.plugins[this.pluginName]); renderer.plugins[this.pluginName].render(this); } @@ -154,6 +171,7 @@ */ _renderCanvas(renderer) { + this.refresh(); renderer.plugins[this.pluginName].render(this); } @@ -164,6 +182,43 @@ */ _onTextureUpdate() { + this._uvTransform.texture = this._texture; + this.refresh(); + } + + /** + * multiplies uvs only if uploadUvTransform is false + * call it after you change uvs manually + * make sure that texture is valid + */ + multiplyUvs() + { + if (!this.uploadUvTransform) + { + this._uvTransform.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._uvTransform.update(forceUpdate)) + { + this._refresh(); + } + } + + /** + * re-calculates mesh coords + * @protected + */ + _refresh() + { /* empty */ } diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 78e6e40..16536d2 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -65,8 +65,36 @@ } /** + * Multiplies uvs array to transform + * @param {Float32Array} uvs mesh uvs + * @param {Float32Array} [out=uvs] output + * @returns {Float32Array} output + */ + multiplyUvs(uvs, out) + { + if (out === undefined) + { + out = uvs; + } + + const mat = this.mapCoord; + + for (let i = 0; i < uvs.length; i += 2) + { + const x = uvs[i]; + const y = uvs[i + 1]; + + out[i] = (x * mat.a) + (y * mat.c) + mat.tx; + out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; + } + + return out; + } + + /** * updates matrices if texture was changed * @param {boolean} forceUpdate if true, matrices will be updated any case + * @returns {boolean} whether or not it was updated */ update(forceUpdate) { @@ -74,13 +102,13 @@ if (!tex || !tex.valid) { - return; + return false; } if (!forceUpdate && this._lastTextureID === tex._updateID) { - return; + return false; } this._lastTextureID = tex._updateID; @@ -110,5 +138,7 @@ frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; this.uClampOffset[0] = offset / texBase.realWidth; this.uClampOffset[1] = offset / texBase.realHeight; + + return true; } } diff --git a/src/extras/index.js b/src/extras/index.js index bbd8bbf..6e8d039 100644 --- a/src/extras/index.js +++ b/src/extras/index.js @@ -1,7 +1,6 @@ /** * @namespace PIXI.extras */ -export { default as TextureTransform } from './TextureTransform'; export { default as AnimatedSprite } from './AnimatedSprite'; export { default as TilingSprite } from './TilingSprite'; export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer'; diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 6568231..5734fc0 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -10,7 +10,7 @@ * WebGL renderer plugin for tiling sprites * * @class - * @memberof PIXI + * @memberof PIXI.extras * @extends PIXI.ObjectRenderer */ export default class TilingSpriteRenderer extends core.ObjectRenderer diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 02c04a0..dc8c88d 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,4 +1,5 @@ import * as core from '../core'; +import { default as TextureTransform } from '../extras/TextureTransform'; const tempPoint = new core.Point(); const tempPolygon = new core.Polygon(); @@ -28,7 +29,7 @@ * @member {PIXI.Texture} * @private */ - this._texture = null; + this._texture = texture; /** * The Uvs of the Mesh @@ -98,9 +99,6 @@ */ this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; - // run texture setter; - this.texture = texture; - /** * The default shader that is used if a mesh doesn't have a more specific one. * @@ -125,9 +123,27 @@ this._glDatas = {}; /** + * transform that is applied to UV to get the texture coords + * its updated independently from texture uvTransform + * updates of uvs are tied to that thing + * + * @member {PIXI.extras.TextureTransform} + * @private + */ + this._uvTransform = new TextureTransform(texture); + + /** + * whether or not upload uvTransform 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.uploadUvTransform = false; + + /** * Plugin that is responsible for rendering this element. * Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods. - * * @member {string} * @default 'mesh' */ @@ -142,6 +158,7 @@ */ _renderWebGL(renderer) { + this.refresh(); renderer.setObjectRenderer(renderer.plugins[this.pluginName]); renderer.plugins[this.pluginName].render(this); } @@ -154,6 +171,7 @@ */ _renderCanvas(renderer) { + this.refresh(); renderer.plugins[this.pluginName].render(this); } @@ -164,6 +182,43 @@ */ _onTextureUpdate() { + this._uvTransform.texture = this._texture; + this.refresh(); + } + + /** + * multiplies uvs only if uploadUvTransform is false + * call it after you change uvs manually + * make sure that texture is valid + */ + multiplyUvs() + { + if (!this.uploadUvTransform) + { + this._uvTransform.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._uvTransform.update(forceUpdate)) + { + this._refresh(); + } + } + + /** + * re-calculates mesh coords + * @protected + */ + _refresh() + { /* empty */ } diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js index b36ea3d..c243246 100644 --- a/src/mesh/NineSlicePlane.js +++ b/src/mesh/NineSlicePlane.js @@ -115,6 +115,8 @@ * @override */ this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE; + + this.refresh(true); } /** diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 78e6e40..16536d2 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -65,8 +65,36 @@ } /** + * Multiplies uvs array to transform + * @param {Float32Array} uvs mesh uvs + * @param {Float32Array} [out=uvs] output + * @returns {Float32Array} output + */ + multiplyUvs(uvs, out) + { + if (out === undefined) + { + out = uvs; + } + + const mat = this.mapCoord; + + for (let i = 0; i < uvs.length; i += 2) + { + const x = uvs[i]; + const y = uvs[i + 1]; + + out[i] = (x * mat.a) + (y * mat.c) + mat.tx; + out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; + } + + return out; + } + + /** * updates matrices if texture was changed * @param {boolean} forceUpdate if true, matrices will be updated any case + * @returns {boolean} whether or not it was updated */ update(forceUpdate) { @@ -74,13 +102,13 @@ if (!tex || !tex.valid) { - return; + return false; } if (!forceUpdate && this._lastTextureID === tex._updateID) { - return; + return false; } this._lastTextureID = tex._updateID; @@ -110,5 +138,7 @@ frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; this.uClampOffset[0] = offset / texBase.realWidth; this.uClampOffset[1] = offset / texBase.realHeight; + + return true; } } diff --git a/src/extras/index.js b/src/extras/index.js index bbd8bbf..6e8d039 100644 --- a/src/extras/index.js +++ b/src/extras/index.js @@ -1,7 +1,6 @@ /** * @namespace PIXI.extras */ -export { default as TextureTransform } from './TextureTransform'; export { default as AnimatedSprite } from './AnimatedSprite'; export { default as TilingSprite } from './TilingSprite'; export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer'; diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 6568231..5734fc0 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -10,7 +10,7 @@ * WebGL renderer plugin for tiling sprites * * @class - * @memberof PIXI + * @memberof PIXI.extras * @extends PIXI.ObjectRenderer */ export default class TilingSpriteRenderer extends core.ObjectRenderer diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 02c04a0..dc8c88d 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,4 +1,5 @@ import * as core from '../core'; +import { default as TextureTransform } from '../extras/TextureTransform'; const tempPoint = new core.Point(); const tempPolygon = new core.Polygon(); @@ -28,7 +29,7 @@ * @member {PIXI.Texture} * @private */ - this._texture = null; + this._texture = texture; /** * The Uvs of the Mesh @@ -98,9 +99,6 @@ */ this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; - // run texture setter; - this.texture = texture; - /** * The default shader that is used if a mesh doesn't have a more specific one. * @@ -125,9 +123,27 @@ this._glDatas = {}; /** + * transform that is applied to UV to get the texture coords + * its updated independently from texture uvTransform + * updates of uvs are tied to that thing + * + * @member {PIXI.extras.TextureTransform} + * @private + */ + this._uvTransform = new TextureTransform(texture); + + /** + * whether or not upload uvTransform 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.uploadUvTransform = false; + + /** * Plugin that is responsible for rendering this element. * Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods. - * * @member {string} * @default 'mesh' */ @@ -142,6 +158,7 @@ */ _renderWebGL(renderer) { + this.refresh(); renderer.setObjectRenderer(renderer.plugins[this.pluginName]); renderer.plugins[this.pluginName].render(this); } @@ -154,6 +171,7 @@ */ _renderCanvas(renderer) { + this.refresh(); renderer.plugins[this.pluginName].render(this); } @@ -164,6 +182,43 @@ */ _onTextureUpdate() { + this._uvTransform.texture = this._texture; + this.refresh(); + } + + /** + * multiplies uvs only if uploadUvTransform is false + * call it after you change uvs manually + * make sure that texture is valid + */ + multiplyUvs() + { + if (!this.uploadUvTransform) + { + this._uvTransform.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._uvTransform.update(forceUpdate)) + { + this._refresh(); + } + } + + /** + * re-calculates mesh coords + * @protected + */ + _refresh() + { /* empty */ } diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js index b36ea3d..c243246 100644 --- a/src/mesh/NineSlicePlane.js +++ b/src/mesh/NineSlicePlane.js @@ -115,6 +115,8 @@ * @override */ this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE; + + this.refresh(true); } /** diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js index 3869a39..4f9b51c 100644 --- a/src/mesh/Plane.js +++ b/src/mesh/Plane.js @@ -43,17 +43,17 @@ } /** - * Refreshes + * Refreshes plane coordinates * */ - refresh() + _refresh() { + const texture = this._texture; const total = this.verticesX * this.verticesY; const verts = []; const colors = []; const uvs = []; const indices = []; - const texture = this.texture; const segmentsX = this.verticesX - 1; const segmentsY = this.verticesY - 1; @@ -63,24 +63,12 @@ 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 @@ -106,8 +94,9 @@ this.uvs = new Float32Array(uvs); this.colors = new Float32Array(colors); this.indices = new Uint16Array(indices); - this.indexDirty = true; + + this.multiplyUvs(); } /** diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 78e6e40..16536d2 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -65,8 +65,36 @@ } /** + * Multiplies uvs array to transform + * @param {Float32Array} uvs mesh uvs + * @param {Float32Array} [out=uvs] output + * @returns {Float32Array} output + */ + multiplyUvs(uvs, out) + { + if (out === undefined) + { + out = uvs; + } + + const mat = this.mapCoord; + + for (let i = 0; i < uvs.length; i += 2) + { + const x = uvs[i]; + const y = uvs[i + 1]; + + out[i] = (x * mat.a) + (y * mat.c) + mat.tx; + out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; + } + + return out; + } + + /** * updates matrices if texture was changed * @param {boolean} forceUpdate if true, matrices will be updated any case + * @returns {boolean} whether or not it was updated */ update(forceUpdate) { @@ -74,13 +102,13 @@ if (!tex || !tex.valid) { - return; + return false; } if (!forceUpdate && this._lastTextureID === tex._updateID) { - return; + return false; } this._lastTextureID = tex._updateID; @@ -110,5 +138,7 @@ frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; this.uClampOffset[0] = offset / texBase.realWidth; this.uClampOffset[1] = offset / texBase.realHeight; + + return true; } } diff --git a/src/extras/index.js b/src/extras/index.js index bbd8bbf..6e8d039 100644 --- a/src/extras/index.js +++ b/src/extras/index.js @@ -1,7 +1,6 @@ /** * @namespace PIXI.extras */ -export { default as TextureTransform } from './TextureTransform'; export { default as AnimatedSprite } from './AnimatedSprite'; export { default as TilingSprite } from './TilingSprite'; export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer'; diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 6568231..5734fc0 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -10,7 +10,7 @@ * WebGL renderer plugin for tiling sprites * * @class - * @memberof PIXI + * @memberof PIXI.extras * @extends PIXI.ObjectRenderer */ export default class TilingSpriteRenderer extends core.ObjectRenderer diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 02c04a0..dc8c88d 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,4 +1,5 @@ import * as core from '../core'; +import { default as TextureTransform } from '../extras/TextureTransform'; const tempPoint = new core.Point(); const tempPolygon = new core.Polygon(); @@ -28,7 +29,7 @@ * @member {PIXI.Texture} * @private */ - this._texture = null; + this._texture = texture; /** * The Uvs of the Mesh @@ -98,9 +99,6 @@ */ this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; - // run texture setter; - this.texture = texture; - /** * The default shader that is used if a mesh doesn't have a more specific one. * @@ -125,9 +123,27 @@ this._glDatas = {}; /** + * transform that is applied to UV to get the texture coords + * its updated independently from texture uvTransform + * updates of uvs are tied to that thing + * + * @member {PIXI.extras.TextureTransform} + * @private + */ + this._uvTransform = new TextureTransform(texture); + + /** + * whether or not upload uvTransform 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.uploadUvTransform = false; + + /** * Plugin that is responsible for rendering this element. * Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods. - * * @member {string} * @default 'mesh' */ @@ -142,6 +158,7 @@ */ _renderWebGL(renderer) { + this.refresh(); renderer.setObjectRenderer(renderer.plugins[this.pluginName]); renderer.plugins[this.pluginName].render(this); } @@ -154,6 +171,7 @@ */ _renderCanvas(renderer) { + this.refresh(); renderer.plugins[this.pluginName].render(this); } @@ -164,6 +182,43 @@ */ _onTextureUpdate() { + this._uvTransform.texture = this._texture; + this.refresh(); + } + + /** + * multiplies uvs only if uploadUvTransform is false + * call it after you change uvs manually + * make sure that texture is valid + */ + multiplyUvs() + { + if (!this.uploadUvTransform) + { + this._uvTransform.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._uvTransform.update(forceUpdate)) + { + this._refresh(); + } + } + + /** + * re-calculates mesh coords + * @protected + */ + _refresh() + { /* empty */ } diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js index b36ea3d..c243246 100644 --- a/src/mesh/NineSlicePlane.js +++ b/src/mesh/NineSlicePlane.js @@ -115,6 +115,8 @@ * @override */ this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE; + + this.refresh(true); } /** diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js index 3869a39..4f9b51c 100644 --- a/src/mesh/Plane.js +++ b/src/mesh/Plane.js @@ -43,17 +43,17 @@ } /** - * Refreshes + * Refreshes plane coordinates * */ - refresh() + _refresh() { + const texture = this._texture; const total = this.verticesX * this.verticesY; const verts = []; const colors = []; const uvs = []; const indices = []; - const texture = this.texture; const segmentsX = this.verticesX - 1; const segmentsY = this.verticesY - 1; @@ -63,24 +63,12 @@ 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 @@ -106,8 +94,9 @@ this.uvs = new Float32Array(uvs); this.colors = new Float32Array(colors); this.indices = new Uint16Array(indices); - this.indexDirty = true; + + this.multiplyUvs(); } /** diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js index 6ff8aaf..299cf3d 100644 --- a/src/mesh/Rope.js +++ b/src/mesh/Rope.js @@ -1,5 +1,4 @@ import Mesh from './Mesh'; -import * as core from '../core'; /** * The rope allows you to draw a texture across several points and them manipulate these points @@ -52,13 +51,11 @@ this.indices = new Uint16Array(points.length * 2); /** - * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can - * call _onTextureUpdated which could call refresh too early. - * + * refreshes vertices on every updateTransform * @member {boolean} - * @private + * @default true */ - this._ready = true; + this.autoUpdate = true; this.refresh(); } @@ -67,7 +64,7 @@ * Refreshes * */ - refresh() + _refresh() { const points = this.points; @@ -91,14 +88,10 @@ const indices = this.indices; const colors = this.colors; - const textureUvs = this._texture._uvs; - const offset = new core.Point(textureUvs.x0, textureUvs.y0); - const factor = new core.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; colors[0] = 1; colors[1] = 1; @@ -114,11 +107,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; colors[index] = 1; @@ -132,30 +125,15 @@ // ensure that the changes are uploaded this.dirty++; this.indexDirty++; + + this.multiplyUvs(); + this.refreshVertices(); } /** - * Clear texture UVs when new texture is set - * - * @private + * refreshes vertices of Rope mesh */ - _onTextureUpdate() - { - super._onTextureUpdate(); - - // wait for the Rope ctor to finish before calling refresh - if (this._ready) - { - this.refresh(); - } - } - - /** - * Updates the object transform for rendering - * - * @private - */ - updateTransform() + refreshVertices() { const points = this.points; @@ -214,7 +192,19 @@ lastPoint = point; } + } + /** + * Updates the object transform for rendering + * + * @private + */ + updateTransform() + { + if (this.autoUpdate) + { + this.refreshVertices(); + } this.containerUpdateTransform(); } diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 78e6e40..16536d2 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -65,8 +65,36 @@ } /** + * Multiplies uvs array to transform + * @param {Float32Array} uvs mesh uvs + * @param {Float32Array} [out=uvs] output + * @returns {Float32Array} output + */ + multiplyUvs(uvs, out) + { + if (out === undefined) + { + out = uvs; + } + + const mat = this.mapCoord; + + for (let i = 0; i < uvs.length; i += 2) + { + const x = uvs[i]; + const y = uvs[i + 1]; + + out[i] = (x * mat.a) + (y * mat.c) + mat.tx; + out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; + } + + return out; + } + + /** * updates matrices if texture was changed * @param {boolean} forceUpdate if true, matrices will be updated any case + * @returns {boolean} whether or not it was updated */ update(forceUpdate) { @@ -74,13 +102,13 @@ if (!tex || !tex.valid) { - return; + return false; } if (!forceUpdate && this._lastTextureID === tex._updateID) { - return; + return false; } this._lastTextureID = tex._updateID; @@ -110,5 +138,7 @@ frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; this.uClampOffset[0] = offset / texBase.realWidth; this.uClampOffset[1] = offset / texBase.realHeight; + + return true; } } diff --git a/src/extras/index.js b/src/extras/index.js index bbd8bbf..6e8d039 100644 --- a/src/extras/index.js +++ b/src/extras/index.js @@ -1,7 +1,6 @@ /** * @namespace PIXI.extras */ -export { default as TextureTransform } from './TextureTransform'; export { default as AnimatedSprite } from './AnimatedSprite'; export { default as TilingSprite } from './TilingSprite'; export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer'; diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 6568231..5734fc0 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -10,7 +10,7 @@ * WebGL renderer plugin for tiling sprites * * @class - * @memberof PIXI + * @memberof PIXI.extras * @extends PIXI.ObjectRenderer */ export default class TilingSpriteRenderer extends core.ObjectRenderer diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 02c04a0..dc8c88d 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,4 +1,5 @@ import * as core from '../core'; +import { default as TextureTransform } from '../extras/TextureTransform'; const tempPoint = new core.Point(); const tempPolygon = new core.Polygon(); @@ -28,7 +29,7 @@ * @member {PIXI.Texture} * @private */ - this._texture = null; + this._texture = texture; /** * The Uvs of the Mesh @@ -98,9 +99,6 @@ */ this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; - // run texture setter; - this.texture = texture; - /** * The default shader that is used if a mesh doesn't have a more specific one. * @@ -125,9 +123,27 @@ this._glDatas = {}; /** + * transform that is applied to UV to get the texture coords + * its updated independently from texture uvTransform + * updates of uvs are tied to that thing + * + * @member {PIXI.extras.TextureTransform} + * @private + */ + this._uvTransform = new TextureTransform(texture); + + /** + * whether or not upload uvTransform 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.uploadUvTransform = false; + + /** * Plugin that is responsible for rendering this element. * Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods. - * * @member {string} * @default 'mesh' */ @@ -142,6 +158,7 @@ */ _renderWebGL(renderer) { + this.refresh(); renderer.setObjectRenderer(renderer.plugins[this.pluginName]); renderer.plugins[this.pluginName].render(this); } @@ -154,6 +171,7 @@ */ _renderCanvas(renderer) { + this.refresh(); renderer.plugins[this.pluginName].render(this); } @@ -164,6 +182,43 @@ */ _onTextureUpdate() { + this._uvTransform.texture = this._texture; + this.refresh(); + } + + /** + * multiplies uvs only if uploadUvTransform is false + * call it after you change uvs manually + * make sure that texture is valid + */ + multiplyUvs() + { + if (!this.uploadUvTransform) + { + this._uvTransform.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._uvTransform.update(forceUpdate)) + { + this._refresh(); + } + } + + /** + * re-calculates mesh coords + * @protected + */ + _refresh() + { /* empty */ } diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js index b36ea3d..c243246 100644 --- a/src/mesh/NineSlicePlane.js +++ b/src/mesh/NineSlicePlane.js @@ -115,6 +115,8 @@ * @override */ this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE; + + this.refresh(true); } /** diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js index 3869a39..4f9b51c 100644 --- a/src/mesh/Plane.js +++ b/src/mesh/Plane.js @@ -43,17 +43,17 @@ } /** - * Refreshes + * Refreshes plane coordinates * */ - refresh() + _refresh() { + const texture = this._texture; const total = this.verticesX * this.verticesY; const verts = []; const colors = []; const uvs = []; const indices = []; - const texture = this.texture; const segmentsX = this.verticesX - 1; const segmentsY = this.verticesY - 1; @@ -63,24 +63,12 @@ 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 @@ -106,8 +94,9 @@ this.uvs = new Float32Array(uvs); this.colors = new Float32Array(colors); this.indices = new Uint16Array(indices); - this.indexDirty = true; + + this.multiplyUvs(); } /** diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js index 6ff8aaf..299cf3d 100644 --- a/src/mesh/Rope.js +++ b/src/mesh/Rope.js @@ -1,5 +1,4 @@ import Mesh from './Mesh'; -import * as core from '../core'; /** * The rope allows you to draw a texture across several points and them manipulate these points @@ -52,13 +51,11 @@ this.indices = new Uint16Array(points.length * 2); /** - * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can - * call _onTextureUpdated which could call refresh too early. - * + * refreshes vertices on every updateTransform * @member {boolean} - * @private + * @default true */ - this._ready = true; + this.autoUpdate = true; this.refresh(); } @@ -67,7 +64,7 @@ * Refreshes * */ - refresh() + _refresh() { const points = this.points; @@ -91,14 +88,10 @@ const indices = this.indices; const colors = this.colors; - const textureUvs = this._texture._uvs; - const offset = new core.Point(textureUvs.x0, textureUvs.y0); - const factor = new core.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; colors[0] = 1; colors[1] = 1; @@ -114,11 +107,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; colors[index] = 1; @@ -132,30 +125,15 @@ // ensure that the changes are uploaded this.dirty++; this.indexDirty++; + + this.multiplyUvs(); + this.refreshVertices(); } /** - * Clear texture UVs when new texture is set - * - * @private + * refreshes vertices of Rope mesh */ - _onTextureUpdate() - { - super._onTextureUpdate(); - - // wait for the Rope ctor to finish before calling refresh - if (this._ready) - { - this.refresh(); - } - } - - /** - * Updates the object transform for rendering - * - * @private - */ - updateTransform() + refreshVertices() { const points = this.points; @@ -214,7 +192,19 @@ lastPoint = point; } + } + /** + * Updates the object transform for rendering + * + * @private + */ + updateTransform() + { + if (this.autoUpdate) + { + this.refreshVertices(); + } this.containerUpdateTransform(); } diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js index 1263170..871612d 100644 --- a/src/mesh/canvas/CanvasMeshRenderer.js +++ b/src/mesh/canvas/CanvasMeshRenderer.js @@ -135,12 +135,24 @@ const textureWidth = base.width; const textureHeight = base.height; - const u0 = uvs[index0] * base.width; - const u1 = uvs[index1] * base.width; - const u2 = uvs[index2] * base.width; - const v0 = uvs[index0 + 1] * base.height; - const v1 = uvs[index1 + 1] * base.height; - const v2 = uvs[index2 + 1] * base.height; + let u0 = uvs[index0] * base.width; + let u1 = uvs[index1] * base.width; + let u2 = uvs[index2] * base.width; + let v0 = uvs[index0 + 1] * base.height; + let v1 = uvs[index1 + 1] * base.height; + let v2 = uvs[index2 + 1] * base.height; + + if (mesh.uploadUvTransform) + { + const ut = mesh._uvTransform.mapCoord; + + u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width; + u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width; + u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width; + v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height; + v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height; + v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height; + } let x0 = vertices[index0]; let x1 = vertices[index1]; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 78e6e40..16536d2 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -65,8 +65,36 @@ } /** + * Multiplies uvs array to transform + * @param {Float32Array} uvs mesh uvs + * @param {Float32Array} [out=uvs] output + * @returns {Float32Array} output + */ + multiplyUvs(uvs, out) + { + if (out === undefined) + { + out = uvs; + } + + const mat = this.mapCoord; + + for (let i = 0; i < uvs.length; i += 2) + { + const x = uvs[i]; + const y = uvs[i + 1]; + + out[i] = (x * mat.a) + (y * mat.c) + mat.tx; + out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; + } + + return out; + } + + /** * updates matrices if texture was changed * @param {boolean} forceUpdate if true, matrices will be updated any case + * @returns {boolean} whether or not it was updated */ update(forceUpdate) { @@ -74,13 +102,13 @@ if (!tex || !tex.valid) { - return; + return false; } if (!forceUpdate && this._lastTextureID === tex._updateID) { - return; + return false; } this._lastTextureID = tex._updateID; @@ -110,5 +138,7 @@ frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; this.uClampOffset[0] = offset / texBase.realWidth; this.uClampOffset[1] = offset / texBase.realHeight; + + return true; } } diff --git a/src/extras/index.js b/src/extras/index.js index bbd8bbf..6e8d039 100644 --- a/src/extras/index.js +++ b/src/extras/index.js @@ -1,7 +1,6 @@ /** * @namespace PIXI.extras */ -export { default as TextureTransform } from './TextureTransform'; export { default as AnimatedSprite } from './AnimatedSprite'; export { default as TilingSprite } from './TilingSprite'; export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer'; diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 6568231..5734fc0 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -10,7 +10,7 @@ * WebGL renderer plugin for tiling sprites * * @class - * @memberof PIXI + * @memberof PIXI.extras * @extends PIXI.ObjectRenderer */ export default class TilingSpriteRenderer extends core.ObjectRenderer diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 02c04a0..dc8c88d 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,4 +1,5 @@ import * as core from '../core'; +import { default as TextureTransform } from '../extras/TextureTransform'; const tempPoint = new core.Point(); const tempPolygon = new core.Polygon(); @@ -28,7 +29,7 @@ * @member {PIXI.Texture} * @private */ - this._texture = null; + this._texture = texture; /** * The Uvs of the Mesh @@ -98,9 +99,6 @@ */ this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; - // run texture setter; - this.texture = texture; - /** * The default shader that is used if a mesh doesn't have a more specific one. * @@ -125,9 +123,27 @@ this._glDatas = {}; /** + * transform that is applied to UV to get the texture coords + * its updated independently from texture uvTransform + * updates of uvs are tied to that thing + * + * @member {PIXI.extras.TextureTransform} + * @private + */ + this._uvTransform = new TextureTransform(texture); + + /** + * whether or not upload uvTransform 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.uploadUvTransform = false; + + /** * Plugin that is responsible for rendering this element. * Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods. - * * @member {string} * @default 'mesh' */ @@ -142,6 +158,7 @@ */ _renderWebGL(renderer) { + this.refresh(); renderer.setObjectRenderer(renderer.plugins[this.pluginName]); renderer.plugins[this.pluginName].render(this); } @@ -154,6 +171,7 @@ */ _renderCanvas(renderer) { + this.refresh(); renderer.plugins[this.pluginName].render(this); } @@ -164,6 +182,43 @@ */ _onTextureUpdate() { + this._uvTransform.texture = this._texture; + this.refresh(); + } + + /** + * multiplies uvs only if uploadUvTransform is false + * call it after you change uvs manually + * make sure that texture is valid + */ + multiplyUvs() + { + if (!this.uploadUvTransform) + { + this._uvTransform.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._uvTransform.update(forceUpdate)) + { + this._refresh(); + } + } + + /** + * re-calculates mesh coords + * @protected + */ + _refresh() + { /* empty */ } diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js index b36ea3d..c243246 100644 --- a/src/mesh/NineSlicePlane.js +++ b/src/mesh/NineSlicePlane.js @@ -115,6 +115,8 @@ * @override */ this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE; + + this.refresh(true); } /** diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js index 3869a39..4f9b51c 100644 --- a/src/mesh/Plane.js +++ b/src/mesh/Plane.js @@ -43,17 +43,17 @@ } /** - * Refreshes + * Refreshes plane coordinates * */ - refresh() + _refresh() { + const texture = this._texture; const total = this.verticesX * this.verticesY; const verts = []; const colors = []; const uvs = []; const indices = []; - const texture = this.texture; const segmentsX = this.verticesX - 1; const segmentsY = this.verticesY - 1; @@ -63,24 +63,12 @@ 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 @@ -106,8 +94,9 @@ this.uvs = new Float32Array(uvs); this.colors = new Float32Array(colors); this.indices = new Uint16Array(indices); - this.indexDirty = true; + + this.multiplyUvs(); } /** diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js index 6ff8aaf..299cf3d 100644 --- a/src/mesh/Rope.js +++ b/src/mesh/Rope.js @@ -1,5 +1,4 @@ import Mesh from './Mesh'; -import * as core from '../core'; /** * The rope allows you to draw a texture across several points and them manipulate these points @@ -52,13 +51,11 @@ this.indices = new Uint16Array(points.length * 2); /** - * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can - * call _onTextureUpdated which could call refresh too early. - * + * refreshes vertices on every updateTransform * @member {boolean} - * @private + * @default true */ - this._ready = true; + this.autoUpdate = true; this.refresh(); } @@ -67,7 +64,7 @@ * Refreshes * */ - refresh() + _refresh() { const points = this.points; @@ -91,14 +88,10 @@ const indices = this.indices; const colors = this.colors; - const textureUvs = this._texture._uvs; - const offset = new core.Point(textureUvs.x0, textureUvs.y0); - const factor = new core.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; colors[0] = 1; colors[1] = 1; @@ -114,11 +107,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; colors[index] = 1; @@ -132,30 +125,15 @@ // ensure that the changes are uploaded this.dirty++; this.indexDirty++; + + this.multiplyUvs(); + this.refreshVertices(); } /** - * Clear texture UVs when new texture is set - * - * @private + * refreshes vertices of Rope mesh */ - _onTextureUpdate() - { - super._onTextureUpdate(); - - // wait for the Rope ctor to finish before calling refresh - if (this._ready) - { - this.refresh(); - } - } - - /** - * Updates the object transform for rendering - * - * @private - */ - updateTransform() + refreshVertices() { const points = this.points; @@ -214,7 +192,19 @@ lastPoint = point; } + } + /** + * Updates the object transform for rendering + * + * @private + */ + updateTransform() + { + if (this.autoUpdate) + { + this.refreshVertices(); + } this.containerUpdateTransform(); } diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js index 1263170..871612d 100644 --- a/src/mesh/canvas/CanvasMeshRenderer.js +++ b/src/mesh/canvas/CanvasMeshRenderer.js @@ -135,12 +135,24 @@ const textureWidth = base.width; const textureHeight = base.height; - const u0 = uvs[index0] * base.width; - const u1 = uvs[index1] * base.width; - const u2 = uvs[index2] * base.width; - const v0 = uvs[index0 + 1] * base.height; - const v1 = uvs[index1 + 1] * base.height; - const v2 = uvs[index2 + 1] * base.height; + let u0 = uvs[index0] * base.width; + let u1 = uvs[index1] * base.width; + let u2 = uvs[index2] * base.width; + let v0 = uvs[index0 + 1] * base.height; + let v1 = uvs[index1 + 1] * base.height; + let v2 = uvs[index2 + 1] * base.height; + + if (mesh.uploadUvTransform) + { + const ut = mesh._uvTransform.mapCoord; + + u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width; + u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width; + u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width; + v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height; + v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height; + v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height; + } let x0 = vertices[index0]; let x1 = vertices[index1]; diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js index 8e61919..9d59adb 100644 --- a/src/mesh/webgl/MeshRenderer.js +++ b/src/mesh/webgl/MeshRenderer.js @@ -4,6 +4,8 @@ import { readFileSync } from 'fs'; import { join } from 'path'; +const matrixIdentity = core.Matrix.IDENTITY; + /** * WebGL renderer plugin for tiling sprites * @@ -104,6 +106,17 @@ renderer.state.setBlendMode(mesh.blendMode); + if (glData.shader.uniforms.uTransform) + { + if (mesh.uploadUvTransform) + { + glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true); + } + else + { + glData.shader.uniforms.uTransform = matrixIdentity.toArray(true); + } + } glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); glData.shader.uniforms.alpha = mesh.worldAlpha; glData.shader.uniforms.tint = mesh.tintRgb; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 78e6e40..16536d2 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -65,8 +65,36 @@ } /** + * Multiplies uvs array to transform + * @param {Float32Array} uvs mesh uvs + * @param {Float32Array} [out=uvs] output + * @returns {Float32Array} output + */ + multiplyUvs(uvs, out) + { + if (out === undefined) + { + out = uvs; + } + + const mat = this.mapCoord; + + for (let i = 0; i < uvs.length; i += 2) + { + const x = uvs[i]; + const y = uvs[i + 1]; + + out[i] = (x * mat.a) + (y * mat.c) + mat.tx; + out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty; + } + + return out; + } + + /** * updates matrices if texture was changed * @param {boolean} forceUpdate if true, matrices will be updated any case + * @returns {boolean} whether or not it was updated */ update(forceUpdate) { @@ -74,13 +102,13 @@ if (!tex || !tex.valid) { - return; + return false; } if (!forceUpdate && this._lastTextureID === tex._updateID) { - return; + return false; } this._lastTextureID = tex._updateID; @@ -110,5 +138,7 @@ frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height; this.uClampOffset[0] = offset / texBase.realWidth; this.uClampOffset[1] = offset / texBase.realHeight; + + return true; } } diff --git a/src/extras/index.js b/src/extras/index.js index bbd8bbf..6e8d039 100644 --- a/src/extras/index.js +++ b/src/extras/index.js @@ -1,7 +1,6 @@ /** * @namespace PIXI.extras */ -export { default as TextureTransform } from './TextureTransform'; export { default as AnimatedSprite } from './AnimatedSprite'; export { default as TilingSprite } from './TilingSprite'; export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer'; diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 6568231..5734fc0 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -10,7 +10,7 @@ * WebGL renderer plugin for tiling sprites * * @class - * @memberof PIXI + * @memberof PIXI.extras * @extends PIXI.ObjectRenderer */ export default class TilingSpriteRenderer extends core.ObjectRenderer diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 02c04a0..dc8c88d 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,4 +1,5 @@ import * as core from '../core'; +import { default as TextureTransform } from '../extras/TextureTransform'; const tempPoint = new core.Point(); const tempPolygon = new core.Polygon(); @@ -28,7 +29,7 @@ * @member {PIXI.Texture} * @private */ - this._texture = null; + this._texture = texture; /** * The Uvs of the Mesh @@ -98,9 +99,6 @@ */ this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; - // run texture setter; - this.texture = texture; - /** * The default shader that is used if a mesh doesn't have a more specific one. * @@ -125,9 +123,27 @@ this._glDatas = {}; /** + * transform that is applied to UV to get the texture coords + * its updated independently from texture uvTransform + * updates of uvs are tied to that thing + * + * @member {PIXI.extras.TextureTransform} + * @private + */ + this._uvTransform = new TextureTransform(texture); + + /** + * whether or not upload uvTransform 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.uploadUvTransform = false; + + /** * Plugin that is responsible for rendering this element. * Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods. - * * @member {string} * @default 'mesh' */ @@ -142,6 +158,7 @@ */ _renderWebGL(renderer) { + this.refresh(); renderer.setObjectRenderer(renderer.plugins[this.pluginName]); renderer.plugins[this.pluginName].render(this); } @@ -154,6 +171,7 @@ */ _renderCanvas(renderer) { + this.refresh(); renderer.plugins[this.pluginName].render(this); } @@ -164,6 +182,43 @@ */ _onTextureUpdate() { + this._uvTransform.texture = this._texture; + this.refresh(); + } + + /** + * multiplies uvs only if uploadUvTransform is false + * call it after you change uvs manually + * make sure that texture is valid + */ + multiplyUvs() + { + if (!this.uploadUvTransform) + { + this._uvTransform.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._uvTransform.update(forceUpdate)) + { + this._refresh(); + } + } + + /** + * re-calculates mesh coords + * @protected + */ + _refresh() + { /* empty */ } diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js index b36ea3d..c243246 100644 --- a/src/mesh/NineSlicePlane.js +++ b/src/mesh/NineSlicePlane.js @@ -115,6 +115,8 @@ * @override */ this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE; + + this.refresh(true); } /** diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js index 3869a39..4f9b51c 100644 --- a/src/mesh/Plane.js +++ b/src/mesh/Plane.js @@ -43,17 +43,17 @@ } /** - * Refreshes + * Refreshes plane coordinates * */ - refresh() + _refresh() { + const texture = this._texture; const total = this.verticesX * this.verticesY; const verts = []; const colors = []; const uvs = []; const indices = []; - const texture = this.texture; const segmentsX = this.verticesX - 1; const segmentsY = this.verticesY - 1; @@ -63,24 +63,12 @@ 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 @@ -106,8 +94,9 @@ this.uvs = new Float32Array(uvs); this.colors = new Float32Array(colors); this.indices = new Uint16Array(indices); - this.indexDirty = true; + + this.multiplyUvs(); } /** diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js index 6ff8aaf..299cf3d 100644 --- a/src/mesh/Rope.js +++ b/src/mesh/Rope.js @@ -1,5 +1,4 @@ import Mesh from './Mesh'; -import * as core from '../core'; /** * The rope allows you to draw a texture across several points and them manipulate these points @@ -52,13 +51,11 @@ this.indices = new Uint16Array(points.length * 2); /** - * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can - * call _onTextureUpdated which could call refresh too early. - * + * refreshes vertices on every updateTransform * @member {boolean} - * @private + * @default true */ - this._ready = true; + this.autoUpdate = true; this.refresh(); } @@ -67,7 +64,7 @@ * Refreshes * */ - refresh() + _refresh() { const points = this.points; @@ -91,14 +88,10 @@ const indices = this.indices; const colors = this.colors; - const textureUvs = this._texture._uvs; - const offset = new core.Point(textureUvs.x0, textureUvs.y0); - const factor = new core.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; colors[0] = 1; colors[1] = 1; @@ -114,11 +107,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; colors[index] = 1; @@ -132,30 +125,15 @@ // ensure that the changes are uploaded this.dirty++; this.indexDirty++; + + this.multiplyUvs(); + this.refreshVertices(); } /** - * Clear texture UVs when new texture is set - * - * @private + * refreshes vertices of Rope mesh */ - _onTextureUpdate() - { - super._onTextureUpdate(); - - // wait for the Rope ctor to finish before calling refresh - if (this._ready) - { - this.refresh(); - } - } - - /** - * Updates the object transform for rendering - * - * @private - */ - updateTransform() + refreshVertices() { const points = this.points; @@ -214,7 +192,19 @@ lastPoint = point; } + } + /** + * Updates the object transform for rendering + * + * @private + */ + updateTransform() + { + if (this.autoUpdate) + { + this.refreshVertices(); + } this.containerUpdateTransform(); } diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js index 1263170..871612d 100644 --- a/src/mesh/canvas/CanvasMeshRenderer.js +++ b/src/mesh/canvas/CanvasMeshRenderer.js @@ -135,12 +135,24 @@ const textureWidth = base.width; const textureHeight = base.height; - const u0 = uvs[index0] * base.width; - const u1 = uvs[index1] * base.width; - const u2 = uvs[index2] * base.width; - const v0 = uvs[index0 + 1] * base.height; - const v1 = uvs[index1 + 1] * base.height; - const v2 = uvs[index2 + 1] * base.height; + let u0 = uvs[index0] * base.width; + let u1 = uvs[index1] * base.width; + let u2 = uvs[index2] * base.width; + let v0 = uvs[index0 + 1] * base.height; + let v1 = uvs[index1 + 1] * base.height; + let v2 = uvs[index2 + 1] * base.height; + + if (mesh.uploadUvTransform) + { + const ut = mesh._uvTransform.mapCoord; + + u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width; + u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width; + u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width; + v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height; + v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height; + v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height; + } let x0 = vertices[index0]; let x1 = vertices[index1]; diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js index 8e61919..9d59adb 100644 --- a/src/mesh/webgl/MeshRenderer.js +++ b/src/mesh/webgl/MeshRenderer.js @@ -4,6 +4,8 @@ import { readFileSync } from 'fs'; import { join } from 'path'; +const matrixIdentity = core.Matrix.IDENTITY; + /** * WebGL renderer plugin for tiling sprites * @@ -104,6 +106,17 @@ renderer.state.setBlendMode(mesh.blendMode); + if (glData.shader.uniforms.uTransform) + { + if (mesh.uploadUvTransform) + { + glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true); + } + else + { + glData.shader.uniforms.uTransform = matrixIdentity.toArray(true); + } + } glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); glData.shader.uniforms.alpha = mesh.worldAlpha; glData.shader.uniforms.tint = mesh.tintRgb; diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert index a337aef..acc096c 100644 --- a/src/mesh/webgl/mesh.vert +++ b/src/mesh/webgl/mesh.vert @@ -1,8 +1,9 @@ attribute vec2 aVertexPosition; attribute vec2 aTextureCoord; -uniform mat3 translationMatrix; uniform mat3 projectionMatrix; +uniform mat3 translationMatrix; +uniform mat3 uTransform; varying vec2 vTextureCoord; @@ -10,5 +11,5 @@ { gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); - vTextureCoord = aTextureCoord; + vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy; }