diff --git a/package.json b/package.json index bc669c9..f149fa7 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ ], "dependencies": { "async": "^1.5.0", + "bit-twiddle": "^1.0.2", "brfs": "^1.4.1", "earcut": "^2.0.7", "eventemitter3": "^1.1.1", diff --git a/package.json b/package.json index bc669c9..f149fa7 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ ], "dependencies": { "async": "^1.5.0", + "bit-twiddle": "^1.0.2", "brfs": "^1.4.1", "earcut": "^2.0.7", "eventemitter3": "^1.1.1", diff --git a/src/core/display/ObservablePoint.js b/src/core/display/ObservablePoint.js index fd7a04b..958661f 100644 --- a/src/core/display/ObservablePoint.js +++ b/src/core/display/ObservablePoint.js @@ -35,7 +35,7 @@ set: function (value) { this._x = value; - this.transform.dirty = true; + this.transform._versionLocal++; } }, @@ -47,7 +47,7 @@ set: function (value) { this._y = value; - this.transform.dirty = true; + this.transform._versionLocal++; } } }); @@ -63,6 +63,6 @@ { this._x = x || 0; this._y = y || ( (y !== 0) ? this._x : 0 ); - - this.transform.dirty = true; + + this.transform._versionLocal++; }; diff --git a/package.json b/package.json index bc669c9..f149fa7 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ ], "dependencies": { "async": "^1.5.0", + "bit-twiddle": "^1.0.2", "brfs": "^1.4.1", "earcut": "^2.0.7", "eventemitter3": "^1.1.1", diff --git a/src/core/display/ObservablePoint.js b/src/core/display/ObservablePoint.js index fd7a04b..958661f 100644 --- a/src/core/display/ObservablePoint.js +++ b/src/core/display/ObservablePoint.js @@ -35,7 +35,7 @@ set: function (value) { this._x = value; - this.transform.dirty = true; + this.transform._versionLocal++; } }, @@ -47,7 +47,7 @@ set: function (value) { this._y = value; - this.transform.dirty = true; + this.transform._versionLocal++; } } }); @@ -63,6 +63,6 @@ { this._x = x || 0; this._y = y || ( (y !== 0) ? this._x : 0 ); - - this.transform.dirty = true; + + this.transform._versionLocal++; }; diff --git a/src/core/display/TransformStatic.js b/src/core/display/TransformStatic.js index 61161d0..b86aa2d 100644 --- a/src/core/display/TransformStatic.js +++ b/src/core/display/TransformStatic.js @@ -1,6 +1,7 @@ var math = require('../math'); var ObservablePoint = require('./ObservablePoint'); +var generatorId = 0; /** * The Point object represents a location in a two-dimensional coordinate system, where x represents * the horizontal axis and y represents the vertical axis. @@ -19,13 +20,17 @@ this.scale = new ObservablePoint(this,1, 1); this.pivot = new ObservablePoint(this, 0, 0); this.skew = new ObservablePoint(this, 0,0); - + this.rotation = 0; this._sr = Math.sin(0); this._cr = Math.cos(0); - - this.dirty = true; - this.updated = true; + + this._versionLocal = 0; + this._versionGlobal = 0; + this._dirtyLocal = 0; + this._dirtyParentVersion = -1; + this._dirtyParentId = -1; + this._transformId = ++generatorId; } TransformStatic.prototype.constructor = TransformStatic; @@ -36,21 +41,21 @@ var wt = this.worldTransform; var lt = this.localTransform; - if(this.dirty) + if(this._dirtyLocal !== this._versionLocal || + parentTransform._dirtyParentId !== parentTransform._transformId || + parentTransform._dirtyParentVersion !== parentTransform._versionGlobal ) { - // get the matrix values of the displayobject based on its transform properties.. - lt.a = this._cr * this.scale._x; - lt.b = this._sr * this.scale._x; - lt.c = -this._sr * this.scale._y; - lt.d = this._cr * this.scale._y; - lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); - lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); - } - - this.updated = this.dirty || parentTransform.updated; - - if(this.updated) - { + if(this._dirtyLocal !== this._versionLocal) + { + // get the matrix values of the displayobject based on its transform properties.. + lt.a = this._cr * this.scale._x; + lt.b = this._sr * this.scale._x; + lt.c = -this._sr * this.scale._y; + lt.d = this._cr * this.scale._y; + lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); + lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); + this._dirtyLocal = this._versionLocal; + } // concat the parent matrix with the objects transform. wt.a = lt.a * pt.a + lt.b * pt.c; wt.b = lt.a * pt.b + lt.b * pt.d; @@ -58,10 +63,12 @@ wt.d = lt.c * pt.b + lt.d * pt.d; wt.tx = lt.tx * pt.a + lt.ty * pt.c + pt.tx; wt.ty = lt.tx * pt.b + lt.ty * pt.d + pt.ty; + + this._dirtyParentId = parentTransform._transformId; + this._dirtyParentVersion = parentTransform._versionGlobal; + this._versionGlobal++; } - - this.dirty = false; -} +}; module.exports = TransformStatic; diff --git a/package.json b/package.json index bc669c9..f149fa7 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ ], "dependencies": { "async": "^1.5.0", + "bit-twiddle": "^1.0.2", "brfs": "^1.4.1", "earcut": "^2.0.7", "eventemitter3": "^1.1.1", diff --git a/src/core/display/ObservablePoint.js b/src/core/display/ObservablePoint.js index fd7a04b..958661f 100644 --- a/src/core/display/ObservablePoint.js +++ b/src/core/display/ObservablePoint.js @@ -35,7 +35,7 @@ set: function (value) { this._x = value; - this.transform.dirty = true; + this.transform._versionLocal++; } }, @@ -47,7 +47,7 @@ set: function (value) { this._y = value; - this.transform.dirty = true; + this.transform._versionLocal++; } } }); @@ -63,6 +63,6 @@ { this._x = x || 0; this._y = y || ( (y !== 0) ? this._x : 0 ); - - this.transform.dirty = true; + + this.transform._versionLocal++; }; diff --git a/src/core/display/TransformStatic.js b/src/core/display/TransformStatic.js index 61161d0..b86aa2d 100644 --- a/src/core/display/TransformStatic.js +++ b/src/core/display/TransformStatic.js @@ -1,6 +1,7 @@ var math = require('../math'); var ObservablePoint = require('./ObservablePoint'); +var generatorId = 0; /** * The Point object represents a location in a two-dimensional coordinate system, where x represents * the horizontal axis and y represents the vertical axis. @@ -19,13 +20,17 @@ this.scale = new ObservablePoint(this,1, 1); this.pivot = new ObservablePoint(this, 0, 0); this.skew = new ObservablePoint(this, 0,0); - + this.rotation = 0; this._sr = Math.sin(0); this._cr = Math.cos(0); - - this.dirty = true; - this.updated = true; + + this._versionLocal = 0; + this._versionGlobal = 0; + this._dirtyLocal = 0; + this._dirtyParentVersion = -1; + this._dirtyParentId = -1; + this._transformId = ++generatorId; } TransformStatic.prototype.constructor = TransformStatic; @@ -36,21 +41,21 @@ var wt = this.worldTransform; var lt = this.localTransform; - if(this.dirty) + if(this._dirtyLocal !== this._versionLocal || + parentTransform._dirtyParentId !== parentTransform._transformId || + parentTransform._dirtyParentVersion !== parentTransform._versionGlobal ) { - // get the matrix values of the displayobject based on its transform properties.. - lt.a = this._cr * this.scale._x; - lt.b = this._sr * this.scale._x; - lt.c = -this._sr * this.scale._y; - lt.d = this._cr * this.scale._y; - lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); - lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); - } - - this.updated = this.dirty || parentTransform.updated; - - if(this.updated) - { + if(this._dirtyLocal !== this._versionLocal) + { + // get the matrix values of the displayobject based on its transform properties.. + lt.a = this._cr * this.scale._x; + lt.b = this._sr * this.scale._x; + lt.c = -this._sr * this.scale._y; + lt.d = this._cr * this.scale._y; + lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); + lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); + this._dirtyLocal = this._versionLocal; + } // concat the parent matrix with the objects transform. wt.a = lt.a * pt.a + lt.b * pt.c; wt.b = lt.a * pt.b + lt.b * pt.d; @@ -58,10 +63,12 @@ wt.d = lt.c * pt.b + lt.d * pt.d; wt.tx = lt.tx * pt.a + lt.ty * pt.c + pt.tx; wt.ty = lt.tx * pt.b + lt.ty * pt.d + pt.ty; + + this._dirtyParentId = parentTransform._transformId; + this._dirtyParentVersion = parentTransform._versionGlobal; + this._versionGlobal++; } - - this.dirty = false; -} +}; module.exports = TransformStatic; diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js new file mode 100644 index 0000000..1edb988 --- /dev/null +++ b/src/core/math/GroupD8.js @@ -0,0 +1,162 @@ +// Your friendly neighbour https://en.wikipedia.org/wiki/Dihedral_group of order 16 + +var ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1]; +var uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1]; +var tempMatrices = []; +var Matrix = require('./Matrix'); + +var mul = []; + +function signum(x) { + if (x < 0) { + return -1; + } + if (x > 0) { + return 1; + } + return 0; +} + +function init() { + for (var i = 0; i < 16; i++) { + var row = []; + mul.push(row); + for (var j = 0; j < 16; j++) { + var _ux = signum(ux[i] * ux[j] + vx[i] * uy[j]); + var _uy = signum(uy[i] * ux[j] + vy[i] * uy[j]); + var _vx = signum(ux[i] * vx[j] + vx[i] * vy[j]); + var _vy = signum(uy[i] * vx[j] + vy[i] * vy[j]); + for (var k = 0; k < 16; k++) { + if (ux[k] === _ux && uy[k] === _uy && vx[k] === _vx && vy[k] === _vy) { + row.push(k); + break; + } + } + } + } + + for (i=0;i<16;i++) { + var mat = new Matrix(); + mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0); + tempMatrices.push(mat); + } +} + +init(); + +/** + * Implements Dihedral Group D_8, see [group D4]{@link http://mathworld.wolfram.com/DihedralGroupD4.html}, D8 is the same but with diagonals + * Used for texture rotations + * Vector xX(i), xY(i) is U-axis of sprite with rotation i + * Vector yY(i), yY(i) is V-axis of sprite with rotation i + * Rotations: 0 grad (0), 90 grad (2), 180 grad (4), 270 grad (6) + * Mirrors: vertical (8), main diagonal (10), horizontal (12), reverse diagonal (14) + * This is the small part of gameofbombs.com portal system. It works. + * @author Ivan @ivanpopelyshev + * + * @namespace PIXI.GroupD8 + */ +var GroupD8 = { + E: 0, + SE: 1, + S: 2, + SW: 3, + W: 4, + NW: 5, + N: 6, + NE: 7, + MIRROR_VERTICAL: 8, + MIRROR_HORIZONTAL: 12, + uX: function (ind) { + return ux[ind]; + }, + uY: function (ind) { + return uy[ind]; + }, + vX: function (ind) { + return vx[ind]; + }, + vY: function (ind) { + return vy[ind]; + }, + inv: function (rotation) { + if (rotation & 8) { + return rotation & 15; + } + return (-rotation) & 7; + }, + add: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][rotationFirst]; + }, + sub: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][GroupD8.inv(rotationFirst)]; + }, + /** + * Adds 180 degrees to rotation. Commutative operation + * @param rotation + * @returns {number} + */ + rotate180: function (rotation) { + return rotation ^ 4; + }, + /** + * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * @param rotation + * @returns {boolean} + */ + isSwapWidthHeight: function(rotation) { + return (rotation & 3) === 2; + }, + byDirection: function (dx, dy) { + if (Math.abs(dx) * 2 <= Math.abs(dy)) { + if (dy >= 0) { + return GroupD8.S; + } + else { + return GroupD8.N; + } + } else if (Math.abs(dy) * 2 <= Math.abs(dx)) { + if (dx > 0) { + return GroupD8.E; + } + else { + return GroupD8.W; + } + } else { + if (dy > 0) { + if (dx > 0) { + return GroupD8.SE; + } + else { + return GroupD8.SW; + } + } + else if (dx > 0) { + return GroupD8.NE; + } + else { + return GroupD8.NW; + } + } + }, + /** + * Helps sprite to compensate texture packer rotation. + * @param matrix {PIXI.Matrix} sprite world matrix + * @param rotation {number} + * @param tx {number|*} sprite anchoring + * @param ty {number|*} sprite anchoring + */ + matrixAppendRotationInv: function (matrix, rotation, tx, ty) { + //Packer used "rotation", we use "inv(rotation)" + var mat = tempMatrices[GroupD8.inv(rotation)]; + tx = tx || 0; + ty = ty || 0; + mat.tx = tx; + mat.ty = ty; + matrix.append(mat); + } +}; + +module.exports = GroupD8; diff --git a/package.json b/package.json index bc669c9..f149fa7 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ ], "dependencies": { "async": "^1.5.0", + "bit-twiddle": "^1.0.2", "brfs": "^1.4.1", "earcut": "^2.0.7", "eventemitter3": "^1.1.1", diff --git a/src/core/display/ObservablePoint.js b/src/core/display/ObservablePoint.js index fd7a04b..958661f 100644 --- a/src/core/display/ObservablePoint.js +++ b/src/core/display/ObservablePoint.js @@ -35,7 +35,7 @@ set: function (value) { this._x = value; - this.transform.dirty = true; + this.transform._versionLocal++; } }, @@ -47,7 +47,7 @@ set: function (value) { this._y = value; - this.transform.dirty = true; + this.transform._versionLocal++; } } }); @@ -63,6 +63,6 @@ { this._x = x || 0; this._y = y || ( (y !== 0) ? this._x : 0 ); - - this.transform.dirty = true; + + this.transform._versionLocal++; }; diff --git a/src/core/display/TransformStatic.js b/src/core/display/TransformStatic.js index 61161d0..b86aa2d 100644 --- a/src/core/display/TransformStatic.js +++ b/src/core/display/TransformStatic.js @@ -1,6 +1,7 @@ var math = require('../math'); var ObservablePoint = require('./ObservablePoint'); +var generatorId = 0; /** * The Point object represents a location in a two-dimensional coordinate system, where x represents * the horizontal axis and y represents the vertical axis. @@ -19,13 +20,17 @@ this.scale = new ObservablePoint(this,1, 1); this.pivot = new ObservablePoint(this, 0, 0); this.skew = new ObservablePoint(this, 0,0); - + this.rotation = 0; this._sr = Math.sin(0); this._cr = Math.cos(0); - - this.dirty = true; - this.updated = true; + + this._versionLocal = 0; + this._versionGlobal = 0; + this._dirtyLocal = 0; + this._dirtyParentVersion = -1; + this._dirtyParentId = -1; + this._transformId = ++generatorId; } TransformStatic.prototype.constructor = TransformStatic; @@ -36,21 +41,21 @@ var wt = this.worldTransform; var lt = this.localTransform; - if(this.dirty) + if(this._dirtyLocal !== this._versionLocal || + parentTransform._dirtyParentId !== parentTransform._transformId || + parentTransform._dirtyParentVersion !== parentTransform._versionGlobal ) { - // get the matrix values of the displayobject based on its transform properties.. - lt.a = this._cr * this.scale._x; - lt.b = this._sr * this.scale._x; - lt.c = -this._sr * this.scale._y; - lt.d = this._cr * this.scale._y; - lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); - lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); - } - - this.updated = this.dirty || parentTransform.updated; - - if(this.updated) - { + if(this._dirtyLocal !== this._versionLocal) + { + // get the matrix values of the displayobject based on its transform properties.. + lt.a = this._cr * this.scale._x; + lt.b = this._sr * this.scale._x; + lt.c = -this._sr * this.scale._y; + lt.d = this._cr * this.scale._y; + lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); + lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); + this._dirtyLocal = this._versionLocal; + } // concat the parent matrix with the objects transform. wt.a = lt.a * pt.a + lt.b * pt.c; wt.b = lt.a * pt.b + lt.b * pt.d; @@ -58,10 +63,12 @@ wt.d = lt.c * pt.b + lt.d * pt.d; wt.tx = lt.tx * pt.a + lt.ty * pt.c + pt.tx; wt.ty = lt.tx * pt.b + lt.ty * pt.d + pt.ty; + + this._dirtyParentId = parentTransform._transformId; + this._dirtyParentVersion = parentTransform._versionGlobal; + this._versionGlobal++; } - - this.dirty = false; -} +}; module.exports = TransformStatic; diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js new file mode 100644 index 0000000..1edb988 --- /dev/null +++ b/src/core/math/GroupD8.js @@ -0,0 +1,162 @@ +// Your friendly neighbour https://en.wikipedia.org/wiki/Dihedral_group of order 16 + +var ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1]; +var uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1]; +var tempMatrices = []; +var Matrix = require('./Matrix'); + +var mul = []; + +function signum(x) { + if (x < 0) { + return -1; + } + if (x > 0) { + return 1; + } + return 0; +} + +function init() { + for (var i = 0; i < 16; i++) { + var row = []; + mul.push(row); + for (var j = 0; j < 16; j++) { + var _ux = signum(ux[i] * ux[j] + vx[i] * uy[j]); + var _uy = signum(uy[i] * ux[j] + vy[i] * uy[j]); + var _vx = signum(ux[i] * vx[j] + vx[i] * vy[j]); + var _vy = signum(uy[i] * vx[j] + vy[i] * vy[j]); + for (var k = 0; k < 16; k++) { + if (ux[k] === _ux && uy[k] === _uy && vx[k] === _vx && vy[k] === _vy) { + row.push(k); + break; + } + } + } + } + + for (i=0;i<16;i++) { + var mat = new Matrix(); + mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0); + tempMatrices.push(mat); + } +} + +init(); + +/** + * Implements Dihedral Group D_8, see [group D4]{@link http://mathworld.wolfram.com/DihedralGroupD4.html}, D8 is the same but with diagonals + * Used for texture rotations + * Vector xX(i), xY(i) is U-axis of sprite with rotation i + * Vector yY(i), yY(i) is V-axis of sprite with rotation i + * Rotations: 0 grad (0), 90 grad (2), 180 grad (4), 270 grad (6) + * Mirrors: vertical (8), main diagonal (10), horizontal (12), reverse diagonal (14) + * This is the small part of gameofbombs.com portal system. It works. + * @author Ivan @ivanpopelyshev + * + * @namespace PIXI.GroupD8 + */ +var GroupD8 = { + E: 0, + SE: 1, + S: 2, + SW: 3, + W: 4, + NW: 5, + N: 6, + NE: 7, + MIRROR_VERTICAL: 8, + MIRROR_HORIZONTAL: 12, + uX: function (ind) { + return ux[ind]; + }, + uY: function (ind) { + return uy[ind]; + }, + vX: function (ind) { + return vx[ind]; + }, + vY: function (ind) { + return vy[ind]; + }, + inv: function (rotation) { + if (rotation & 8) { + return rotation & 15; + } + return (-rotation) & 7; + }, + add: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][rotationFirst]; + }, + sub: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][GroupD8.inv(rotationFirst)]; + }, + /** + * Adds 180 degrees to rotation. Commutative operation + * @param rotation + * @returns {number} + */ + rotate180: function (rotation) { + return rotation ^ 4; + }, + /** + * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * @param rotation + * @returns {boolean} + */ + isSwapWidthHeight: function(rotation) { + return (rotation & 3) === 2; + }, + byDirection: function (dx, dy) { + if (Math.abs(dx) * 2 <= Math.abs(dy)) { + if (dy >= 0) { + return GroupD8.S; + } + else { + return GroupD8.N; + } + } else if (Math.abs(dy) * 2 <= Math.abs(dx)) { + if (dx > 0) { + return GroupD8.E; + } + else { + return GroupD8.W; + } + } else { + if (dy > 0) { + if (dx > 0) { + return GroupD8.SE; + } + else { + return GroupD8.SW; + } + } + else if (dx > 0) { + return GroupD8.NE; + } + else { + return GroupD8.NW; + } + } + }, + /** + * Helps sprite to compensate texture packer rotation. + * @param matrix {PIXI.Matrix} sprite world matrix + * @param rotation {number} + * @param tx {number|*} sprite anchoring + * @param ty {number|*} sprite anchoring + */ + matrixAppendRotationInv: function (matrix, rotation, tx, ty) { + //Packer used "rotation", we use "inv(rotation)" + var mat = tempMatrices[GroupD8.inv(rotation)]; + tx = tx || 0; + ty = ty || 0; + mat.tx = tx; + mat.ty = ty; + matrix.append(mat); + } +}; + +module.exports = GroupD8; diff --git a/src/core/math/index.js b/src/core/math/index.js index fd0d9be..a43f6e7 100644 --- a/src/core/math/index.js +++ b/src/core/math/index.js @@ -11,6 +11,7 @@ Point: require('./Point'), Matrix: require('./Matrix'), + GroupD8: require('./GroupD8'), Circle: require('./shapes/Circle'), Ellipse: require('./shapes/Ellipse'), diff --git a/package.json b/package.json index bc669c9..f149fa7 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ ], "dependencies": { "async": "^1.5.0", + "bit-twiddle": "^1.0.2", "brfs": "^1.4.1", "earcut": "^2.0.7", "eventemitter3": "^1.1.1", diff --git a/src/core/display/ObservablePoint.js b/src/core/display/ObservablePoint.js index fd7a04b..958661f 100644 --- a/src/core/display/ObservablePoint.js +++ b/src/core/display/ObservablePoint.js @@ -35,7 +35,7 @@ set: function (value) { this._x = value; - this.transform.dirty = true; + this.transform._versionLocal++; } }, @@ -47,7 +47,7 @@ set: function (value) { this._y = value; - this.transform.dirty = true; + this.transform._versionLocal++; } } }); @@ -63,6 +63,6 @@ { this._x = x || 0; this._y = y || ( (y !== 0) ? this._x : 0 ); - - this.transform.dirty = true; + + this.transform._versionLocal++; }; diff --git a/src/core/display/TransformStatic.js b/src/core/display/TransformStatic.js index 61161d0..b86aa2d 100644 --- a/src/core/display/TransformStatic.js +++ b/src/core/display/TransformStatic.js @@ -1,6 +1,7 @@ var math = require('../math'); var ObservablePoint = require('./ObservablePoint'); +var generatorId = 0; /** * The Point object represents a location in a two-dimensional coordinate system, where x represents * the horizontal axis and y represents the vertical axis. @@ -19,13 +20,17 @@ this.scale = new ObservablePoint(this,1, 1); this.pivot = new ObservablePoint(this, 0, 0); this.skew = new ObservablePoint(this, 0,0); - + this.rotation = 0; this._sr = Math.sin(0); this._cr = Math.cos(0); - - this.dirty = true; - this.updated = true; + + this._versionLocal = 0; + this._versionGlobal = 0; + this._dirtyLocal = 0; + this._dirtyParentVersion = -1; + this._dirtyParentId = -1; + this._transformId = ++generatorId; } TransformStatic.prototype.constructor = TransformStatic; @@ -36,21 +41,21 @@ var wt = this.worldTransform; var lt = this.localTransform; - if(this.dirty) + if(this._dirtyLocal !== this._versionLocal || + parentTransform._dirtyParentId !== parentTransform._transformId || + parentTransform._dirtyParentVersion !== parentTransform._versionGlobal ) { - // get the matrix values of the displayobject based on its transform properties.. - lt.a = this._cr * this.scale._x; - lt.b = this._sr * this.scale._x; - lt.c = -this._sr * this.scale._y; - lt.d = this._cr * this.scale._y; - lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); - lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); - } - - this.updated = this.dirty || parentTransform.updated; - - if(this.updated) - { + if(this._dirtyLocal !== this._versionLocal) + { + // get the matrix values of the displayobject based on its transform properties.. + lt.a = this._cr * this.scale._x; + lt.b = this._sr * this.scale._x; + lt.c = -this._sr * this.scale._y; + lt.d = this._cr * this.scale._y; + lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); + lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); + this._dirtyLocal = this._versionLocal; + } // concat the parent matrix with the objects transform. wt.a = lt.a * pt.a + lt.b * pt.c; wt.b = lt.a * pt.b + lt.b * pt.d; @@ -58,10 +63,12 @@ wt.d = lt.c * pt.b + lt.d * pt.d; wt.tx = lt.tx * pt.a + lt.ty * pt.c + pt.tx; wt.ty = lt.tx * pt.b + lt.ty * pt.d + pt.ty; + + this._dirtyParentId = parentTransform._transformId; + this._dirtyParentVersion = parentTransform._versionGlobal; + this._versionGlobal++; } - - this.dirty = false; -} +}; module.exports = TransformStatic; diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js new file mode 100644 index 0000000..1edb988 --- /dev/null +++ b/src/core/math/GroupD8.js @@ -0,0 +1,162 @@ +// Your friendly neighbour https://en.wikipedia.org/wiki/Dihedral_group of order 16 + +var ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1]; +var uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1]; +var tempMatrices = []; +var Matrix = require('./Matrix'); + +var mul = []; + +function signum(x) { + if (x < 0) { + return -1; + } + if (x > 0) { + return 1; + } + return 0; +} + +function init() { + for (var i = 0; i < 16; i++) { + var row = []; + mul.push(row); + for (var j = 0; j < 16; j++) { + var _ux = signum(ux[i] * ux[j] + vx[i] * uy[j]); + var _uy = signum(uy[i] * ux[j] + vy[i] * uy[j]); + var _vx = signum(ux[i] * vx[j] + vx[i] * vy[j]); + var _vy = signum(uy[i] * vx[j] + vy[i] * vy[j]); + for (var k = 0; k < 16; k++) { + if (ux[k] === _ux && uy[k] === _uy && vx[k] === _vx && vy[k] === _vy) { + row.push(k); + break; + } + } + } + } + + for (i=0;i<16;i++) { + var mat = new Matrix(); + mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0); + tempMatrices.push(mat); + } +} + +init(); + +/** + * Implements Dihedral Group D_8, see [group D4]{@link http://mathworld.wolfram.com/DihedralGroupD4.html}, D8 is the same but with diagonals + * Used for texture rotations + * Vector xX(i), xY(i) is U-axis of sprite with rotation i + * Vector yY(i), yY(i) is V-axis of sprite with rotation i + * Rotations: 0 grad (0), 90 grad (2), 180 grad (4), 270 grad (6) + * Mirrors: vertical (8), main diagonal (10), horizontal (12), reverse diagonal (14) + * This is the small part of gameofbombs.com portal system. It works. + * @author Ivan @ivanpopelyshev + * + * @namespace PIXI.GroupD8 + */ +var GroupD8 = { + E: 0, + SE: 1, + S: 2, + SW: 3, + W: 4, + NW: 5, + N: 6, + NE: 7, + MIRROR_VERTICAL: 8, + MIRROR_HORIZONTAL: 12, + uX: function (ind) { + return ux[ind]; + }, + uY: function (ind) { + return uy[ind]; + }, + vX: function (ind) { + return vx[ind]; + }, + vY: function (ind) { + return vy[ind]; + }, + inv: function (rotation) { + if (rotation & 8) { + return rotation & 15; + } + return (-rotation) & 7; + }, + add: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][rotationFirst]; + }, + sub: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][GroupD8.inv(rotationFirst)]; + }, + /** + * Adds 180 degrees to rotation. Commutative operation + * @param rotation + * @returns {number} + */ + rotate180: function (rotation) { + return rotation ^ 4; + }, + /** + * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * @param rotation + * @returns {boolean} + */ + isSwapWidthHeight: function(rotation) { + return (rotation & 3) === 2; + }, + byDirection: function (dx, dy) { + if (Math.abs(dx) * 2 <= Math.abs(dy)) { + if (dy >= 0) { + return GroupD8.S; + } + else { + return GroupD8.N; + } + } else if (Math.abs(dy) * 2 <= Math.abs(dx)) { + if (dx > 0) { + return GroupD8.E; + } + else { + return GroupD8.W; + } + } else { + if (dy > 0) { + if (dx > 0) { + return GroupD8.SE; + } + else { + return GroupD8.SW; + } + } + else if (dx > 0) { + return GroupD8.NE; + } + else { + return GroupD8.NW; + } + } + }, + /** + * Helps sprite to compensate texture packer rotation. + * @param matrix {PIXI.Matrix} sprite world matrix + * @param rotation {number} + * @param tx {number|*} sprite anchoring + * @param ty {number|*} sprite anchoring + */ + matrixAppendRotationInv: function (matrix, rotation, tx, ty) { + //Packer used "rotation", we use "inv(rotation)" + var mat = tempMatrices[GroupD8.inv(rotation)]; + tx = tx || 0; + ty = ty || 0; + mat.tx = tx; + mat.ty = ty; + matrix.append(mat); + } +}; + +module.exports = GroupD8; diff --git a/src/core/math/index.js b/src/core/math/index.js index fd0d9be..a43f6e7 100644 --- a/src/core/math/index.js +++ b/src/core/math/index.js @@ -11,6 +11,7 @@ Point: require('./Point'), Matrix: require('./Matrix'), + GroupD8: require('./GroupD8'), Circle: require('./shapes/Circle'), Ellipse: require('./shapes/Ellipse'), diff --git a/src/core/particles/webgl/ParticleRenderer.js b/src/core/particles/webgl/ParticleRenderer.js index 8481c92..0b0abd0 100644 --- a/src/core/particles/webgl/ParticleRenderer.js +++ b/src/core/particles/webgl/ParticleRenderer.js @@ -285,25 +285,25 @@ texture = sprite._texture; sx = sprite.scale.x; sy = sprite.scale.y; + var trim = texture.trim, crop = texture.crop; - if (texture.trim) + if (trim) { - // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. - trim = texture.trim; + // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space before transforming the sprite coords.. + w1 = trim.x - sprite.anchor.x * crop.width; + w0 = w1 + trim.width; - w1 = trim.x - sprite.anchor.x * trim.width; - w0 = w1 + texture.crop.width; + h1 = trim.y - sprite.anchor.y * crop.height; + h0 = h1 + trim.height; - h1 = trim.y - sprite.anchor.y * trim.height; - h0 = h1 + texture.crop.height; } else { - w0 = (texture._frame.width ) * (1-sprite.anchor.x); - w1 = (texture._frame.width ) * -sprite.anchor.x; + w0 = (crop.width ) * (1-sprite.anchor.x); + w1 = (crop.width ) * -sprite.anchor.x; - h0 = texture._frame.height * (1-sprite.anchor.y); - h1 = texture._frame.height * -sprite.anchor.y; + h0 = crop.height * (1-sprite.anchor.y); + h1 = crop.height * -sprite.anchor.y; } array[offset] = w1 * sx; diff --git a/package.json b/package.json index bc669c9..f149fa7 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ ], "dependencies": { "async": "^1.5.0", + "bit-twiddle": "^1.0.2", "brfs": "^1.4.1", "earcut": "^2.0.7", "eventemitter3": "^1.1.1", diff --git a/src/core/display/ObservablePoint.js b/src/core/display/ObservablePoint.js index fd7a04b..958661f 100644 --- a/src/core/display/ObservablePoint.js +++ b/src/core/display/ObservablePoint.js @@ -35,7 +35,7 @@ set: function (value) { this._x = value; - this.transform.dirty = true; + this.transform._versionLocal++; } }, @@ -47,7 +47,7 @@ set: function (value) { this._y = value; - this.transform.dirty = true; + this.transform._versionLocal++; } } }); @@ -63,6 +63,6 @@ { this._x = x || 0; this._y = y || ( (y !== 0) ? this._x : 0 ); - - this.transform.dirty = true; + + this.transform._versionLocal++; }; diff --git a/src/core/display/TransformStatic.js b/src/core/display/TransformStatic.js index 61161d0..b86aa2d 100644 --- a/src/core/display/TransformStatic.js +++ b/src/core/display/TransformStatic.js @@ -1,6 +1,7 @@ var math = require('../math'); var ObservablePoint = require('./ObservablePoint'); +var generatorId = 0; /** * The Point object represents a location in a two-dimensional coordinate system, where x represents * the horizontal axis and y represents the vertical axis. @@ -19,13 +20,17 @@ this.scale = new ObservablePoint(this,1, 1); this.pivot = new ObservablePoint(this, 0, 0); this.skew = new ObservablePoint(this, 0,0); - + this.rotation = 0; this._sr = Math.sin(0); this._cr = Math.cos(0); - - this.dirty = true; - this.updated = true; + + this._versionLocal = 0; + this._versionGlobal = 0; + this._dirtyLocal = 0; + this._dirtyParentVersion = -1; + this._dirtyParentId = -1; + this._transformId = ++generatorId; } TransformStatic.prototype.constructor = TransformStatic; @@ -36,21 +41,21 @@ var wt = this.worldTransform; var lt = this.localTransform; - if(this.dirty) + if(this._dirtyLocal !== this._versionLocal || + parentTransform._dirtyParentId !== parentTransform._transformId || + parentTransform._dirtyParentVersion !== parentTransform._versionGlobal ) { - // get the matrix values of the displayobject based on its transform properties.. - lt.a = this._cr * this.scale._x; - lt.b = this._sr * this.scale._x; - lt.c = -this._sr * this.scale._y; - lt.d = this._cr * this.scale._y; - lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); - lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); - } - - this.updated = this.dirty || parentTransform.updated; - - if(this.updated) - { + if(this._dirtyLocal !== this._versionLocal) + { + // get the matrix values of the displayobject based on its transform properties.. + lt.a = this._cr * this.scale._x; + lt.b = this._sr * this.scale._x; + lt.c = -this._sr * this.scale._y; + lt.d = this._cr * this.scale._y; + lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); + lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); + this._dirtyLocal = this._versionLocal; + } // concat the parent matrix with the objects transform. wt.a = lt.a * pt.a + lt.b * pt.c; wt.b = lt.a * pt.b + lt.b * pt.d; @@ -58,10 +63,12 @@ wt.d = lt.c * pt.b + lt.d * pt.d; wt.tx = lt.tx * pt.a + lt.ty * pt.c + pt.tx; wt.ty = lt.tx * pt.b + lt.ty * pt.d + pt.ty; + + this._dirtyParentId = parentTransform._transformId; + this._dirtyParentVersion = parentTransform._versionGlobal; + this._versionGlobal++; } - - this.dirty = false; -} +}; module.exports = TransformStatic; diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js new file mode 100644 index 0000000..1edb988 --- /dev/null +++ b/src/core/math/GroupD8.js @@ -0,0 +1,162 @@ +// Your friendly neighbour https://en.wikipedia.org/wiki/Dihedral_group of order 16 + +var ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1]; +var uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1]; +var tempMatrices = []; +var Matrix = require('./Matrix'); + +var mul = []; + +function signum(x) { + if (x < 0) { + return -1; + } + if (x > 0) { + return 1; + } + return 0; +} + +function init() { + for (var i = 0; i < 16; i++) { + var row = []; + mul.push(row); + for (var j = 0; j < 16; j++) { + var _ux = signum(ux[i] * ux[j] + vx[i] * uy[j]); + var _uy = signum(uy[i] * ux[j] + vy[i] * uy[j]); + var _vx = signum(ux[i] * vx[j] + vx[i] * vy[j]); + var _vy = signum(uy[i] * vx[j] + vy[i] * vy[j]); + for (var k = 0; k < 16; k++) { + if (ux[k] === _ux && uy[k] === _uy && vx[k] === _vx && vy[k] === _vy) { + row.push(k); + break; + } + } + } + } + + for (i=0;i<16;i++) { + var mat = new Matrix(); + mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0); + tempMatrices.push(mat); + } +} + +init(); + +/** + * Implements Dihedral Group D_8, see [group D4]{@link http://mathworld.wolfram.com/DihedralGroupD4.html}, D8 is the same but with diagonals + * Used for texture rotations + * Vector xX(i), xY(i) is U-axis of sprite with rotation i + * Vector yY(i), yY(i) is V-axis of sprite with rotation i + * Rotations: 0 grad (0), 90 grad (2), 180 grad (4), 270 grad (6) + * Mirrors: vertical (8), main diagonal (10), horizontal (12), reverse diagonal (14) + * This is the small part of gameofbombs.com portal system. It works. + * @author Ivan @ivanpopelyshev + * + * @namespace PIXI.GroupD8 + */ +var GroupD8 = { + E: 0, + SE: 1, + S: 2, + SW: 3, + W: 4, + NW: 5, + N: 6, + NE: 7, + MIRROR_VERTICAL: 8, + MIRROR_HORIZONTAL: 12, + uX: function (ind) { + return ux[ind]; + }, + uY: function (ind) { + return uy[ind]; + }, + vX: function (ind) { + return vx[ind]; + }, + vY: function (ind) { + return vy[ind]; + }, + inv: function (rotation) { + if (rotation & 8) { + return rotation & 15; + } + return (-rotation) & 7; + }, + add: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][rotationFirst]; + }, + sub: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][GroupD8.inv(rotationFirst)]; + }, + /** + * Adds 180 degrees to rotation. Commutative operation + * @param rotation + * @returns {number} + */ + rotate180: function (rotation) { + return rotation ^ 4; + }, + /** + * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * @param rotation + * @returns {boolean} + */ + isSwapWidthHeight: function(rotation) { + return (rotation & 3) === 2; + }, + byDirection: function (dx, dy) { + if (Math.abs(dx) * 2 <= Math.abs(dy)) { + if (dy >= 0) { + return GroupD8.S; + } + else { + return GroupD8.N; + } + } else if (Math.abs(dy) * 2 <= Math.abs(dx)) { + if (dx > 0) { + return GroupD8.E; + } + else { + return GroupD8.W; + } + } else { + if (dy > 0) { + if (dx > 0) { + return GroupD8.SE; + } + else { + return GroupD8.SW; + } + } + else if (dx > 0) { + return GroupD8.NE; + } + else { + return GroupD8.NW; + } + } + }, + /** + * Helps sprite to compensate texture packer rotation. + * @param matrix {PIXI.Matrix} sprite world matrix + * @param rotation {number} + * @param tx {number|*} sprite anchoring + * @param ty {number|*} sprite anchoring + */ + matrixAppendRotationInv: function (matrix, rotation, tx, ty) { + //Packer used "rotation", we use "inv(rotation)" + var mat = tempMatrices[GroupD8.inv(rotation)]; + tx = tx || 0; + ty = ty || 0; + mat.tx = tx; + mat.ty = ty; + matrix.append(mat); + } +}; + +module.exports = GroupD8; diff --git a/src/core/math/index.js b/src/core/math/index.js index fd0d9be..a43f6e7 100644 --- a/src/core/math/index.js +++ b/src/core/math/index.js @@ -11,6 +11,7 @@ Point: require('./Point'), Matrix: require('./Matrix'), + GroupD8: require('./GroupD8'), Circle: require('./shapes/Circle'), Ellipse: require('./shapes/Ellipse'), diff --git a/src/core/particles/webgl/ParticleRenderer.js b/src/core/particles/webgl/ParticleRenderer.js index 8481c92..0b0abd0 100644 --- a/src/core/particles/webgl/ParticleRenderer.js +++ b/src/core/particles/webgl/ParticleRenderer.js @@ -285,25 +285,25 @@ texture = sprite._texture; sx = sprite.scale.x; sy = sprite.scale.y; + var trim = texture.trim, crop = texture.crop; - if (texture.trim) + if (trim) { - // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. - trim = texture.trim; + // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space before transforming the sprite coords.. + w1 = trim.x - sprite.anchor.x * crop.width; + w0 = w1 + trim.width; - w1 = trim.x - sprite.anchor.x * trim.width; - w0 = w1 + texture.crop.width; + h1 = trim.y - sprite.anchor.y * crop.height; + h0 = h1 + trim.height; - h1 = trim.y - sprite.anchor.y * trim.height; - h0 = h1 + texture.crop.height; } else { - w0 = (texture._frame.width ) * (1-sprite.anchor.x); - w1 = (texture._frame.width ) * -sprite.anchor.x; + w0 = (crop.width ) * (1-sprite.anchor.x); + w1 = (crop.width ) * -sprite.anchor.x; - h0 = texture._frame.height * (1-sprite.anchor.y); - h1 = texture._frame.height * -sprite.anchor.y; + h0 = crop.height * (1-sprite.anchor.y); + h1 = crop.height * -sprite.anchor.y; } array[offset] = w1 * sx; diff --git a/src/core/renderers/canvas/utils/CanvasTinter.js b/src/core/renderers/canvas/utils/CanvasTinter.js index eedf175..8827558 100644 --- a/src/core/renderers/canvas/utils/CanvasTinter.js +++ b/src/core/renderers/canvas/utils/CanvasTinter.js @@ -68,11 +68,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; @@ -123,11 +119,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; @@ -165,11 +157,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; diff --git a/package.json b/package.json index bc669c9..f149fa7 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ ], "dependencies": { "async": "^1.5.0", + "bit-twiddle": "^1.0.2", "brfs": "^1.4.1", "earcut": "^2.0.7", "eventemitter3": "^1.1.1", diff --git a/src/core/display/ObservablePoint.js b/src/core/display/ObservablePoint.js index fd7a04b..958661f 100644 --- a/src/core/display/ObservablePoint.js +++ b/src/core/display/ObservablePoint.js @@ -35,7 +35,7 @@ set: function (value) { this._x = value; - this.transform.dirty = true; + this.transform._versionLocal++; } }, @@ -47,7 +47,7 @@ set: function (value) { this._y = value; - this.transform.dirty = true; + this.transform._versionLocal++; } } }); @@ -63,6 +63,6 @@ { this._x = x || 0; this._y = y || ( (y !== 0) ? this._x : 0 ); - - this.transform.dirty = true; + + this.transform._versionLocal++; }; diff --git a/src/core/display/TransformStatic.js b/src/core/display/TransformStatic.js index 61161d0..b86aa2d 100644 --- a/src/core/display/TransformStatic.js +++ b/src/core/display/TransformStatic.js @@ -1,6 +1,7 @@ var math = require('../math'); var ObservablePoint = require('./ObservablePoint'); +var generatorId = 0; /** * The Point object represents a location in a two-dimensional coordinate system, where x represents * the horizontal axis and y represents the vertical axis. @@ -19,13 +20,17 @@ this.scale = new ObservablePoint(this,1, 1); this.pivot = new ObservablePoint(this, 0, 0); this.skew = new ObservablePoint(this, 0,0); - + this.rotation = 0; this._sr = Math.sin(0); this._cr = Math.cos(0); - - this.dirty = true; - this.updated = true; + + this._versionLocal = 0; + this._versionGlobal = 0; + this._dirtyLocal = 0; + this._dirtyParentVersion = -1; + this._dirtyParentId = -1; + this._transformId = ++generatorId; } TransformStatic.prototype.constructor = TransformStatic; @@ -36,21 +41,21 @@ var wt = this.worldTransform; var lt = this.localTransform; - if(this.dirty) + if(this._dirtyLocal !== this._versionLocal || + parentTransform._dirtyParentId !== parentTransform._transformId || + parentTransform._dirtyParentVersion !== parentTransform._versionGlobal ) { - // get the matrix values of the displayobject based on its transform properties.. - lt.a = this._cr * this.scale._x; - lt.b = this._sr * this.scale._x; - lt.c = -this._sr * this.scale._y; - lt.d = this._cr * this.scale._y; - lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); - lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); - } - - this.updated = this.dirty || parentTransform.updated; - - if(this.updated) - { + if(this._dirtyLocal !== this._versionLocal) + { + // get the matrix values of the displayobject based on its transform properties.. + lt.a = this._cr * this.scale._x; + lt.b = this._sr * this.scale._x; + lt.c = -this._sr * this.scale._y; + lt.d = this._cr * this.scale._y; + lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); + lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); + this._dirtyLocal = this._versionLocal; + } // concat the parent matrix with the objects transform. wt.a = lt.a * pt.a + lt.b * pt.c; wt.b = lt.a * pt.b + lt.b * pt.d; @@ -58,10 +63,12 @@ wt.d = lt.c * pt.b + lt.d * pt.d; wt.tx = lt.tx * pt.a + lt.ty * pt.c + pt.tx; wt.ty = lt.tx * pt.b + lt.ty * pt.d + pt.ty; + + this._dirtyParentId = parentTransform._transformId; + this._dirtyParentVersion = parentTransform._versionGlobal; + this._versionGlobal++; } - - this.dirty = false; -} +}; module.exports = TransformStatic; diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js new file mode 100644 index 0000000..1edb988 --- /dev/null +++ b/src/core/math/GroupD8.js @@ -0,0 +1,162 @@ +// Your friendly neighbour https://en.wikipedia.org/wiki/Dihedral_group of order 16 + +var ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1]; +var uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1]; +var tempMatrices = []; +var Matrix = require('./Matrix'); + +var mul = []; + +function signum(x) { + if (x < 0) { + return -1; + } + if (x > 0) { + return 1; + } + return 0; +} + +function init() { + for (var i = 0; i < 16; i++) { + var row = []; + mul.push(row); + for (var j = 0; j < 16; j++) { + var _ux = signum(ux[i] * ux[j] + vx[i] * uy[j]); + var _uy = signum(uy[i] * ux[j] + vy[i] * uy[j]); + var _vx = signum(ux[i] * vx[j] + vx[i] * vy[j]); + var _vy = signum(uy[i] * vx[j] + vy[i] * vy[j]); + for (var k = 0; k < 16; k++) { + if (ux[k] === _ux && uy[k] === _uy && vx[k] === _vx && vy[k] === _vy) { + row.push(k); + break; + } + } + } + } + + for (i=0;i<16;i++) { + var mat = new Matrix(); + mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0); + tempMatrices.push(mat); + } +} + +init(); + +/** + * Implements Dihedral Group D_8, see [group D4]{@link http://mathworld.wolfram.com/DihedralGroupD4.html}, D8 is the same but with diagonals + * Used for texture rotations + * Vector xX(i), xY(i) is U-axis of sprite with rotation i + * Vector yY(i), yY(i) is V-axis of sprite with rotation i + * Rotations: 0 grad (0), 90 grad (2), 180 grad (4), 270 grad (6) + * Mirrors: vertical (8), main diagonal (10), horizontal (12), reverse diagonal (14) + * This is the small part of gameofbombs.com portal system. It works. + * @author Ivan @ivanpopelyshev + * + * @namespace PIXI.GroupD8 + */ +var GroupD8 = { + E: 0, + SE: 1, + S: 2, + SW: 3, + W: 4, + NW: 5, + N: 6, + NE: 7, + MIRROR_VERTICAL: 8, + MIRROR_HORIZONTAL: 12, + uX: function (ind) { + return ux[ind]; + }, + uY: function (ind) { + return uy[ind]; + }, + vX: function (ind) { + return vx[ind]; + }, + vY: function (ind) { + return vy[ind]; + }, + inv: function (rotation) { + if (rotation & 8) { + return rotation & 15; + } + return (-rotation) & 7; + }, + add: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][rotationFirst]; + }, + sub: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][GroupD8.inv(rotationFirst)]; + }, + /** + * Adds 180 degrees to rotation. Commutative operation + * @param rotation + * @returns {number} + */ + rotate180: function (rotation) { + return rotation ^ 4; + }, + /** + * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * @param rotation + * @returns {boolean} + */ + isSwapWidthHeight: function(rotation) { + return (rotation & 3) === 2; + }, + byDirection: function (dx, dy) { + if (Math.abs(dx) * 2 <= Math.abs(dy)) { + if (dy >= 0) { + return GroupD8.S; + } + else { + return GroupD8.N; + } + } else if (Math.abs(dy) * 2 <= Math.abs(dx)) { + if (dx > 0) { + return GroupD8.E; + } + else { + return GroupD8.W; + } + } else { + if (dy > 0) { + if (dx > 0) { + return GroupD8.SE; + } + else { + return GroupD8.SW; + } + } + else if (dx > 0) { + return GroupD8.NE; + } + else { + return GroupD8.NW; + } + } + }, + /** + * Helps sprite to compensate texture packer rotation. + * @param matrix {PIXI.Matrix} sprite world matrix + * @param rotation {number} + * @param tx {number|*} sprite anchoring + * @param ty {number|*} sprite anchoring + */ + matrixAppendRotationInv: function (matrix, rotation, tx, ty) { + //Packer used "rotation", we use "inv(rotation)" + var mat = tempMatrices[GroupD8.inv(rotation)]; + tx = tx || 0; + ty = ty || 0; + mat.tx = tx; + mat.ty = ty; + matrix.append(mat); + } +}; + +module.exports = GroupD8; diff --git a/src/core/math/index.js b/src/core/math/index.js index fd0d9be..a43f6e7 100644 --- a/src/core/math/index.js +++ b/src/core/math/index.js @@ -11,6 +11,7 @@ Point: require('./Point'), Matrix: require('./Matrix'), + GroupD8: require('./GroupD8'), Circle: require('./shapes/Circle'), Ellipse: require('./shapes/Ellipse'), diff --git a/src/core/particles/webgl/ParticleRenderer.js b/src/core/particles/webgl/ParticleRenderer.js index 8481c92..0b0abd0 100644 --- a/src/core/particles/webgl/ParticleRenderer.js +++ b/src/core/particles/webgl/ParticleRenderer.js @@ -285,25 +285,25 @@ texture = sprite._texture; sx = sprite.scale.x; sy = sprite.scale.y; + var trim = texture.trim, crop = texture.crop; - if (texture.trim) + if (trim) { - // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. - trim = texture.trim; + // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space before transforming the sprite coords.. + w1 = trim.x - sprite.anchor.x * crop.width; + w0 = w1 + trim.width; - w1 = trim.x - sprite.anchor.x * trim.width; - w0 = w1 + texture.crop.width; + h1 = trim.y - sprite.anchor.y * crop.height; + h0 = h1 + trim.height; - h1 = trim.y - sprite.anchor.y * trim.height; - h0 = h1 + texture.crop.height; } else { - w0 = (texture._frame.width ) * (1-sprite.anchor.x); - w1 = (texture._frame.width ) * -sprite.anchor.x; + w0 = (crop.width ) * (1-sprite.anchor.x); + w1 = (crop.width ) * -sprite.anchor.x; - h0 = texture._frame.height * (1-sprite.anchor.y); - h1 = texture._frame.height * -sprite.anchor.y; + h0 = crop.height * (1-sprite.anchor.y); + h1 = crop.height * -sprite.anchor.y; } array[offset] = w1 * sx; diff --git a/src/core/renderers/canvas/utils/CanvasTinter.js b/src/core/renderers/canvas/utils/CanvasTinter.js index eedf175..8827558 100644 --- a/src/core/renderers/canvas/utils/CanvasTinter.js +++ b/src/core/renderers/canvas/utils/CanvasTinter.js @@ -68,11 +68,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; @@ -123,11 +119,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; @@ -165,11 +157,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index b174736..970212f 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -111,12 +111,12 @@ width: { get: function () { - return Math.abs(this.scale.x) * this.texture._frame.width; + return Math.abs(this.scale.x) * this.texture.crop.width; }, set: function (value) { var sign = utils.sign(this.scale.x) || 1; - this.scale.x = sign * value / this.texture._frame.width; + this.scale.x = sign * value / this.texture.crop.width; this._width = value; } }, @@ -130,12 +130,12 @@ height: { get: function () { - return Math.abs(this.scale.y) * this.texture._frame.height; + return Math.abs(this.scale.y) * this.texture.crop.height; }, set: function (value) { var sign = utils.sign(this.scale.y) || 1; - this.scale.y = sign * value / this.texture._frame.height; + this.scale.y = sign * value / this.texture.crop.height; this._height = value; } }, @@ -191,12 +191,12 @@ // so if _width is 0 then width was not set.. if (this._width) { - this.scale.x = utils.sign(this.scale.x) * this._width / this.texture.frame.width; + this.scale.x = utils.sign(this.scale.x) * this._width / this.texture.crop.width; } if (this._height) { - this.scale.y = utils.sign(this.scale.y) * this._height / this.texture.frame.height; + this.scale.y = utils.sign(this.scale.y) * this._height / this.texture.crop.height; } }; @@ -207,25 +207,26 @@ a = wt.a, b = wt.b, c = wt.c, d = wt.d, tx = wt.tx, ty = wt.ty, vertexData = this.vertexData, w0, w1, h0, h1, - trim = texture.trim; - + trim = texture.trim, + crop = texture.crop; + if (trim) { // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space before transforming the sprite coords.. - w1 = trim.x - this.anchor.x * trim.width; - w0 = w1 + texture.crop.width; + w1 = trim.x - this.anchor.x * crop.width; + w0 = w1 + trim.width; - h1 = trim.y - this.anchor.y * trim.height; - h0 = h1 + texture.crop.height; + h1 = trim.y - this.anchor.y * crop.height; + h0 = h1 + trim.height; } else { - w0 = (texture._frame.width ) * (1-this.anchor.x); - w1 = (texture._frame.width ) * -this.anchor.x; + w0 = (crop.width ) * (1-this.anchor.x); + w1 = (crop.width ) * -this.anchor.x; - h0 = texture._frame.height * (1-this.anchor.y); - h1 = texture._frame.height * -this.anchor.y; + h0 = crop.height * (1-this.anchor.y); + h1 = crop.height * -this.anchor.y; } // xy @@ -244,7 +245,6 @@ vertexData[6] = a * w1 + c * h0 + tx; vertexData[7] = d * h0 + b * w1 + ty; - } /** @@ -262,7 +262,7 @@ // set the vertex data this.caclulateVertices(); } - + renderer.setObjectRenderer(renderer.plugins.sprite); renderer.plugins.sprite.render(this); }; @@ -363,10 +363,10 @@ */ Sprite.prototype.getLocalBounds = function () { - this._bounds.x = -this._texture._frame.width * this.anchor.x; - this._bounds.y = -this._texture._frame.height * this.anchor.y; - this._bounds.width = this._texture._frame.width; - this._bounds.height = this._texture._frame.height; + this._bounds.x = -this._texture.crop.width * this.anchor.x; + this._bounds.y = -this._texture.crop.height * this.anchor.y; + this._bounds.width = this._texture.crop.width; + this._bounds.height = this._texture.crop.height; return this._bounds; }; @@ -380,8 +380,8 @@ { this.worldTransform.applyInverse(point, tempPoint); - var width = this._texture._frame.width; - var height = this._texture._frame.height; + var width = this._texture.crop.width; + var height = this._texture.crop.height; var x1 = -width * this.anchor.x; var y1; @@ -424,8 +424,8 @@ wt = this.worldTransform, dx, dy, - width, - height; + width = texture._frame.width, + height = texture._frame.height; renderer.context.globalAlpha = this.worldAlpha; @@ -436,45 +436,23 @@ renderer.context[renderer.smoothProperty] = smoothingEnabled; } - // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions - - if(texture.rotate) - { - width = texture.crop.height; - height = texture.crop.width; - - dx = (texture.trim) ? texture.trim.y - this.anchor.y * texture.trim.height : this.anchor.y * -texture._frame.height; - dy = (texture.trim) ? texture.trim.x - this.anchor.x * texture.trim.width : this.anchor.x * -texture._frame.width; - - dx += width; - - wt.tx = dy * wt.a + dx * wt.c + wt.tx; - wt.ty = dy * wt.b + dx * wt.d + wt.ty; - - var temp = wt.a; - wt.a = -wt.c; - wt.c = temp; - - temp = wt.b; - wt.b = -wt.d; - wt.d = temp; - + if (texture.trim) { + dx = texture.trim.width/2 + texture.trim.x - this.anchor.x * texture.crop.width; + dy = texture.trim.height/2 + texture.trim.y - this.anchor.y * texture.crop.height; + } else { + dx = (0.5 - this.anchor.x) * texture.crop.width; + dy = (0.5 - this.anchor.y) * texture.crop.height; + } + if(texture.rotate) { + wt.copy(canvasRenderWorldTransform); + wt = canvasRenderWorldTransform; + GroupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); // the anchor has already been applied above, so lets set it to zero dx = 0; dy = 0; - } - else - { - width = texture.crop.width; - height = texture.crop.height; - - dx = (texture.trim) ? texture.trim.x - this.anchor.x * texture.trim.width : this.anchor.x * -texture._frame.width; - dy = (texture.trim) ? texture.trim.y - this.anchor.y * texture.trim.height : this.anchor.y * -texture._frame.height; - } - - - + dx -= width/2; + dy -= height/2; // Allow for pixel rounding if (renderer.roundPixels) { @@ -492,7 +470,6 @@ } else { - renderer.context.setTransform( wt.a, wt.b, @@ -501,8 +478,6 @@ wt.tx * renderer.resolution, wt.ty * renderer.resolution ); - - } var resolution = texture.baseTexture.resolution; @@ -533,8 +508,8 @@ { renderer.context.drawImage( texture.baseTexture.source, - texture.crop.x * resolution, - texture.crop.y * resolution, + texture.frame.x * resolution, + texture.frame.y * resolution, width * resolution, height * resolution, dx * renderer.resolution, diff --git a/package.json b/package.json index bc669c9..f149fa7 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ ], "dependencies": { "async": "^1.5.0", + "bit-twiddle": "^1.0.2", "brfs": "^1.4.1", "earcut": "^2.0.7", "eventemitter3": "^1.1.1", diff --git a/src/core/display/ObservablePoint.js b/src/core/display/ObservablePoint.js index fd7a04b..958661f 100644 --- a/src/core/display/ObservablePoint.js +++ b/src/core/display/ObservablePoint.js @@ -35,7 +35,7 @@ set: function (value) { this._x = value; - this.transform.dirty = true; + this.transform._versionLocal++; } }, @@ -47,7 +47,7 @@ set: function (value) { this._y = value; - this.transform.dirty = true; + this.transform._versionLocal++; } } }); @@ -63,6 +63,6 @@ { this._x = x || 0; this._y = y || ( (y !== 0) ? this._x : 0 ); - - this.transform.dirty = true; + + this.transform._versionLocal++; }; diff --git a/src/core/display/TransformStatic.js b/src/core/display/TransformStatic.js index 61161d0..b86aa2d 100644 --- a/src/core/display/TransformStatic.js +++ b/src/core/display/TransformStatic.js @@ -1,6 +1,7 @@ var math = require('../math'); var ObservablePoint = require('./ObservablePoint'); +var generatorId = 0; /** * The Point object represents a location in a two-dimensional coordinate system, where x represents * the horizontal axis and y represents the vertical axis. @@ -19,13 +20,17 @@ this.scale = new ObservablePoint(this,1, 1); this.pivot = new ObservablePoint(this, 0, 0); this.skew = new ObservablePoint(this, 0,0); - + this.rotation = 0; this._sr = Math.sin(0); this._cr = Math.cos(0); - - this.dirty = true; - this.updated = true; + + this._versionLocal = 0; + this._versionGlobal = 0; + this._dirtyLocal = 0; + this._dirtyParentVersion = -1; + this._dirtyParentId = -1; + this._transformId = ++generatorId; } TransformStatic.prototype.constructor = TransformStatic; @@ -36,21 +41,21 @@ var wt = this.worldTransform; var lt = this.localTransform; - if(this.dirty) + if(this._dirtyLocal !== this._versionLocal || + parentTransform._dirtyParentId !== parentTransform._transformId || + parentTransform._dirtyParentVersion !== parentTransform._versionGlobal ) { - // get the matrix values of the displayobject based on its transform properties.. - lt.a = this._cr * this.scale._x; - lt.b = this._sr * this.scale._x; - lt.c = -this._sr * this.scale._y; - lt.d = this._cr * this.scale._y; - lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); - lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); - } - - this.updated = this.dirty || parentTransform.updated; - - if(this.updated) - { + if(this._dirtyLocal !== this._versionLocal) + { + // get the matrix values of the displayobject based on its transform properties.. + lt.a = this._cr * this.scale._x; + lt.b = this._sr * this.scale._x; + lt.c = -this._sr * this.scale._y; + lt.d = this._cr * this.scale._y; + lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); + lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); + this._dirtyLocal = this._versionLocal; + } // concat the parent matrix with the objects transform. wt.a = lt.a * pt.a + lt.b * pt.c; wt.b = lt.a * pt.b + lt.b * pt.d; @@ -58,10 +63,12 @@ wt.d = lt.c * pt.b + lt.d * pt.d; wt.tx = lt.tx * pt.a + lt.ty * pt.c + pt.tx; wt.ty = lt.tx * pt.b + lt.ty * pt.d + pt.ty; + + this._dirtyParentId = parentTransform._transformId; + this._dirtyParentVersion = parentTransform._versionGlobal; + this._versionGlobal++; } - - this.dirty = false; -} +}; module.exports = TransformStatic; diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js new file mode 100644 index 0000000..1edb988 --- /dev/null +++ b/src/core/math/GroupD8.js @@ -0,0 +1,162 @@ +// Your friendly neighbour https://en.wikipedia.org/wiki/Dihedral_group of order 16 + +var ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1]; +var uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1]; +var tempMatrices = []; +var Matrix = require('./Matrix'); + +var mul = []; + +function signum(x) { + if (x < 0) { + return -1; + } + if (x > 0) { + return 1; + } + return 0; +} + +function init() { + for (var i = 0; i < 16; i++) { + var row = []; + mul.push(row); + for (var j = 0; j < 16; j++) { + var _ux = signum(ux[i] * ux[j] + vx[i] * uy[j]); + var _uy = signum(uy[i] * ux[j] + vy[i] * uy[j]); + var _vx = signum(ux[i] * vx[j] + vx[i] * vy[j]); + var _vy = signum(uy[i] * vx[j] + vy[i] * vy[j]); + for (var k = 0; k < 16; k++) { + if (ux[k] === _ux && uy[k] === _uy && vx[k] === _vx && vy[k] === _vy) { + row.push(k); + break; + } + } + } + } + + for (i=0;i<16;i++) { + var mat = new Matrix(); + mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0); + tempMatrices.push(mat); + } +} + +init(); + +/** + * Implements Dihedral Group D_8, see [group D4]{@link http://mathworld.wolfram.com/DihedralGroupD4.html}, D8 is the same but with diagonals + * Used for texture rotations + * Vector xX(i), xY(i) is U-axis of sprite with rotation i + * Vector yY(i), yY(i) is V-axis of sprite with rotation i + * Rotations: 0 grad (0), 90 grad (2), 180 grad (4), 270 grad (6) + * Mirrors: vertical (8), main diagonal (10), horizontal (12), reverse diagonal (14) + * This is the small part of gameofbombs.com portal system. It works. + * @author Ivan @ivanpopelyshev + * + * @namespace PIXI.GroupD8 + */ +var GroupD8 = { + E: 0, + SE: 1, + S: 2, + SW: 3, + W: 4, + NW: 5, + N: 6, + NE: 7, + MIRROR_VERTICAL: 8, + MIRROR_HORIZONTAL: 12, + uX: function (ind) { + return ux[ind]; + }, + uY: function (ind) { + return uy[ind]; + }, + vX: function (ind) { + return vx[ind]; + }, + vY: function (ind) { + return vy[ind]; + }, + inv: function (rotation) { + if (rotation & 8) { + return rotation & 15; + } + return (-rotation) & 7; + }, + add: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][rotationFirst]; + }, + sub: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][GroupD8.inv(rotationFirst)]; + }, + /** + * Adds 180 degrees to rotation. Commutative operation + * @param rotation + * @returns {number} + */ + rotate180: function (rotation) { + return rotation ^ 4; + }, + /** + * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * @param rotation + * @returns {boolean} + */ + isSwapWidthHeight: function(rotation) { + return (rotation & 3) === 2; + }, + byDirection: function (dx, dy) { + if (Math.abs(dx) * 2 <= Math.abs(dy)) { + if (dy >= 0) { + return GroupD8.S; + } + else { + return GroupD8.N; + } + } else if (Math.abs(dy) * 2 <= Math.abs(dx)) { + if (dx > 0) { + return GroupD8.E; + } + else { + return GroupD8.W; + } + } else { + if (dy > 0) { + if (dx > 0) { + return GroupD8.SE; + } + else { + return GroupD8.SW; + } + } + else if (dx > 0) { + return GroupD8.NE; + } + else { + return GroupD8.NW; + } + } + }, + /** + * Helps sprite to compensate texture packer rotation. + * @param matrix {PIXI.Matrix} sprite world matrix + * @param rotation {number} + * @param tx {number|*} sprite anchoring + * @param ty {number|*} sprite anchoring + */ + matrixAppendRotationInv: function (matrix, rotation, tx, ty) { + //Packer used "rotation", we use "inv(rotation)" + var mat = tempMatrices[GroupD8.inv(rotation)]; + tx = tx || 0; + ty = ty || 0; + mat.tx = tx; + mat.ty = ty; + matrix.append(mat); + } +}; + +module.exports = GroupD8; diff --git a/src/core/math/index.js b/src/core/math/index.js index fd0d9be..a43f6e7 100644 --- a/src/core/math/index.js +++ b/src/core/math/index.js @@ -11,6 +11,7 @@ Point: require('./Point'), Matrix: require('./Matrix'), + GroupD8: require('./GroupD8'), Circle: require('./shapes/Circle'), Ellipse: require('./shapes/Ellipse'), diff --git a/src/core/particles/webgl/ParticleRenderer.js b/src/core/particles/webgl/ParticleRenderer.js index 8481c92..0b0abd0 100644 --- a/src/core/particles/webgl/ParticleRenderer.js +++ b/src/core/particles/webgl/ParticleRenderer.js @@ -285,25 +285,25 @@ texture = sprite._texture; sx = sprite.scale.x; sy = sprite.scale.y; + var trim = texture.trim, crop = texture.crop; - if (texture.trim) + if (trim) { - // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. - trim = texture.trim; + // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space before transforming the sprite coords.. + w1 = trim.x - sprite.anchor.x * crop.width; + w0 = w1 + trim.width; - w1 = trim.x - sprite.anchor.x * trim.width; - w0 = w1 + texture.crop.width; + h1 = trim.y - sprite.anchor.y * crop.height; + h0 = h1 + trim.height; - h1 = trim.y - sprite.anchor.y * trim.height; - h0 = h1 + texture.crop.height; } else { - w0 = (texture._frame.width ) * (1-sprite.anchor.x); - w1 = (texture._frame.width ) * -sprite.anchor.x; + w0 = (crop.width ) * (1-sprite.anchor.x); + w1 = (crop.width ) * -sprite.anchor.x; - h0 = texture._frame.height * (1-sprite.anchor.y); - h1 = texture._frame.height * -sprite.anchor.y; + h0 = crop.height * (1-sprite.anchor.y); + h1 = crop.height * -sprite.anchor.y; } array[offset] = w1 * sx; diff --git a/src/core/renderers/canvas/utils/CanvasTinter.js b/src/core/renderers/canvas/utils/CanvasTinter.js index eedf175..8827558 100644 --- a/src/core/renderers/canvas/utils/CanvasTinter.js +++ b/src/core/renderers/canvas/utils/CanvasTinter.js @@ -68,11 +68,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; @@ -123,11 +119,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; @@ -165,11 +157,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index b174736..970212f 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -111,12 +111,12 @@ width: { get: function () { - return Math.abs(this.scale.x) * this.texture._frame.width; + return Math.abs(this.scale.x) * this.texture.crop.width; }, set: function (value) { var sign = utils.sign(this.scale.x) || 1; - this.scale.x = sign * value / this.texture._frame.width; + this.scale.x = sign * value / this.texture.crop.width; this._width = value; } }, @@ -130,12 +130,12 @@ height: { get: function () { - return Math.abs(this.scale.y) * this.texture._frame.height; + return Math.abs(this.scale.y) * this.texture.crop.height; }, set: function (value) { var sign = utils.sign(this.scale.y) || 1; - this.scale.y = sign * value / this.texture._frame.height; + this.scale.y = sign * value / this.texture.crop.height; this._height = value; } }, @@ -191,12 +191,12 @@ // so if _width is 0 then width was not set.. if (this._width) { - this.scale.x = utils.sign(this.scale.x) * this._width / this.texture.frame.width; + this.scale.x = utils.sign(this.scale.x) * this._width / this.texture.crop.width; } if (this._height) { - this.scale.y = utils.sign(this.scale.y) * this._height / this.texture.frame.height; + this.scale.y = utils.sign(this.scale.y) * this._height / this.texture.crop.height; } }; @@ -207,25 +207,26 @@ a = wt.a, b = wt.b, c = wt.c, d = wt.d, tx = wt.tx, ty = wt.ty, vertexData = this.vertexData, w0, w1, h0, h1, - trim = texture.trim; - + trim = texture.trim, + crop = texture.crop; + if (trim) { // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space before transforming the sprite coords.. - w1 = trim.x - this.anchor.x * trim.width; - w0 = w1 + texture.crop.width; + w1 = trim.x - this.anchor.x * crop.width; + w0 = w1 + trim.width; - h1 = trim.y - this.anchor.y * trim.height; - h0 = h1 + texture.crop.height; + h1 = trim.y - this.anchor.y * crop.height; + h0 = h1 + trim.height; } else { - w0 = (texture._frame.width ) * (1-this.anchor.x); - w1 = (texture._frame.width ) * -this.anchor.x; + w0 = (crop.width ) * (1-this.anchor.x); + w1 = (crop.width ) * -this.anchor.x; - h0 = texture._frame.height * (1-this.anchor.y); - h1 = texture._frame.height * -this.anchor.y; + h0 = crop.height * (1-this.anchor.y); + h1 = crop.height * -this.anchor.y; } // xy @@ -244,7 +245,6 @@ vertexData[6] = a * w1 + c * h0 + tx; vertexData[7] = d * h0 + b * w1 + ty; - } /** @@ -262,7 +262,7 @@ // set the vertex data this.caclulateVertices(); } - + renderer.setObjectRenderer(renderer.plugins.sprite); renderer.plugins.sprite.render(this); }; @@ -363,10 +363,10 @@ */ Sprite.prototype.getLocalBounds = function () { - this._bounds.x = -this._texture._frame.width * this.anchor.x; - this._bounds.y = -this._texture._frame.height * this.anchor.y; - this._bounds.width = this._texture._frame.width; - this._bounds.height = this._texture._frame.height; + this._bounds.x = -this._texture.crop.width * this.anchor.x; + this._bounds.y = -this._texture.crop.height * this.anchor.y; + this._bounds.width = this._texture.crop.width; + this._bounds.height = this._texture.crop.height; return this._bounds; }; @@ -380,8 +380,8 @@ { this.worldTransform.applyInverse(point, tempPoint); - var width = this._texture._frame.width; - var height = this._texture._frame.height; + var width = this._texture.crop.width; + var height = this._texture.crop.height; var x1 = -width * this.anchor.x; var y1; @@ -424,8 +424,8 @@ wt = this.worldTransform, dx, dy, - width, - height; + width = texture._frame.width, + height = texture._frame.height; renderer.context.globalAlpha = this.worldAlpha; @@ -436,45 +436,23 @@ renderer.context[renderer.smoothProperty] = smoothingEnabled; } - // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions - - if(texture.rotate) - { - width = texture.crop.height; - height = texture.crop.width; - - dx = (texture.trim) ? texture.trim.y - this.anchor.y * texture.trim.height : this.anchor.y * -texture._frame.height; - dy = (texture.trim) ? texture.trim.x - this.anchor.x * texture.trim.width : this.anchor.x * -texture._frame.width; - - dx += width; - - wt.tx = dy * wt.a + dx * wt.c + wt.tx; - wt.ty = dy * wt.b + dx * wt.d + wt.ty; - - var temp = wt.a; - wt.a = -wt.c; - wt.c = temp; - - temp = wt.b; - wt.b = -wt.d; - wt.d = temp; - + if (texture.trim) { + dx = texture.trim.width/2 + texture.trim.x - this.anchor.x * texture.crop.width; + dy = texture.trim.height/2 + texture.trim.y - this.anchor.y * texture.crop.height; + } else { + dx = (0.5 - this.anchor.x) * texture.crop.width; + dy = (0.5 - this.anchor.y) * texture.crop.height; + } + if(texture.rotate) { + wt.copy(canvasRenderWorldTransform); + wt = canvasRenderWorldTransform; + GroupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); // the anchor has already been applied above, so lets set it to zero dx = 0; dy = 0; - } - else - { - width = texture.crop.width; - height = texture.crop.height; - - dx = (texture.trim) ? texture.trim.x - this.anchor.x * texture.trim.width : this.anchor.x * -texture._frame.width; - dy = (texture.trim) ? texture.trim.y - this.anchor.y * texture.trim.height : this.anchor.y * -texture._frame.height; - } - - - + dx -= width/2; + dy -= height/2; // Allow for pixel rounding if (renderer.roundPixels) { @@ -492,7 +470,6 @@ } else { - renderer.context.setTransform( wt.a, wt.b, @@ -501,8 +478,6 @@ wt.tx * renderer.resolution, wt.ty * renderer.resolution ); - - } var resolution = texture.baseTexture.resolution; @@ -533,8 +508,8 @@ { renderer.context.drawImage( texture.baseTexture.source, - texture.crop.x * resolution, - texture.crop.y * resolution, + texture.frame.x * resolution, + texture.frame.y * resolution, width * resolution, height * resolution, dx * renderer.resolution, diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 61b6d12..36ac3ed 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -4,7 +4,8 @@ createIndicesForQuads = require('../../utils/createIndicesForQuads'), generateMultiTextureShader = require('./generateMultiTextureShader'), CONST = require('../../const'), - glCore = require('pixi-gl-core'); + glCore = require('pixi-gl-core'), + bitTwiddle = require('bit-twiddle'); /** * @author Mat Groves @@ -53,38 +54,44 @@ this.size = CONST.SPRITE_BATCH_SIZE; // 2000 is a nice balance between mobile / desktop // the total number of bytes in our batch - var numVerts = (this.size * 4) * this.vertByteSize; + var numVerts = this.size * 4 * this.vertByteSize; // the total number of indices in our batch, there are 6 points per quad. var numIndices = this.size * 6; + this.buffers = []; + for (var i = 1; i <= 2048; i*=2) { + var numVerts = i * 4 * this.vertByteSize; + this.buffers.push(new Buffer(numVerts, i)); + }; + /** * Holds the vertex data that will be sent to the vertex shader. * * @member {ArrayBuffer} */ - this.vertices = new ArrayBuffer(numVerts); + // this.vertices = new ArrayBuffer(numVerts); /** * View on the vertices as a Float32Array for positions * * @member {Float32Array} */ - this.positions = new Float32Array(this.vertices); + // this.positions = new Float32Array(this.vertices); /** * View on the vertices as a Uint32Array for uvs * * @member {Float32Array} */ - this.uvs = new Uint32Array(this.vertices); + //this.uvs = new Uint32Array(this.vertices); /** * View on the vertices as a Uint32Array for colors * * @member {Uint32Array} */ - this.colors = new Uint32Array(this.vertices); + //this.colors = new Uint32Array(this.vertices); /** * Holds the indices of the geometry (quads) to draw @@ -115,6 +122,8 @@ this.currentGroup = this.groups[this.groupCount++]; this.currentTexture = null; + + this.sprites = []; } @@ -215,45 +224,14 @@ this.textureCount++; } - // TODO trim?? - var index = this.currentIndex * this.vertByteSize; + // TODO add this variable to sprite.. + sprite._glBatchTextureId = nextTexture._id; + + this.sprites[this.currentIndex] = sprite; this.currentIndex++; - // upload the sprite elemetns... - // they have all ready been calculated so we just need to push them into the buffer. - var colors = this.colors; - var positions = this.positions; - var vertexData = sprite.vertexData - var tint = (sprite.tint >> 16) + (sprite.tint & 0xff00) + ((sprite.tint & 0xff) << 16) + (sprite.worldAlpha * 255 << 24); - var uvs = sprite.texture._uvs.uvs_uint32; - //xy - positions[index++] = vertexData[0]; - positions[index++] = vertexData[1]; - this.uvs[index++] = uvs[0]; - colors[index++] = tint; - positions[index++] = nextTexture._id; - // xy - positions[index++] = vertexData[2]; - positions[index++] = vertexData[3]; - this.uvs[index++] = uvs[1]; - colors[index++] = tint; - positions[index++] = nextTexture._id; - - // xy - positions[index++] = vertexData[4]; - positions[index++] = vertexData[5]; - this.uvs[index++] = uvs[2]; - colors[index++] = tint; - positions[index++] = nextTexture._id; - - // xy - positions[index++] = vertexData[6]; - positions[index++] = vertexData[7]; - this.uvs[index++] = uvs[3]; - colors[index++] = tint; - positions[index++] = nextTexture._id; }; /** @@ -266,6 +244,59 @@ var gl = this.renderer.gl; + var np2 = bitTwiddle.nextPow2(this.currentIndex); + var log2 = bitTwiddle.log2(np2); + + var buffer = this.buffers[log2]; + + var colors = buffer.colors; + var positions = buffer.positions; + var uvsBuffer = buffer.uvs; + var index = 0; + + //console.log(this.currentIndex); + //var array = + + for (var i = 0; i < this.currentIndex; i++) + { + // upload the sprite elemetns... + // they have all ready been calculated so we just need to push them into the buffer. + var sprite = this.sprites[i]; + + var vertexData = sprite.vertexData + var tint = (sprite.tint >> 16) + (sprite.tint & 0xff00) + ((sprite.tint & 0xff) << 16) + (sprite.worldAlpha * 255 << 24); + var uvs = sprite.texture._uvs.uvs_uint32; + var textureId = sprite._glBatchTextureId; + + //xy + positions[index++] = vertexData[0]; + positions[index++] = vertexData[1]; + uvsBuffer[index++] = uvs[0]; + colors[index++] = tint; + positions[index++] = textureId; + + // xy + positions[index++] = vertexData[2]; + positions[index++] = vertexData[3]; + uvsBuffer[index++] = uvs[1]; + colors[index++] = tint; + positions[index++] = textureId; + + // xy + positions[index++] = vertexData[4]; + positions[index++] = vertexData[5]; + uvsBuffer[index++] = uvs[2]; + colors[index++] = tint; + positions[index++] = textureId; + + // xy + positions[index++] = vertexData[6]; + positions[index++] = vertexData[7]; + uvsBuffer[index++] = uvs[3]; + colors[index++] = tint; + positions[index++] = textureId; + + }; this.currentGroup.size = this.currentIndex - this.currentGroup.start; for (var i = 0; i < this.currentGroup.textureCount; i++) @@ -276,19 +307,19 @@ // do some smart array stuff.. // double size so we dont alway subarray the elements.. // upload the verts to the buffer - if (this.currentBatchSize > ( this.size * 0.5 ) ) - { - this.vertexBuffer.upload(this.vertices, 0, true); - } - else - { + //if (this.currentBatchSize > ( this.size * 0.5 ) ) + //{ + // this.vertexBuffer.upload(this.vertices, 0, true); + // } + //else + //{ // o k .. sub array is SLOW>? - var view = this.positions.subarray(0, this.currentIndex * this.vertByteSize); - this.vertexBuffer.upload(view, 0, true); - } + // var view = this.positions.subarray(0, this.currentIndex * this.vertByteSize); + this.vertexBuffer.upload(buffer.vertices, 0, true); + //} // bind shader.. - this.renderer.bindShader(this.shader); + this.renderer.blendModeManager.setBlendMode( 0 ); /// render the groups.. @@ -319,6 +350,7 @@ */ SpriteRenderer.prototype.start = function () { + this.renderer.bindShader(this.shader); this.vao.bind(); }; @@ -353,3 +385,33 @@ this.sprites = null; this.shader = null; }; + +var Buffer = function(size, realSize) +{ + + this.realSize = realSize; + this.vertices = new ArrayBuffer(size); + + /** + * View on the vertices as a Float32Array for positions + * + * @member {Float32Array} + */ + this.positions = new Float32Array(this.vertices); + + /** + * View on the vertices as a Uint32Array for uvs + * + * @member {Float32Array} + */ + this.uvs = new Uint32Array(this.vertices); + + /** + * View on the vertices as a Uint32Array for colors + * + * @member {Uint32Array} + */ + this.colors = new Uint32Array(this.vertices); + + //this.buffer = +} \ No newline at end of file diff --git a/package.json b/package.json index bc669c9..f149fa7 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ ], "dependencies": { "async": "^1.5.0", + "bit-twiddle": "^1.0.2", "brfs": "^1.4.1", "earcut": "^2.0.7", "eventemitter3": "^1.1.1", diff --git a/src/core/display/ObservablePoint.js b/src/core/display/ObservablePoint.js index fd7a04b..958661f 100644 --- a/src/core/display/ObservablePoint.js +++ b/src/core/display/ObservablePoint.js @@ -35,7 +35,7 @@ set: function (value) { this._x = value; - this.transform.dirty = true; + this.transform._versionLocal++; } }, @@ -47,7 +47,7 @@ set: function (value) { this._y = value; - this.transform.dirty = true; + this.transform._versionLocal++; } } }); @@ -63,6 +63,6 @@ { this._x = x || 0; this._y = y || ( (y !== 0) ? this._x : 0 ); - - this.transform.dirty = true; + + this.transform._versionLocal++; }; diff --git a/src/core/display/TransformStatic.js b/src/core/display/TransformStatic.js index 61161d0..b86aa2d 100644 --- a/src/core/display/TransformStatic.js +++ b/src/core/display/TransformStatic.js @@ -1,6 +1,7 @@ var math = require('../math'); var ObservablePoint = require('./ObservablePoint'); +var generatorId = 0; /** * The Point object represents a location in a two-dimensional coordinate system, where x represents * the horizontal axis and y represents the vertical axis. @@ -19,13 +20,17 @@ this.scale = new ObservablePoint(this,1, 1); this.pivot = new ObservablePoint(this, 0, 0); this.skew = new ObservablePoint(this, 0,0); - + this.rotation = 0; this._sr = Math.sin(0); this._cr = Math.cos(0); - - this.dirty = true; - this.updated = true; + + this._versionLocal = 0; + this._versionGlobal = 0; + this._dirtyLocal = 0; + this._dirtyParentVersion = -1; + this._dirtyParentId = -1; + this._transformId = ++generatorId; } TransformStatic.prototype.constructor = TransformStatic; @@ -36,21 +41,21 @@ var wt = this.worldTransform; var lt = this.localTransform; - if(this.dirty) + if(this._dirtyLocal !== this._versionLocal || + parentTransform._dirtyParentId !== parentTransform._transformId || + parentTransform._dirtyParentVersion !== parentTransform._versionGlobal ) { - // get the matrix values of the displayobject based on its transform properties.. - lt.a = this._cr * this.scale._x; - lt.b = this._sr * this.scale._x; - lt.c = -this._sr * this.scale._y; - lt.d = this._cr * this.scale._y; - lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); - lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); - } - - this.updated = this.dirty || parentTransform.updated; - - if(this.updated) - { + if(this._dirtyLocal !== this._versionLocal) + { + // get the matrix values of the displayobject based on its transform properties.. + lt.a = this._cr * this.scale._x; + lt.b = this._sr * this.scale._x; + lt.c = -this._sr * this.scale._y; + lt.d = this._cr * this.scale._y; + lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); + lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); + this._dirtyLocal = this._versionLocal; + } // concat the parent matrix with the objects transform. wt.a = lt.a * pt.a + lt.b * pt.c; wt.b = lt.a * pt.b + lt.b * pt.d; @@ -58,10 +63,12 @@ wt.d = lt.c * pt.b + lt.d * pt.d; wt.tx = lt.tx * pt.a + lt.ty * pt.c + pt.tx; wt.ty = lt.tx * pt.b + lt.ty * pt.d + pt.ty; + + this._dirtyParentId = parentTransform._transformId; + this._dirtyParentVersion = parentTransform._versionGlobal; + this._versionGlobal++; } - - this.dirty = false; -} +}; module.exports = TransformStatic; diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js new file mode 100644 index 0000000..1edb988 --- /dev/null +++ b/src/core/math/GroupD8.js @@ -0,0 +1,162 @@ +// Your friendly neighbour https://en.wikipedia.org/wiki/Dihedral_group of order 16 + +var ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1]; +var uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1]; +var tempMatrices = []; +var Matrix = require('./Matrix'); + +var mul = []; + +function signum(x) { + if (x < 0) { + return -1; + } + if (x > 0) { + return 1; + } + return 0; +} + +function init() { + for (var i = 0; i < 16; i++) { + var row = []; + mul.push(row); + for (var j = 0; j < 16; j++) { + var _ux = signum(ux[i] * ux[j] + vx[i] * uy[j]); + var _uy = signum(uy[i] * ux[j] + vy[i] * uy[j]); + var _vx = signum(ux[i] * vx[j] + vx[i] * vy[j]); + var _vy = signum(uy[i] * vx[j] + vy[i] * vy[j]); + for (var k = 0; k < 16; k++) { + if (ux[k] === _ux && uy[k] === _uy && vx[k] === _vx && vy[k] === _vy) { + row.push(k); + break; + } + } + } + } + + for (i=0;i<16;i++) { + var mat = new Matrix(); + mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0); + tempMatrices.push(mat); + } +} + +init(); + +/** + * Implements Dihedral Group D_8, see [group D4]{@link http://mathworld.wolfram.com/DihedralGroupD4.html}, D8 is the same but with diagonals + * Used for texture rotations + * Vector xX(i), xY(i) is U-axis of sprite with rotation i + * Vector yY(i), yY(i) is V-axis of sprite with rotation i + * Rotations: 0 grad (0), 90 grad (2), 180 grad (4), 270 grad (6) + * Mirrors: vertical (8), main diagonal (10), horizontal (12), reverse diagonal (14) + * This is the small part of gameofbombs.com portal system. It works. + * @author Ivan @ivanpopelyshev + * + * @namespace PIXI.GroupD8 + */ +var GroupD8 = { + E: 0, + SE: 1, + S: 2, + SW: 3, + W: 4, + NW: 5, + N: 6, + NE: 7, + MIRROR_VERTICAL: 8, + MIRROR_HORIZONTAL: 12, + uX: function (ind) { + return ux[ind]; + }, + uY: function (ind) { + return uy[ind]; + }, + vX: function (ind) { + return vx[ind]; + }, + vY: function (ind) { + return vy[ind]; + }, + inv: function (rotation) { + if (rotation & 8) { + return rotation & 15; + } + return (-rotation) & 7; + }, + add: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][rotationFirst]; + }, + sub: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][GroupD8.inv(rotationFirst)]; + }, + /** + * Adds 180 degrees to rotation. Commutative operation + * @param rotation + * @returns {number} + */ + rotate180: function (rotation) { + return rotation ^ 4; + }, + /** + * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * @param rotation + * @returns {boolean} + */ + isSwapWidthHeight: function(rotation) { + return (rotation & 3) === 2; + }, + byDirection: function (dx, dy) { + if (Math.abs(dx) * 2 <= Math.abs(dy)) { + if (dy >= 0) { + return GroupD8.S; + } + else { + return GroupD8.N; + } + } else if (Math.abs(dy) * 2 <= Math.abs(dx)) { + if (dx > 0) { + return GroupD8.E; + } + else { + return GroupD8.W; + } + } else { + if (dy > 0) { + if (dx > 0) { + return GroupD8.SE; + } + else { + return GroupD8.SW; + } + } + else if (dx > 0) { + return GroupD8.NE; + } + else { + return GroupD8.NW; + } + } + }, + /** + * Helps sprite to compensate texture packer rotation. + * @param matrix {PIXI.Matrix} sprite world matrix + * @param rotation {number} + * @param tx {number|*} sprite anchoring + * @param ty {number|*} sprite anchoring + */ + matrixAppendRotationInv: function (matrix, rotation, tx, ty) { + //Packer used "rotation", we use "inv(rotation)" + var mat = tempMatrices[GroupD8.inv(rotation)]; + tx = tx || 0; + ty = ty || 0; + mat.tx = tx; + mat.ty = ty; + matrix.append(mat); + } +}; + +module.exports = GroupD8; diff --git a/src/core/math/index.js b/src/core/math/index.js index fd0d9be..a43f6e7 100644 --- a/src/core/math/index.js +++ b/src/core/math/index.js @@ -11,6 +11,7 @@ Point: require('./Point'), Matrix: require('./Matrix'), + GroupD8: require('./GroupD8'), Circle: require('./shapes/Circle'), Ellipse: require('./shapes/Ellipse'), diff --git a/src/core/particles/webgl/ParticleRenderer.js b/src/core/particles/webgl/ParticleRenderer.js index 8481c92..0b0abd0 100644 --- a/src/core/particles/webgl/ParticleRenderer.js +++ b/src/core/particles/webgl/ParticleRenderer.js @@ -285,25 +285,25 @@ texture = sprite._texture; sx = sprite.scale.x; sy = sprite.scale.y; + var trim = texture.trim, crop = texture.crop; - if (texture.trim) + if (trim) { - // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. - trim = texture.trim; + // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space before transforming the sprite coords.. + w1 = trim.x - sprite.anchor.x * crop.width; + w0 = w1 + trim.width; - w1 = trim.x - sprite.anchor.x * trim.width; - w0 = w1 + texture.crop.width; + h1 = trim.y - sprite.anchor.y * crop.height; + h0 = h1 + trim.height; - h1 = trim.y - sprite.anchor.y * trim.height; - h0 = h1 + texture.crop.height; } else { - w0 = (texture._frame.width ) * (1-sprite.anchor.x); - w1 = (texture._frame.width ) * -sprite.anchor.x; + w0 = (crop.width ) * (1-sprite.anchor.x); + w1 = (crop.width ) * -sprite.anchor.x; - h0 = texture._frame.height * (1-sprite.anchor.y); - h1 = texture._frame.height * -sprite.anchor.y; + h0 = crop.height * (1-sprite.anchor.y); + h1 = crop.height * -sprite.anchor.y; } array[offset] = w1 * sx; diff --git a/src/core/renderers/canvas/utils/CanvasTinter.js b/src/core/renderers/canvas/utils/CanvasTinter.js index eedf175..8827558 100644 --- a/src/core/renderers/canvas/utils/CanvasTinter.js +++ b/src/core/renderers/canvas/utils/CanvasTinter.js @@ -68,11 +68,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; @@ -123,11 +119,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; @@ -165,11 +157,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index b174736..970212f 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -111,12 +111,12 @@ width: { get: function () { - return Math.abs(this.scale.x) * this.texture._frame.width; + return Math.abs(this.scale.x) * this.texture.crop.width; }, set: function (value) { var sign = utils.sign(this.scale.x) || 1; - this.scale.x = sign * value / this.texture._frame.width; + this.scale.x = sign * value / this.texture.crop.width; this._width = value; } }, @@ -130,12 +130,12 @@ height: { get: function () { - return Math.abs(this.scale.y) * this.texture._frame.height; + return Math.abs(this.scale.y) * this.texture.crop.height; }, set: function (value) { var sign = utils.sign(this.scale.y) || 1; - this.scale.y = sign * value / this.texture._frame.height; + this.scale.y = sign * value / this.texture.crop.height; this._height = value; } }, @@ -191,12 +191,12 @@ // so if _width is 0 then width was not set.. if (this._width) { - this.scale.x = utils.sign(this.scale.x) * this._width / this.texture.frame.width; + this.scale.x = utils.sign(this.scale.x) * this._width / this.texture.crop.width; } if (this._height) { - this.scale.y = utils.sign(this.scale.y) * this._height / this.texture.frame.height; + this.scale.y = utils.sign(this.scale.y) * this._height / this.texture.crop.height; } }; @@ -207,25 +207,26 @@ a = wt.a, b = wt.b, c = wt.c, d = wt.d, tx = wt.tx, ty = wt.ty, vertexData = this.vertexData, w0, w1, h0, h1, - trim = texture.trim; - + trim = texture.trim, + crop = texture.crop; + if (trim) { // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space before transforming the sprite coords.. - w1 = trim.x - this.anchor.x * trim.width; - w0 = w1 + texture.crop.width; + w1 = trim.x - this.anchor.x * crop.width; + w0 = w1 + trim.width; - h1 = trim.y - this.anchor.y * trim.height; - h0 = h1 + texture.crop.height; + h1 = trim.y - this.anchor.y * crop.height; + h0 = h1 + trim.height; } else { - w0 = (texture._frame.width ) * (1-this.anchor.x); - w1 = (texture._frame.width ) * -this.anchor.x; + w0 = (crop.width ) * (1-this.anchor.x); + w1 = (crop.width ) * -this.anchor.x; - h0 = texture._frame.height * (1-this.anchor.y); - h1 = texture._frame.height * -this.anchor.y; + h0 = crop.height * (1-this.anchor.y); + h1 = crop.height * -this.anchor.y; } // xy @@ -244,7 +245,6 @@ vertexData[6] = a * w1 + c * h0 + tx; vertexData[7] = d * h0 + b * w1 + ty; - } /** @@ -262,7 +262,7 @@ // set the vertex data this.caclulateVertices(); } - + renderer.setObjectRenderer(renderer.plugins.sprite); renderer.plugins.sprite.render(this); }; @@ -363,10 +363,10 @@ */ Sprite.prototype.getLocalBounds = function () { - this._bounds.x = -this._texture._frame.width * this.anchor.x; - this._bounds.y = -this._texture._frame.height * this.anchor.y; - this._bounds.width = this._texture._frame.width; - this._bounds.height = this._texture._frame.height; + this._bounds.x = -this._texture.crop.width * this.anchor.x; + this._bounds.y = -this._texture.crop.height * this.anchor.y; + this._bounds.width = this._texture.crop.width; + this._bounds.height = this._texture.crop.height; return this._bounds; }; @@ -380,8 +380,8 @@ { this.worldTransform.applyInverse(point, tempPoint); - var width = this._texture._frame.width; - var height = this._texture._frame.height; + var width = this._texture.crop.width; + var height = this._texture.crop.height; var x1 = -width * this.anchor.x; var y1; @@ -424,8 +424,8 @@ wt = this.worldTransform, dx, dy, - width, - height; + width = texture._frame.width, + height = texture._frame.height; renderer.context.globalAlpha = this.worldAlpha; @@ -436,45 +436,23 @@ renderer.context[renderer.smoothProperty] = smoothingEnabled; } - // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions - - if(texture.rotate) - { - width = texture.crop.height; - height = texture.crop.width; - - dx = (texture.trim) ? texture.trim.y - this.anchor.y * texture.trim.height : this.anchor.y * -texture._frame.height; - dy = (texture.trim) ? texture.trim.x - this.anchor.x * texture.trim.width : this.anchor.x * -texture._frame.width; - - dx += width; - - wt.tx = dy * wt.a + dx * wt.c + wt.tx; - wt.ty = dy * wt.b + dx * wt.d + wt.ty; - - var temp = wt.a; - wt.a = -wt.c; - wt.c = temp; - - temp = wt.b; - wt.b = -wt.d; - wt.d = temp; - + if (texture.trim) { + dx = texture.trim.width/2 + texture.trim.x - this.anchor.x * texture.crop.width; + dy = texture.trim.height/2 + texture.trim.y - this.anchor.y * texture.crop.height; + } else { + dx = (0.5 - this.anchor.x) * texture.crop.width; + dy = (0.5 - this.anchor.y) * texture.crop.height; + } + if(texture.rotate) { + wt.copy(canvasRenderWorldTransform); + wt = canvasRenderWorldTransform; + GroupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); // the anchor has already been applied above, so lets set it to zero dx = 0; dy = 0; - } - else - { - width = texture.crop.width; - height = texture.crop.height; - - dx = (texture.trim) ? texture.trim.x - this.anchor.x * texture.trim.width : this.anchor.x * -texture._frame.width; - dy = (texture.trim) ? texture.trim.y - this.anchor.y * texture.trim.height : this.anchor.y * -texture._frame.height; - } - - - + dx -= width/2; + dy -= height/2; // Allow for pixel rounding if (renderer.roundPixels) { @@ -492,7 +470,6 @@ } else { - renderer.context.setTransform( wt.a, wt.b, @@ -501,8 +478,6 @@ wt.tx * renderer.resolution, wt.ty * renderer.resolution ); - - } var resolution = texture.baseTexture.resolution; @@ -533,8 +508,8 @@ { renderer.context.drawImage( texture.baseTexture.source, - texture.crop.x * resolution, - texture.crop.y * resolution, + texture.frame.x * resolution, + texture.frame.y * resolution, width * resolution, height * resolution, dx * renderer.resolution, diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 61b6d12..36ac3ed 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -4,7 +4,8 @@ createIndicesForQuads = require('../../utils/createIndicesForQuads'), generateMultiTextureShader = require('./generateMultiTextureShader'), CONST = require('../../const'), - glCore = require('pixi-gl-core'); + glCore = require('pixi-gl-core'), + bitTwiddle = require('bit-twiddle'); /** * @author Mat Groves @@ -53,38 +54,44 @@ this.size = CONST.SPRITE_BATCH_SIZE; // 2000 is a nice balance between mobile / desktop // the total number of bytes in our batch - var numVerts = (this.size * 4) * this.vertByteSize; + var numVerts = this.size * 4 * this.vertByteSize; // the total number of indices in our batch, there are 6 points per quad. var numIndices = this.size * 6; + this.buffers = []; + for (var i = 1; i <= 2048; i*=2) { + var numVerts = i * 4 * this.vertByteSize; + this.buffers.push(new Buffer(numVerts, i)); + }; + /** * Holds the vertex data that will be sent to the vertex shader. * * @member {ArrayBuffer} */ - this.vertices = new ArrayBuffer(numVerts); + // this.vertices = new ArrayBuffer(numVerts); /** * View on the vertices as a Float32Array for positions * * @member {Float32Array} */ - this.positions = new Float32Array(this.vertices); + // this.positions = new Float32Array(this.vertices); /** * View on the vertices as a Uint32Array for uvs * * @member {Float32Array} */ - this.uvs = new Uint32Array(this.vertices); + //this.uvs = new Uint32Array(this.vertices); /** * View on the vertices as a Uint32Array for colors * * @member {Uint32Array} */ - this.colors = new Uint32Array(this.vertices); + //this.colors = new Uint32Array(this.vertices); /** * Holds the indices of the geometry (quads) to draw @@ -115,6 +122,8 @@ this.currentGroup = this.groups[this.groupCount++]; this.currentTexture = null; + + this.sprites = []; } @@ -215,45 +224,14 @@ this.textureCount++; } - // TODO trim?? - var index = this.currentIndex * this.vertByteSize; + // TODO add this variable to sprite.. + sprite._glBatchTextureId = nextTexture._id; + + this.sprites[this.currentIndex] = sprite; this.currentIndex++; - // upload the sprite elemetns... - // they have all ready been calculated so we just need to push them into the buffer. - var colors = this.colors; - var positions = this.positions; - var vertexData = sprite.vertexData - var tint = (sprite.tint >> 16) + (sprite.tint & 0xff00) + ((sprite.tint & 0xff) << 16) + (sprite.worldAlpha * 255 << 24); - var uvs = sprite.texture._uvs.uvs_uint32; - //xy - positions[index++] = vertexData[0]; - positions[index++] = vertexData[1]; - this.uvs[index++] = uvs[0]; - colors[index++] = tint; - positions[index++] = nextTexture._id; - // xy - positions[index++] = vertexData[2]; - positions[index++] = vertexData[3]; - this.uvs[index++] = uvs[1]; - colors[index++] = tint; - positions[index++] = nextTexture._id; - - // xy - positions[index++] = vertexData[4]; - positions[index++] = vertexData[5]; - this.uvs[index++] = uvs[2]; - colors[index++] = tint; - positions[index++] = nextTexture._id; - - // xy - positions[index++] = vertexData[6]; - positions[index++] = vertexData[7]; - this.uvs[index++] = uvs[3]; - colors[index++] = tint; - positions[index++] = nextTexture._id; }; /** @@ -266,6 +244,59 @@ var gl = this.renderer.gl; + var np2 = bitTwiddle.nextPow2(this.currentIndex); + var log2 = bitTwiddle.log2(np2); + + var buffer = this.buffers[log2]; + + var colors = buffer.colors; + var positions = buffer.positions; + var uvsBuffer = buffer.uvs; + var index = 0; + + //console.log(this.currentIndex); + //var array = + + for (var i = 0; i < this.currentIndex; i++) + { + // upload the sprite elemetns... + // they have all ready been calculated so we just need to push them into the buffer. + var sprite = this.sprites[i]; + + var vertexData = sprite.vertexData + var tint = (sprite.tint >> 16) + (sprite.tint & 0xff00) + ((sprite.tint & 0xff) << 16) + (sprite.worldAlpha * 255 << 24); + var uvs = sprite.texture._uvs.uvs_uint32; + var textureId = sprite._glBatchTextureId; + + //xy + positions[index++] = vertexData[0]; + positions[index++] = vertexData[1]; + uvsBuffer[index++] = uvs[0]; + colors[index++] = tint; + positions[index++] = textureId; + + // xy + positions[index++] = vertexData[2]; + positions[index++] = vertexData[3]; + uvsBuffer[index++] = uvs[1]; + colors[index++] = tint; + positions[index++] = textureId; + + // xy + positions[index++] = vertexData[4]; + positions[index++] = vertexData[5]; + uvsBuffer[index++] = uvs[2]; + colors[index++] = tint; + positions[index++] = textureId; + + // xy + positions[index++] = vertexData[6]; + positions[index++] = vertexData[7]; + uvsBuffer[index++] = uvs[3]; + colors[index++] = tint; + positions[index++] = textureId; + + }; this.currentGroup.size = this.currentIndex - this.currentGroup.start; for (var i = 0; i < this.currentGroup.textureCount; i++) @@ -276,19 +307,19 @@ // do some smart array stuff.. // double size so we dont alway subarray the elements.. // upload the verts to the buffer - if (this.currentBatchSize > ( this.size * 0.5 ) ) - { - this.vertexBuffer.upload(this.vertices, 0, true); - } - else - { + //if (this.currentBatchSize > ( this.size * 0.5 ) ) + //{ + // this.vertexBuffer.upload(this.vertices, 0, true); + // } + //else + //{ // o k .. sub array is SLOW>? - var view = this.positions.subarray(0, this.currentIndex * this.vertByteSize); - this.vertexBuffer.upload(view, 0, true); - } + // var view = this.positions.subarray(0, this.currentIndex * this.vertByteSize); + this.vertexBuffer.upload(buffer.vertices, 0, true); + //} // bind shader.. - this.renderer.bindShader(this.shader); + this.renderer.blendModeManager.setBlendMode( 0 ); /// render the groups.. @@ -319,6 +350,7 @@ */ SpriteRenderer.prototype.start = function () { + this.renderer.bindShader(this.shader); this.vao.bind(); }; @@ -353,3 +385,33 @@ this.sprites = null; this.shader = null; }; + +var Buffer = function(size, realSize) +{ + + this.realSize = realSize; + this.vertices = new ArrayBuffer(size); + + /** + * View on the vertices as a Float32Array for positions + * + * @member {Float32Array} + */ + this.positions = new Float32Array(this.vertices); + + /** + * View on the vertices as a Uint32Array for uvs + * + * @member {Float32Array} + */ + this.uvs = new Uint32Array(this.vertices); + + /** + * View on the vertices as a Uint32Array for colors + * + * @member {Uint32Array} + */ + this.colors = new Uint32Array(this.vertices); + + //this.buffer = +} \ No newline at end of file diff --git a/src/core/sprites/webgl/generateMultiTextureShader.js b/src/core/sprites/webgl/generateMultiTextureShader.js index c864d37..4134111 100644 --- a/src/core/sprites/webgl/generateMultiTextureShader.js +++ b/src/core/sprites/webgl/generateMultiTextureShader.js @@ -4,13 +4,13 @@ function generateMultiTextureShader(gl, maxTextures) { var vertexSrc = fs.readFileSync(__dirname + '/texture.vert', 'utf8'); - var fragmentSrc = 'precision lowp float;\n\n' + generateSampleSrc(maxTextures) + "\n\n" + fragTemplate + var fragmentSrc = fragTemplate - fragmentSrc = fragmentSrc.replace(/\%\%/gi, '16'); + fragmentSrc = fragmentSrc.replace(/\%\%/gi, maxTextures); + fragmentSrc = fragmentSrc.replace(/\%forloop\%/gi, generateSampleSrc(maxTextures)); var shader = new Shader(gl, vertexSrc, fragmentSrc); - var sampleValues = []; for (var i = 0; i < maxTextures; i++) { sampleValues[i] = i; @@ -24,7 +24,7 @@ function generateSampleSrc(maxTextures) { - var src = 'vec4 getSampleFromArray(sampler2D textures[%%], int ndx, vec2 uv) {\n\nvec4 color;' + var src = '' src += '\n'; src += '\n'; @@ -34,27 +34,29 @@ if(i > 0)src += '\nelse '; if(i < maxTextures-1)src += 'if(ndx == ' + i + ')'; src += '\n{'; - src += '\n\tcolor = texture2D(textures['+i+'], uv);'; + src += '\n\tcolor = texture2D(uSamplers['+i+'], vTextureCoord);'; src += '\n}'; }; src += '\n'; src += '\n'; - src += 'return color;'; - src += '\n}'; return src; } var fragTemplate = [ + 'precision lowp float;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', 'varying float vTextureId;', 'uniform sampler2D uSamplers[%%];', 'void main(void){', - 'gl_FragColor = getSampleFromArray(uSamplers, int(vTextureId), vTextureCoord) * vColor;', + 'vec4 color;', + 'int ndx = int(vTextureId);', + '%forloop%', + 'gl_FragColor = color * vColor;', '}' ].join('\n'); diff --git a/package.json b/package.json index bc669c9..f149fa7 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ ], "dependencies": { "async": "^1.5.0", + "bit-twiddle": "^1.0.2", "brfs": "^1.4.1", "earcut": "^2.0.7", "eventemitter3": "^1.1.1", diff --git a/src/core/display/ObservablePoint.js b/src/core/display/ObservablePoint.js index fd7a04b..958661f 100644 --- a/src/core/display/ObservablePoint.js +++ b/src/core/display/ObservablePoint.js @@ -35,7 +35,7 @@ set: function (value) { this._x = value; - this.transform.dirty = true; + this.transform._versionLocal++; } }, @@ -47,7 +47,7 @@ set: function (value) { this._y = value; - this.transform.dirty = true; + this.transform._versionLocal++; } } }); @@ -63,6 +63,6 @@ { this._x = x || 0; this._y = y || ( (y !== 0) ? this._x : 0 ); - - this.transform.dirty = true; + + this.transform._versionLocal++; }; diff --git a/src/core/display/TransformStatic.js b/src/core/display/TransformStatic.js index 61161d0..b86aa2d 100644 --- a/src/core/display/TransformStatic.js +++ b/src/core/display/TransformStatic.js @@ -1,6 +1,7 @@ var math = require('../math'); var ObservablePoint = require('./ObservablePoint'); +var generatorId = 0; /** * The Point object represents a location in a two-dimensional coordinate system, where x represents * the horizontal axis and y represents the vertical axis. @@ -19,13 +20,17 @@ this.scale = new ObservablePoint(this,1, 1); this.pivot = new ObservablePoint(this, 0, 0); this.skew = new ObservablePoint(this, 0,0); - + this.rotation = 0; this._sr = Math.sin(0); this._cr = Math.cos(0); - - this.dirty = true; - this.updated = true; + + this._versionLocal = 0; + this._versionGlobal = 0; + this._dirtyLocal = 0; + this._dirtyParentVersion = -1; + this._dirtyParentId = -1; + this._transformId = ++generatorId; } TransformStatic.prototype.constructor = TransformStatic; @@ -36,21 +41,21 @@ var wt = this.worldTransform; var lt = this.localTransform; - if(this.dirty) + if(this._dirtyLocal !== this._versionLocal || + parentTransform._dirtyParentId !== parentTransform._transformId || + parentTransform._dirtyParentVersion !== parentTransform._versionGlobal ) { - // get the matrix values of the displayobject based on its transform properties.. - lt.a = this._cr * this.scale._x; - lt.b = this._sr * this.scale._x; - lt.c = -this._sr * this.scale._y; - lt.d = this._cr * this.scale._y; - lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); - lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); - } - - this.updated = this.dirty || parentTransform.updated; - - if(this.updated) - { + if(this._dirtyLocal !== this._versionLocal) + { + // get the matrix values of the displayobject based on its transform properties.. + lt.a = this._cr * this.scale._x; + lt.b = this._sr * this.scale._x; + lt.c = -this._sr * this.scale._y; + lt.d = this._cr * this.scale._y; + lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); + lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); + this._dirtyLocal = this._versionLocal; + } // concat the parent matrix with the objects transform. wt.a = lt.a * pt.a + lt.b * pt.c; wt.b = lt.a * pt.b + lt.b * pt.d; @@ -58,10 +63,12 @@ wt.d = lt.c * pt.b + lt.d * pt.d; wt.tx = lt.tx * pt.a + lt.ty * pt.c + pt.tx; wt.ty = lt.tx * pt.b + lt.ty * pt.d + pt.ty; + + this._dirtyParentId = parentTransform._transformId; + this._dirtyParentVersion = parentTransform._versionGlobal; + this._versionGlobal++; } - - this.dirty = false; -} +}; module.exports = TransformStatic; diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js new file mode 100644 index 0000000..1edb988 --- /dev/null +++ b/src/core/math/GroupD8.js @@ -0,0 +1,162 @@ +// Your friendly neighbour https://en.wikipedia.org/wiki/Dihedral_group of order 16 + +var ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1]; +var uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1]; +var tempMatrices = []; +var Matrix = require('./Matrix'); + +var mul = []; + +function signum(x) { + if (x < 0) { + return -1; + } + if (x > 0) { + return 1; + } + return 0; +} + +function init() { + for (var i = 0; i < 16; i++) { + var row = []; + mul.push(row); + for (var j = 0; j < 16; j++) { + var _ux = signum(ux[i] * ux[j] + vx[i] * uy[j]); + var _uy = signum(uy[i] * ux[j] + vy[i] * uy[j]); + var _vx = signum(ux[i] * vx[j] + vx[i] * vy[j]); + var _vy = signum(uy[i] * vx[j] + vy[i] * vy[j]); + for (var k = 0; k < 16; k++) { + if (ux[k] === _ux && uy[k] === _uy && vx[k] === _vx && vy[k] === _vy) { + row.push(k); + break; + } + } + } + } + + for (i=0;i<16;i++) { + var mat = new Matrix(); + mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0); + tempMatrices.push(mat); + } +} + +init(); + +/** + * Implements Dihedral Group D_8, see [group D4]{@link http://mathworld.wolfram.com/DihedralGroupD4.html}, D8 is the same but with diagonals + * Used for texture rotations + * Vector xX(i), xY(i) is U-axis of sprite with rotation i + * Vector yY(i), yY(i) is V-axis of sprite with rotation i + * Rotations: 0 grad (0), 90 grad (2), 180 grad (4), 270 grad (6) + * Mirrors: vertical (8), main diagonal (10), horizontal (12), reverse diagonal (14) + * This is the small part of gameofbombs.com portal system. It works. + * @author Ivan @ivanpopelyshev + * + * @namespace PIXI.GroupD8 + */ +var GroupD8 = { + E: 0, + SE: 1, + S: 2, + SW: 3, + W: 4, + NW: 5, + N: 6, + NE: 7, + MIRROR_VERTICAL: 8, + MIRROR_HORIZONTAL: 12, + uX: function (ind) { + return ux[ind]; + }, + uY: function (ind) { + return uy[ind]; + }, + vX: function (ind) { + return vx[ind]; + }, + vY: function (ind) { + return vy[ind]; + }, + inv: function (rotation) { + if (rotation & 8) { + return rotation & 15; + } + return (-rotation) & 7; + }, + add: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][rotationFirst]; + }, + sub: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][GroupD8.inv(rotationFirst)]; + }, + /** + * Adds 180 degrees to rotation. Commutative operation + * @param rotation + * @returns {number} + */ + rotate180: function (rotation) { + return rotation ^ 4; + }, + /** + * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * @param rotation + * @returns {boolean} + */ + isSwapWidthHeight: function(rotation) { + return (rotation & 3) === 2; + }, + byDirection: function (dx, dy) { + if (Math.abs(dx) * 2 <= Math.abs(dy)) { + if (dy >= 0) { + return GroupD8.S; + } + else { + return GroupD8.N; + } + } else if (Math.abs(dy) * 2 <= Math.abs(dx)) { + if (dx > 0) { + return GroupD8.E; + } + else { + return GroupD8.W; + } + } else { + if (dy > 0) { + if (dx > 0) { + return GroupD8.SE; + } + else { + return GroupD8.SW; + } + } + else if (dx > 0) { + return GroupD8.NE; + } + else { + return GroupD8.NW; + } + } + }, + /** + * Helps sprite to compensate texture packer rotation. + * @param matrix {PIXI.Matrix} sprite world matrix + * @param rotation {number} + * @param tx {number|*} sprite anchoring + * @param ty {number|*} sprite anchoring + */ + matrixAppendRotationInv: function (matrix, rotation, tx, ty) { + //Packer used "rotation", we use "inv(rotation)" + var mat = tempMatrices[GroupD8.inv(rotation)]; + tx = tx || 0; + ty = ty || 0; + mat.tx = tx; + mat.ty = ty; + matrix.append(mat); + } +}; + +module.exports = GroupD8; diff --git a/src/core/math/index.js b/src/core/math/index.js index fd0d9be..a43f6e7 100644 --- a/src/core/math/index.js +++ b/src/core/math/index.js @@ -11,6 +11,7 @@ Point: require('./Point'), Matrix: require('./Matrix'), + GroupD8: require('./GroupD8'), Circle: require('./shapes/Circle'), Ellipse: require('./shapes/Ellipse'), diff --git a/src/core/particles/webgl/ParticleRenderer.js b/src/core/particles/webgl/ParticleRenderer.js index 8481c92..0b0abd0 100644 --- a/src/core/particles/webgl/ParticleRenderer.js +++ b/src/core/particles/webgl/ParticleRenderer.js @@ -285,25 +285,25 @@ texture = sprite._texture; sx = sprite.scale.x; sy = sprite.scale.y; + var trim = texture.trim, crop = texture.crop; - if (texture.trim) + if (trim) { - // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. - trim = texture.trim; + // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space before transforming the sprite coords.. + w1 = trim.x - sprite.anchor.x * crop.width; + w0 = w1 + trim.width; - w1 = trim.x - sprite.anchor.x * trim.width; - w0 = w1 + texture.crop.width; + h1 = trim.y - sprite.anchor.y * crop.height; + h0 = h1 + trim.height; - h1 = trim.y - sprite.anchor.y * trim.height; - h0 = h1 + texture.crop.height; } else { - w0 = (texture._frame.width ) * (1-sprite.anchor.x); - w1 = (texture._frame.width ) * -sprite.anchor.x; + w0 = (crop.width ) * (1-sprite.anchor.x); + w1 = (crop.width ) * -sprite.anchor.x; - h0 = texture._frame.height * (1-sprite.anchor.y); - h1 = texture._frame.height * -sprite.anchor.y; + h0 = crop.height * (1-sprite.anchor.y); + h1 = crop.height * -sprite.anchor.y; } array[offset] = w1 * sx; diff --git a/src/core/renderers/canvas/utils/CanvasTinter.js b/src/core/renderers/canvas/utils/CanvasTinter.js index eedf175..8827558 100644 --- a/src/core/renderers/canvas/utils/CanvasTinter.js +++ b/src/core/renderers/canvas/utils/CanvasTinter.js @@ -68,11 +68,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; @@ -123,11 +119,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; @@ -165,11 +157,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index b174736..970212f 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -111,12 +111,12 @@ width: { get: function () { - return Math.abs(this.scale.x) * this.texture._frame.width; + return Math.abs(this.scale.x) * this.texture.crop.width; }, set: function (value) { var sign = utils.sign(this.scale.x) || 1; - this.scale.x = sign * value / this.texture._frame.width; + this.scale.x = sign * value / this.texture.crop.width; this._width = value; } }, @@ -130,12 +130,12 @@ height: { get: function () { - return Math.abs(this.scale.y) * this.texture._frame.height; + return Math.abs(this.scale.y) * this.texture.crop.height; }, set: function (value) { var sign = utils.sign(this.scale.y) || 1; - this.scale.y = sign * value / this.texture._frame.height; + this.scale.y = sign * value / this.texture.crop.height; this._height = value; } }, @@ -191,12 +191,12 @@ // so if _width is 0 then width was not set.. if (this._width) { - this.scale.x = utils.sign(this.scale.x) * this._width / this.texture.frame.width; + this.scale.x = utils.sign(this.scale.x) * this._width / this.texture.crop.width; } if (this._height) { - this.scale.y = utils.sign(this.scale.y) * this._height / this.texture.frame.height; + this.scale.y = utils.sign(this.scale.y) * this._height / this.texture.crop.height; } }; @@ -207,25 +207,26 @@ a = wt.a, b = wt.b, c = wt.c, d = wt.d, tx = wt.tx, ty = wt.ty, vertexData = this.vertexData, w0, w1, h0, h1, - trim = texture.trim; - + trim = texture.trim, + crop = texture.crop; + if (trim) { // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space before transforming the sprite coords.. - w1 = trim.x - this.anchor.x * trim.width; - w0 = w1 + texture.crop.width; + w1 = trim.x - this.anchor.x * crop.width; + w0 = w1 + trim.width; - h1 = trim.y - this.anchor.y * trim.height; - h0 = h1 + texture.crop.height; + h1 = trim.y - this.anchor.y * crop.height; + h0 = h1 + trim.height; } else { - w0 = (texture._frame.width ) * (1-this.anchor.x); - w1 = (texture._frame.width ) * -this.anchor.x; + w0 = (crop.width ) * (1-this.anchor.x); + w1 = (crop.width ) * -this.anchor.x; - h0 = texture._frame.height * (1-this.anchor.y); - h1 = texture._frame.height * -this.anchor.y; + h0 = crop.height * (1-this.anchor.y); + h1 = crop.height * -this.anchor.y; } // xy @@ -244,7 +245,6 @@ vertexData[6] = a * w1 + c * h0 + tx; vertexData[7] = d * h0 + b * w1 + ty; - } /** @@ -262,7 +262,7 @@ // set the vertex data this.caclulateVertices(); } - + renderer.setObjectRenderer(renderer.plugins.sprite); renderer.plugins.sprite.render(this); }; @@ -363,10 +363,10 @@ */ Sprite.prototype.getLocalBounds = function () { - this._bounds.x = -this._texture._frame.width * this.anchor.x; - this._bounds.y = -this._texture._frame.height * this.anchor.y; - this._bounds.width = this._texture._frame.width; - this._bounds.height = this._texture._frame.height; + this._bounds.x = -this._texture.crop.width * this.anchor.x; + this._bounds.y = -this._texture.crop.height * this.anchor.y; + this._bounds.width = this._texture.crop.width; + this._bounds.height = this._texture.crop.height; return this._bounds; }; @@ -380,8 +380,8 @@ { this.worldTransform.applyInverse(point, tempPoint); - var width = this._texture._frame.width; - var height = this._texture._frame.height; + var width = this._texture.crop.width; + var height = this._texture.crop.height; var x1 = -width * this.anchor.x; var y1; @@ -424,8 +424,8 @@ wt = this.worldTransform, dx, dy, - width, - height; + width = texture._frame.width, + height = texture._frame.height; renderer.context.globalAlpha = this.worldAlpha; @@ -436,45 +436,23 @@ renderer.context[renderer.smoothProperty] = smoothingEnabled; } - // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions - - if(texture.rotate) - { - width = texture.crop.height; - height = texture.crop.width; - - dx = (texture.trim) ? texture.trim.y - this.anchor.y * texture.trim.height : this.anchor.y * -texture._frame.height; - dy = (texture.trim) ? texture.trim.x - this.anchor.x * texture.trim.width : this.anchor.x * -texture._frame.width; - - dx += width; - - wt.tx = dy * wt.a + dx * wt.c + wt.tx; - wt.ty = dy * wt.b + dx * wt.d + wt.ty; - - var temp = wt.a; - wt.a = -wt.c; - wt.c = temp; - - temp = wt.b; - wt.b = -wt.d; - wt.d = temp; - + if (texture.trim) { + dx = texture.trim.width/2 + texture.trim.x - this.anchor.x * texture.crop.width; + dy = texture.trim.height/2 + texture.trim.y - this.anchor.y * texture.crop.height; + } else { + dx = (0.5 - this.anchor.x) * texture.crop.width; + dy = (0.5 - this.anchor.y) * texture.crop.height; + } + if(texture.rotate) { + wt.copy(canvasRenderWorldTransform); + wt = canvasRenderWorldTransform; + GroupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); // the anchor has already been applied above, so lets set it to zero dx = 0; dy = 0; - } - else - { - width = texture.crop.width; - height = texture.crop.height; - - dx = (texture.trim) ? texture.trim.x - this.anchor.x * texture.trim.width : this.anchor.x * -texture._frame.width; - dy = (texture.trim) ? texture.trim.y - this.anchor.y * texture.trim.height : this.anchor.y * -texture._frame.height; - } - - - + dx -= width/2; + dy -= height/2; // Allow for pixel rounding if (renderer.roundPixels) { @@ -492,7 +470,6 @@ } else { - renderer.context.setTransform( wt.a, wt.b, @@ -501,8 +478,6 @@ wt.tx * renderer.resolution, wt.ty * renderer.resolution ); - - } var resolution = texture.baseTexture.resolution; @@ -533,8 +508,8 @@ { renderer.context.drawImage( texture.baseTexture.source, - texture.crop.x * resolution, - texture.crop.y * resolution, + texture.frame.x * resolution, + texture.frame.y * resolution, width * resolution, height * resolution, dx * renderer.resolution, diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 61b6d12..36ac3ed 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -4,7 +4,8 @@ createIndicesForQuads = require('../../utils/createIndicesForQuads'), generateMultiTextureShader = require('./generateMultiTextureShader'), CONST = require('../../const'), - glCore = require('pixi-gl-core'); + glCore = require('pixi-gl-core'), + bitTwiddle = require('bit-twiddle'); /** * @author Mat Groves @@ -53,38 +54,44 @@ this.size = CONST.SPRITE_BATCH_SIZE; // 2000 is a nice balance between mobile / desktop // the total number of bytes in our batch - var numVerts = (this.size * 4) * this.vertByteSize; + var numVerts = this.size * 4 * this.vertByteSize; // the total number of indices in our batch, there are 6 points per quad. var numIndices = this.size * 6; + this.buffers = []; + for (var i = 1; i <= 2048; i*=2) { + var numVerts = i * 4 * this.vertByteSize; + this.buffers.push(new Buffer(numVerts, i)); + }; + /** * Holds the vertex data that will be sent to the vertex shader. * * @member {ArrayBuffer} */ - this.vertices = new ArrayBuffer(numVerts); + // this.vertices = new ArrayBuffer(numVerts); /** * View on the vertices as a Float32Array for positions * * @member {Float32Array} */ - this.positions = new Float32Array(this.vertices); + // this.positions = new Float32Array(this.vertices); /** * View on the vertices as a Uint32Array for uvs * * @member {Float32Array} */ - this.uvs = new Uint32Array(this.vertices); + //this.uvs = new Uint32Array(this.vertices); /** * View on the vertices as a Uint32Array for colors * * @member {Uint32Array} */ - this.colors = new Uint32Array(this.vertices); + //this.colors = new Uint32Array(this.vertices); /** * Holds the indices of the geometry (quads) to draw @@ -115,6 +122,8 @@ this.currentGroup = this.groups[this.groupCount++]; this.currentTexture = null; + + this.sprites = []; } @@ -215,45 +224,14 @@ this.textureCount++; } - // TODO trim?? - var index = this.currentIndex * this.vertByteSize; + // TODO add this variable to sprite.. + sprite._glBatchTextureId = nextTexture._id; + + this.sprites[this.currentIndex] = sprite; this.currentIndex++; - // upload the sprite elemetns... - // they have all ready been calculated so we just need to push them into the buffer. - var colors = this.colors; - var positions = this.positions; - var vertexData = sprite.vertexData - var tint = (sprite.tint >> 16) + (sprite.tint & 0xff00) + ((sprite.tint & 0xff) << 16) + (sprite.worldAlpha * 255 << 24); - var uvs = sprite.texture._uvs.uvs_uint32; - //xy - positions[index++] = vertexData[0]; - positions[index++] = vertexData[1]; - this.uvs[index++] = uvs[0]; - colors[index++] = tint; - positions[index++] = nextTexture._id; - // xy - positions[index++] = vertexData[2]; - positions[index++] = vertexData[3]; - this.uvs[index++] = uvs[1]; - colors[index++] = tint; - positions[index++] = nextTexture._id; - - // xy - positions[index++] = vertexData[4]; - positions[index++] = vertexData[5]; - this.uvs[index++] = uvs[2]; - colors[index++] = tint; - positions[index++] = nextTexture._id; - - // xy - positions[index++] = vertexData[6]; - positions[index++] = vertexData[7]; - this.uvs[index++] = uvs[3]; - colors[index++] = tint; - positions[index++] = nextTexture._id; }; /** @@ -266,6 +244,59 @@ var gl = this.renderer.gl; + var np2 = bitTwiddle.nextPow2(this.currentIndex); + var log2 = bitTwiddle.log2(np2); + + var buffer = this.buffers[log2]; + + var colors = buffer.colors; + var positions = buffer.positions; + var uvsBuffer = buffer.uvs; + var index = 0; + + //console.log(this.currentIndex); + //var array = + + for (var i = 0; i < this.currentIndex; i++) + { + // upload the sprite elemetns... + // they have all ready been calculated so we just need to push them into the buffer. + var sprite = this.sprites[i]; + + var vertexData = sprite.vertexData + var tint = (sprite.tint >> 16) + (sprite.tint & 0xff00) + ((sprite.tint & 0xff) << 16) + (sprite.worldAlpha * 255 << 24); + var uvs = sprite.texture._uvs.uvs_uint32; + var textureId = sprite._glBatchTextureId; + + //xy + positions[index++] = vertexData[0]; + positions[index++] = vertexData[1]; + uvsBuffer[index++] = uvs[0]; + colors[index++] = tint; + positions[index++] = textureId; + + // xy + positions[index++] = vertexData[2]; + positions[index++] = vertexData[3]; + uvsBuffer[index++] = uvs[1]; + colors[index++] = tint; + positions[index++] = textureId; + + // xy + positions[index++] = vertexData[4]; + positions[index++] = vertexData[5]; + uvsBuffer[index++] = uvs[2]; + colors[index++] = tint; + positions[index++] = textureId; + + // xy + positions[index++] = vertexData[6]; + positions[index++] = vertexData[7]; + uvsBuffer[index++] = uvs[3]; + colors[index++] = tint; + positions[index++] = textureId; + + }; this.currentGroup.size = this.currentIndex - this.currentGroup.start; for (var i = 0; i < this.currentGroup.textureCount; i++) @@ -276,19 +307,19 @@ // do some smart array stuff.. // double size so we dont alway subarray the elements.. // upload the verts to the buffer - if (this.currentBatchSize > ( this.size * 0.5 ) ) - { - this.vertexBuffer.upload(this.vertices, 0, true); - } - else - { + //if (this.currentBatchSize > ( this.size * 0.5 ) ) + //{ + // this.vertexBuffer.upload(this.vertices, 0, true); + // } + //else + //{ // o k .. sub array is SLOW>? - var view = this.positions.subarray(0, this.currentIndex * this.vertByteSize); - this.vertexBuffer.upload(view, 0, true); - } + // var view = this.positions.subarray(0, this.currentIndex * this.vertByteSize); + this.vertexBuffer.upload(buffer.vertices, 0, true); + //} // bind shader.. - this.renderer.bindShader(this.shader); + this.renderer.blendModeManager.setBlendMode( 0 ); /// render the groups.. @@ -319,6 +350,7 @@ */ SpriteRenderer.prototype.start = function () { + this.renderer.bindShader(this.shader); this.vao.bind(); }; @@ -353,3 +385,33 @@ this.sprites = null; this.shader = null; }; + +var Buffer = function(size, realSize) +{ + + this.realSize = realSize; + this.vertices = new ArrayBuffer(size); + + /** + * View on the vertices as a Float32Array for positions + * + * @member {Float32Array} + */ + this.positions = new Float32Array(this.vertices); + + /** + * View on the vertices as a Uint32Array for uvs + * + * @member {Float32Array} + */ + this.uvs = new Uint32Array(this.vertices); + + /** + * View on the vertices as a Uint32Array for colors + * + * @member {Uint32Array} + */ + this.colors = new Uint32Array(this.vertices); + + //this.buffer = +} \ No newline at end of file diff --git a/src/core/sprites/webgl/generateMultiTextureShader.js b/src/core/sprites/webgl/generateMultiTextureShader.js index c864d37..4134111 100644 --- a/src/core/sprites/webgl/generateMultiTextureShader.js +++ b/src/core/sprites/webgl/generateMultiTextureShader.js @@ -4,13 +4,13 @@ function generateMultiTextureShader(gl, maxTextures) { var vertexSrc = fs.readFileSync(__dirname + '/texture.vert', 'utf8'); - var fragmentSrc = 'precision lowp float;\n\n' + generateSampleSrc(maxTextures) + "\n\n" + fragTemplate + var fragmentSrc = fragTemplate - fragmentSrc = fragmentSrc.replace(/\%\%/gi, '16'); + fragmentSrc = fragmentSrc.replace(/\%\%/gi, maxTextures); + fragmentSrc = fragmentSrc.replace(/\%forloop\%/gi, generateSampleSrc(maxTextures)); var shader = new Shader(gl, vertexSrc, fragmentSrc); - var sampleValues = []; for (var i = 0; i < maxTextures; i++) { sampleValues[i] = i; @@ -24,7 +24,7 @@ function generateSampleSrc(maxTextures) { - var src = 'vec4 getSampleFromArray(sampler2D textures[%%], int ndx, vec2 uv) {\n\nvec4 color;' + var src = '' src += '\n'; src += '\n'; @@ -34,27 +34,29 @@ if(i > 0)src += '\nelse '; if(i < maxTextures-1)src += 'if(ndx == ' + i + ')'; src += '\n{'; - src += '\n\tcolor = texture2D(textures['+i+'], uv);'; + src += '\n\tcolor = texture2D(uSamplers['+i+'], vTextureCoord);'; src += '\n}'; }; src += '\n'; src += '\n'; - src += 'return color;'; - src += '\n}'; return src; } var fragTemplate = [ + 'precision lowp float;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', 'varying float vTextureId;', 'uniform sampler2D uSamplers[%%];', 'void main(void){', - 'gl_FragColor = getSampleFromArray(uSamplers, int(vTextureId), vTextureCoord) * vColor;', + 'vec4 color;', + 'int ndx = int(vTextureId);', + '%forloop%', + 'gl_FragColor = color * vColor;', '}' ].join('\n'); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index e43e8b5..00aaa6f 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -22,8 +22,8 @@ * @param baseTexture {PIXI.BaseTexture} The base texture source to create the texture from * @param [frame] {PIXI.Rectangle} The rectangle frame of the texture to show * @param [crop] {PIXI.Rectangle} The area of original texture - * @param [trim] {PIXI.Rectangle} Trimmed texture rectangle - * @param [rotate] {boolean} indicates whether the texture should be rotated by 90 degrees ( used by texture packer ) + * @param [trim] {PIXI.Rectangle} Trimmed rectangle of original texture + * @param [rotate] {number} indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} */ function Texture(baseTexture, frame, crop, trim, rotate) { @@ -55,15 +55,15 @@ this.baseTexture = baseTexture; /** - * The frame specifies the region of the base texture that this texture uses + * This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering, + * irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases) * * @member {PIXI.Rectangle} - * @private */ this._frame = frame; /** - * The texture trim data. + * This is the trimmed area of original texture, before it was put in atlas * * @member {PIXI.Rectangle} */ @@ -106,20 +106,22 @@ this.height = 0; /** - * This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering, - * irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases) + * This is the area of original texture, before it was put in atlas * * @member {PIXI.Rectangle} */ this.crop = crop || frame;//new math.Rectangle(0, 0, 1, 1); - /** - * Indicates whether the texture should be rotated by 90 degrees - * - * @private - * @member {boolean} - */ - this.rotate = !!rotate; + this._rotate = +(rotate || 0); + + if (rotate === true) { + // this is old texturepacker legacy, some games/libraries are passing "true" for rotated textures + this._rotate = 2; + } else { + if (this._rotate % 2 !== 0) { + throw 'attempt to use diamond-shaped UVs. If you are sure, set rotation manually'; + } + } if (baseTexture.hasLoaded) { @@ -168,10 +170,7 @@ this.noFrame = false; - this.width = frame.width; - this.height = frame.height; - - if (!this.trim && !this.rotate && (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height)) + if (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height) { throw new Error('Texture Error: frame does not fit inside the base Texture dimensions ' + this); } @@ -179,15 +178,9 @@ //this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded; this.valid = frame && frame.width && frame.height && this.baseTexture.hasLoaded; - if (this.trim) - { - this.width = this.trim.width; - this.height = this.trim.height; - this._frame.width = this.trim.width; - this._frame.height = this.trim.height; - } - else - { + if (!this.trim) { + this.width = frame.width; + this.height = frame.height; this.crop = frame; } @@ -196,6 +189,29 @@ this._updateUvs(); } } + }, + /** + * Indicates whether the texture is rotated inside the atlas + * set to 2 to compensate for texture packer rotation + * set to 6 to compensate for spine packer rotation + * can be used to rotate or mirror sprites + * See {@link PIXI.GroupD8} for explanation + * + * @member {number} + */ + rotate: { + get: function () + { + return this._rotate; + }, + set: function (rotate) + { + this._rotate = rotate; + if (this.valid) + { + this._updateUvs(); + } + } } }); @@ -294,7 +310,7 @@ this._uvs = new TextureUvs(); } - this._uvs.set(this.crop, this.baseTexture, this.rotate); + this._uvs.set(this._frame, this.baseTexture, this.rotate); }; /** diff --git a/package.json b/package.json index bc669c9..f149fa7 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ ], "dependencies": { "async": "^1.5.0", + "bit-twiddle": "^1.0.2", "brfs": "^1.4.1", "earcut": "^2.0.7", "eventemitter3": "^1.1.1", diff --git a/src/core/display/ObservablePoint.js b/src/core/display/ObservablePoint.js index fd7a04b..958661f 100644 --- a/src/core/display/ObservablePoint.js +++ b/src/core/display/ObservablePoint.js @@ -35,7 +35,7 @@ set: function (value) { this._x = value; - this.transform.dirty = true; + this.transform._versionLocal++; } }, @@ -47,7 +47,7 @@ set: function (value) { this._y = value; - this.transform.dirty = true; + this.transform._versionLocal++; } } }); @@ -63,6 +63,6 @@ { this._x = x || 0; this._y = y || ( (y !== 0) ? this._x : 0 ); - - this.transform.dirty = true; + + this.transform._versionLocal++; }; diff --git a/src/core/display/TransformStatic.js b/src/core/display/TransformStatic.js index 61161d0..b86aa2d 100644 --- a/src/core/display/TransformStatic.js +++ b/src/core/display/TransformStatic.js @@ -1,6 +1,7 @@ var math = require('../math'); var ObservablePoint = require('./ObservablePoint'); +var generatorId = 0; /** * The Point object represents a location in a two-dimensional coordinate system, where x represents * the horizontal axis and y represents the vertical axis. @@ -19,13 +20,17 @@ this.scale = new ObservablePoint(this,1, 1); this.pivot = new ObservablePoint(this, 0, 0); this.skew = new ObservablePoint(this, 0,0); - + this.rotation = 0; this._sr = Math.sin(0); this._cr = Math.cos(0); - - this.dirty = true; - this.updated = true; + + this._versionLocal = 0; + this._versionGlobal = 0; + this._dirtyLocal = 0; + this._dirtyParentVersion = -1; + this._dirtyParentId = -1; + this._transformId = ++generatorId; } TransformStatic.prototype.constructor = TransformStatic; @@ -36,21 +41,21 @@ var wt = this.worldTransform; var lt = this.localTransform; - if(this.dirty) + if(this._dirtyLocal !== this._versionLocal || + parentTransform._dirtyParentId !== parentTransform._transformId || + parentTransform._dirtyParentVersion !== parentTransform._versionGlobal ) { - // get the matrix values of the displayobject based on its transform properties.. - lt.a = this._cr * this.scale._x; - lt.b = this._sr * this.scale._x; - lt.c = -this._sr * this.scale._y; - lt.d = this._cr * this.scale._y; - lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); - lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); - } - - this.updated = this.dirty || parentTransform.updated; - - if(this.updated) - { + if(this._dirtyLocal !== this._versionLocal) + { + // get the matrix values of the displayobject based on its transform properties.. + lt.a = this._cr * this.scale._x; + lt.b = this._sr * this.scale._x; + lt.c = -this._sr * this.scale._y; + lt.d = this._cr * this.scale._y; + lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); + lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); + this._dirtyLocal = this._versionLocal; + } // concat the parent matrix with the objects transform. wt.a = lt.a * pt.a + lt.b * pt.c; wt.b = lt.a * pt.b + lt.b * pt.d; @@ -58,10 +63,12 @@ wt.d = lt.c * pt.b + lt.d * pt.d; wt.tx = lt.tx * pt.a + lt.ty * pt.c + pt.tx; wt.ty = lt.tx * pt.b + lt.ty * pt.d + pt.ty; + + this._dirtyParentId = parentTransform._transformId; + this._dirtyParentVersion = parentTransform._versionGlobal; + this._versionGlobal++; } - - this.dirty = false; -} +}; module.exports = TransformStatic; diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js new file mode 100644 index 0000000..1edb988 --- /dev/null +++ b/src/core/math/GroupD8.js @@ -0,0 +1,162 @@ +// Your friendly neighbour https://en.wikipedia.org/wiki/Dihedral_group of order 16 + +var ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1]; +var uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1]; +var tempMatrices = []; +var Matrix = require('./Matrix'); + +var mul = []; + +function signum(x) { + if (x < 0) { + return -1; + } + if (x > 0) { + return 1; + } + return 0; +} + +function init() { + for (var i = 0; i < 16; i++) { + var row = []; + mul.push(row); + for (var j = 0; j < 16; j++) { + var _ux = signum(ux[i] * ux[j] + vx[i] * uy[j]); + var _uy = signum(uy[i] * ux[j] + vy[i] * uy[j]); + var _vx = signum(ux[i] * vx[j] + vx[i] * vy[j]); + var _vy = signum(uy[i] * vx[j] + vy[i] * vy[j]); + for (var k = 0; k < 16; k++) { + if (ux[k] === _ux && uy[k] === _uy && vx[k] === _vx && vy[k] === _vy) { + row.push(k); + break; + } + } + } + } + + for (i=0;i<16;i++) { + var mat = new Matrix(); + mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0); + tempMatrices.push(mat); + } +} + +init(); + +/** + * Implements Dihedral Group D_8, see [group D4]{@link http://mathworld.wolfram.com/DihedralGroupD4.html}, D8 is the same but with diagonals + * Used for texture rotations + * Vector xX(i), xY(i) is U-axis of sprite with rotation i + * Vector yY(i), yY(i) is V-axis of sprite with rotation i + * Rotations: 0 grad (0), 90 grad (2), 180 grad (4), 270 grad (6) + * Mirrors: vertical (8), main diagonal (10), horizontal (12), reverse diagonal (14) + * This is the small part of gameofbombs.com portal system. It works. + * @author Ivan @ivanpopelyshev + * + * @namespace PIXI.GroupD8 + */ +var GroupD8 = { + E: 0, + SE: 1, + S: 2, + SW: 3, + W: 4, + NW: 5, + N: 6, + NE: 7, + MIRROR_VERTICAL: 8, + MIRROR_HORIZONTAL: 12, + uX: function (ind) { + return ux[ind]; + }, + uY: function (ind) { + return uy[ind]; + }, + vX: function (ind) { + return vx[ind]; + }, + vY: function (ind) { + return vy[ind]; + }, + inv: function (rotation) { + if (rotation & 8) { + return rotation & 15; + } + return (-rotation) & 7; + }, + add: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][rotationFirst]; + }, + sub: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][GroupD8.inv(rotationFirst)]; + }, + /** + * Adds 180 degrees to rotation. Commutative operation + * @param rotation + * @returns {number} + */ + rotate180: function (rotation) { + return rotation ^ 4; + }, + /** + * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * @param rotation + * @returns {boolean} + */ + isSwapWidthHeight: function(rotation) { + return (rotation & 3) === 2; + }, + byDirection: function (dx, dy) { + if (Math.abs(dx) * 2 <= Math.abs(dy)) { + if (dy >= 0) { + return GroupD8.S; + } + else { + return GroupD8.N; + } + } else if (Math.abs(dy) * 2 <= Math.abs(dx)) { + if (dx > 0) { + return GroupD8.E; + } + else { + return GroupD8.W; + } + } else { + if (dy > 0) { + if (dx > 0) { + return GroupD8.SE; + } + else { + return GroupD8.SW; + } + } + else if (dx > 0) { + return GroupD8.NE; + } + else { + return GroupD8.NW; + } + } + }, + /** + * Helps sprite to compensate texture packer rotation. + * @param matrix {PIXI.Matrix} sprite world matrix + * @param rotation {number} + * @param tx {number|*} sprite anchoring + * @param ty {number|*} sprite anchoring + */ + matrixAppendRotationInv: function (matrix, rotation, tx, ty) { + //Packer used "rotation", we use "inv(rotation)" + var mat = tempMatrices[GroupD8.inv(rotation)]; + tx = tx || 0; + ty = ty || 0; + mat.tx = tx; + mat.ty = ty; + matrix.append(mat); + } +}; + +module.exports = GroupD8; diff --git a/src/core/math/index.js b/src/core/math/index.js index fd0d9be..a43f6e7 100644 --- a/src/core/math/index.js +++ b/src/core/math/index.js @@ -11,6 +11,7 @@ Point: require('./Point'), Matrix: require('./Matrix'), + GroupD8: require('./GroupD8'), Circle: require('./shapes/Circle'), Ellipse: require('./shapes/Ellipse'), diff --git a/src/core/particles/webgl/ParticleRenderer.js b/src/core/particles/webgl/ParticleRenderer.js index 8481c92..0b0abd0 100644 --- a/src/core/particles/webgl/ParticleRenderer.js +++ b/src/core/particles/webgl/ParticleRenderer.js @@ -285,25 +285,25 @@ texture = sprite._texture; sx = sprite.scale.x; sy = sprite.scale.y; + var trim = texture.trim, crop = texture.crop; - if (texture.trim) + if (trim) { - // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. - trim = texture.trim; + // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space before transforming the sprite coords.. + w1 = trim.x - sprite.anchor.x * crop.width; + w0 = w1 + trim.width; - w1 = trim.x - sprite.anchor.x * trim.width; - w0 = w1 + texture.crop.width; + h1 = trim.y - sprite.anchor.y * crop.height; + h0 = h1 + trim.height; - h1 = trim.y - sprite.anchor.y * trim.height; - h0 = h1 + texture.crop.height; } else { - w0 = (texture._frame.width ) * (1-sprite.anchor.x); - w1 = (texture._frame.width ) * -sprite.anchor.x; + w0 = (crop.width ) * (1-sprite.anchor.x); + w1 = (crop.width ) * -sprite.anchor.x; - h0 = texture._frame.height * (1-sprite.anchor.y); - h1 = texture._frame.height * -sprite.anchor.y; + h0 = crop.height * (1-sprite.anchor.y); + h1 = crop.height * -sprite.anchor.y; } array[offset] = w1 * sx; diff --git a/src/core/renderers/canvas/utils/CanvasTinter.js b/src/core/renderers/canvas/utils/CanvasTinter.js index eedf175..8827558 100644 --- a/src/core/renderers/canvas/utils/CanvasTinter.js +++ b/src/core/renderers/canvas/utils/CanvasTinter.js @@ -68,11 +68,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; @@ -123,11 +119,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; @@ -165,11 +157,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index b174736..970212f 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -111,12 +111,12 @@ width: { get: function () { - return Math.abs(this.scale.x) * this.texture._frame.width; + return Math.abs(this.scale.x) * this.texture.crop.width; }, set: function (value) { var sign = utils.sign(this.scale.x) || 1; - this.scale.x = sign * value / this.texture._frame.width; + this.scale.x = sign * value / this.texture.crop.width; this._width = value; } }, @@ -130,12 +130,12 @@ height: { get: function () { - return Math.abs(this.scale.y) * this.texture._frame.height; + return Math.abs(this.scale.y) * this.texture.crop.height; }, set: function (value) { var sign = utils.sign(this.scale.y) || 1; - this.scale.y = sign * value / this.texture._frame.height; + this.scale.y = sign * value / this.texture.crop.height; this._height = value; } }, @@ -191,12 +191,12 @@ // so if _width is 0 then width was not set.. if (this._width) { - this.scale.x = utils.sign(this.scale.x) * this._width / this.texture.frame.width; + this.scale.x = utils.sign(this.scale.x) * this._width / this.texture.crop.width; } if (this._height) { - this.scale.y = utils.sign(this.scale.y) * this._height / this.texture.frame.height; + this.scale.y = utils.sign(this.scale.y) * this._height / this.texture.crop.height; } }; @@ -207,25 +207,26 @@ a = wt.a, b = wt.b, c = wt.c, d = wt.d, tx = wt.tx, ty = wt.ty, vertexData = this.vertexData, w0, w1, h0, h1, - trim = texture.trim; - + trim = texture.trim, + crop = texture.crop; + if (trim) { // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space before transforming the sprite coords.. - w1 = trim.x - this.anchor.x * trim.width; - w0 = w1 + texture.crop.width; + w1 = trim.x - this.anchor.x * crop.width; + w0 = w1 + trim.width; - h1 = trim.y - this.anchor.y * trim.height; - h0 = h1 + texture.crop.height; + h1 = trim.y - this.anchor.y * crop.height; + h0 = h1 + trim.height; } else { - w0 = (texture._frame.width ) * (1-this.anchor.x); - w1 = (texture._frame.width ) * -this.anchor.x; + w0 = (crop.width ) * (1-this.anchor.x); + w1 = (crop.width ) * -this.anchor.x; - h0 = texture._frame.height * (1-this.anchor.y); - h1 = texture._frame.height * -this.anchor.y; + h0 = crop.height * (1-this.anchor.y); + h1 = crop.height * -this.anchor.y; } // xy @@ -244,7 +245,6 @@ vertexData[6] = a * w1 + c * h0 + tx; vertexData[7] = d * h0 + b * w1 + ty; - } /** @@ -262,7 +262,7 @@ // set the vertex data this.caclulateVertices(); } - + renderer.setObjectRenderer(renderer.plugins.sprite); renderer.plugins.sprite.render(this); }; @@ -363,10 +363,10 @@ */ Sprite.prototype.getLocalBounds = function () { - this._bounds.x = -this._texture._frame.width * this.anchor.x; - this._bounds.y = -this._texture._frame.height * this.anchor.y; - this._bounds.width = this._texture._frame.width; - this._bounds.height = this._texture._frame.height; + this._bounds.x = -this._texture.crop.width * this.anchor.x; + this._bounds.y = -this._texture.crop.height * this.anchor.y; + this._bounds.width = this._texture.crop.width; + this._bounds.height = this._texture.crop.height; return this._bounds; }; @@ -380,8 +380,8 @@ { this.worldTransform.applyInverse(point, tempPoint); - var width = this._texture._frame.width; - var height = this._texture._frame.height; + var width = this._texture.crop.width; + var height = this._texture.crop.height; var x1 = -width * this.anchor.x; var y1; @@ -424,8 +424,8 @@ wt = this.worldTransform, dx, dy, - width, - height; + width = texture._frame.width, + height = texture._frame.height; renderer.context.globalAlpha = this.worldAlpha; @@ -436,45 +436,23 @@ renderer.context[renderer.smoothProperty] = smoothingEnabled; } - // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions - - if(texture.rotate) - { - width = texture.crop.height; - height = texture.crop.width; - - dx = (texture.trim) ? texture.trim.y - this.anchor.y * texture.trim.height : this.anchor.y * -texture._frame.height; - dy = (texture.trim) ? texture.trim.x - this.anchor.x * texture.trim.width : this.anchor.x * -texture._frame.width; - - dx += width; - - wt.tx = dy * wt.a + dx * wt.c + wt.tx; - wt.ty = dy * wt.b + dx * wt.d + wt.ty; - - var temp = wt.a; - wt.a = -wt.c; - wt.c = temp; - - temp = wt.b; - wt.b = -wt.d; - wt.d = temp; - + if (texture.trim) { + dx = texture.trim.width/2 + texture.trim.x - this.anchor.x * texture.crop.width; + dy = texture.trim.height/2 + texture.trim.y - this.anchor.y * texture.crop.height; + } else { + dx = (0.5 - this.anchor.x) * texture.crop.width; + dy = (0.5 - this.anchor.y) * texture.crop.height; + } + if(texture.rotate) { + wt.copy(canvasRenderWorldTransform); + wt = canvasRenderWorldTransform; + GroupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); // the anchor has already been applied above, so lets set it to zero dx = 0; dy = 0; - } - else - { - width = texture.crop.width; - height = texture.crop.height; - - dx = (texture.trim) ? texture.trim.x - this.anchor.x * texture.trim.width : this.anchor.x * -texture._frame.width; - dy = (texture.trim) ? texture.trim.y - this.anchor.y * texture.trim.height : this.anchor.y * -texture._frame.height; - } - - - + dx -= width/2; + dy -= height/2; // Allow for pixel rounding if (renderer.roundPixels) { @@ -492,7 +470,6 @@ } else { - renderer.context.setTransform( wt.a, wt.b, @@ -501,8 +478,6 @@ wt.tx * renderer.resolution, wt.ty * renderer.resolution ); - - } var resolution = texture.baseTexture.resolution; @@ -533,8 +508,8 @@ { renderer.context.drawImage( texture.baseTexture.source, - texture.crop.x * resolution, - texture.crop.y * resolution, + texture.frame.x * resolution, + texture.frame.y * resolution, width * resolution, height * resolution, dx * renderer.resolution, diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 61b6d12..36ac3ed 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -4,7 +4,8 @@ createIndicesForQuads = require('../../utils/createIndicesForQuads'), generateMultiTextureShader = require('./generateMultiTextureShader'), CONST = require('../../const'), - glCore = require('pixi-gl-core'); + glCore = require('pixi-gl-core'), + bitTwiddle = require('bit-twiddle'); /** * @author Mat Groves @@ -53,38 +54,44 @@ this.size = CONST.SPRITE_BATCH_SIZE; // 2000 is a nice balance between mobile / desktop // the total number of bytes in our batch - var numVerts = (this.size * 4) * this.vertByteSize; + var numVerts = this.size * 4 * this.vertByteSize; // the total number of indices in our batch, there are 6 points per quad. var numIndices = this.size * 6; + this.buffers = []; + for (var i = 1; i <= 2048; i*=2) { + var numVerts = i * 4 * this.vertByteSize; + this.buffers.push(new Buffer(numVerts, i)); + }; + /** * Holds the vertex data that will be sent to the vertex shader. * * @member {ArrayBuffer} */ - this.vertices = new ArrayBuffer(numVerts); + // this.vertices = new ArrayBuffer(numVerts); /** * View on the vertices as a Float32Array for positions * * @member {Float32Array} */ - this.positions = new Float32Array(this.vertices); + // this.positions = new Float32Array(this.vertices); /** * View on the vertices as a Uint32Array for uvs * * @member {Float32Array} */ - this.uvs = new Uint32Array(this.vertices); + //this.uvs = new Uint32Array(this.vertices); /** * View on the vertices as a Uint32Array for colors * * @member {Uint32Array} */ - this.colors = new Uint32Array(this.vertices); + //this.colors = new Uint32Array(this.vertices); /** * Holds the indices of the geometry (quads) to draw @@ -115,6 +122,8 @@ this.currentGroup = this.groups[this.groupCount++]; this.currentTexture = null; + + this.sprites = []; } @@ -215,45 +224,14 @@ this.textureCount++; } - // TODO trim?? - var index = this.currentIndex * this.vertByteSize; + // TODO add this variable to sprite.. + sprite._glBatchTextureId = nextTexture._id; + + this.sprites[this.currentIndex] = sprite; this.currentIndex++; - // upload the sprite elemetns... - // they have all ready been calculated so we just need to push them into the buffer. - var colors = this.colors; - var positions = this.positions; - var vertexData = sprite.vertexData - var tint = (sprite.tint >> 16) + (sprite.tint & 0xff00) + ((sprite.tint & 0xff) << 16) + (sprite.worldAlpha * 255 << 24); - var uvs = sprite.texture._uvs.uvs_uint32; - //xy - positions[index++] = vertexData[0]; - positions[index++] = vertexData[1]; - this.uvs[index++] = uvs[0]; - colors[index++] = tint; - positions[index++] = nextTexture._id; - // xy - positions[index++] = vertexData[2]; - positions[index++] = vertexData[3]; - this.uvs[index++] = uvs[1]; - colors[index++] = tint; - positions[index++] = nextTexture._id; - - // xy - positions[index++] = vertexData[4]; - positions[index++] = vertexData[5]; - this.uvs[index++] = uvs[2]; - colors[index++] = tint; - positions[index++] = nextTexture._id; - - // xy - positions[index++] = vertexData[6]; - positions[index++] = vertexData[7]; - this.uvs[index++] = uvs[3]; - colors[index++] = tint; - positions[index++] = nextTexture._id; }; /** @@ -266,6 +244,59 @@ var gl = this.renderer.gl; + var np2 = bitTwiddle.nextPow2(this.currentIndex); + var log2 = bitTwiddle.log2(np2); + + var buffer = this.buffers[log2]; + + var colors = buffer.colors; + var positions = buffer.positions; + var uvsBuffer = buffer.uvs; + var index = 0; + + //console.log(this.currentIndex); + //var array = + + for (var i = 0; i < this.currentIndex; i++) + { + // upload the sprite elemetns... + // they have all ready been calculated so we just need to push them into the buffer. + var sprite = this.sprites[i]; + + var vertexData = sprite.vertexData + var tint = (sprite.tint >> 16) + (sprite.tint & 0xff00) + ((sprite.tint & 0xff) << 16) + (sprite.worldAlpha * 255 << 24); + var uvs = sprite.texture._uvs.uvs_uint32; + var textureId = sprite._glBatchTextureId; + + //xy + positions[index++] = vertexData[0]; + positions[index++] = vertexData[1]; + uvsBuffer[index++] = uvs[0]; + colors[index++] = tint; + positions[index++] = textureId; + + // xy + positions[index++] = vertexData[2]; + positions[index++] = vertexData[3]; + uvsBuffer[index++] = uvs[1]; + colors[index++] = tint; + positions[index++] = textureId; + + // xy + positions[index++] = vertexData[4]; + positions[index++] = vertexData[5]; + uvsBuffer[index++] = uvs[2]; + colors[index++] = tint; + positions[index++] = textureId; + + // xy + positions[index++] = vertexData[6]; + positions[index++] = vertexData[7]; + uvsBuffer[index++] = uvs[3]; + colors[index++] = tint; + positions[index++] = textureId; + + }; this.currentGroup.size = this.currentIndex - this.currentGroup.start; for (var i = 0; i < this.currentGroup.textureCount; i++) @@ -276,19 +307,19 @@ // do some smart array stuff.. // double size so we dont alway subarray the elements.. // upload the verts to the buffer - if (this.currentBatchSize > ( this.size * 0.5 ) ) - { - this.vertexBuffer.upload(this.vertices, 0, true); - } - else - { + //if (this.currentBatchSize > ( this.size * 0.5 ) ) + //{ + // this.vertexBuffer.upload(this.vertices, 0, true); + // } + //else + //{ // o k .. sub array is SLOW>? - var view = this.positions.subarray(0, this.currentIndex * this.vertByteSize); - this.vertexBuffer.upload(view, 0, true); - } + // var view = this.positions.subarray(0, this.currentIndex * this.vertByteSize); + this.vertexBuffer.upload(buffer.vertices, 0, true); + //} // bind shader.. - this.renderer.bindShader(this.shader); + this.renderer.blendModeManager.setBlendMode( 0 ); /// render the groups.. @@ -319,6 +350,7 @@ */ SpriteRenderer.prototype.start = function () { + this.renderer.bindShader(this.shader); this.vao.bind(); }; @@ -353,3 +385,33 @@ this.sprites = null; this.shader = null; }; + +var Buffer = function(size, realSize) +{ + + this.realSize = realSize; + this.vertices = new ArrayBuffer(size); + + /** + * View on the vertices as a Float32Array for positions + * + * @member {Float32Array} + */ + this.positions = new Float32Array(this.vertices); + + /** + * View on the vertices as a Uint32Array for uvs + * + * @member {Float32Array} + */ + this.uvs = new Uint32Array(this.vertices); + + /** + * View on the vertices as a Uint32Array for colors + * + * @member {Uint32Array} + */ + this.colors = new Uint32Array(this.vertices); + + //this.buffer = +} \ No newline at end of file diff --git a/src/core/sprites/webgl/generateMultiTextureShader.js b/src/core/sprites/webgl/generateMultiTextureShader.js index c864d37..4134111 100644 --- a/src/core/sprites/webgl/generateMultiTextureShader.js +++ b/src/core/sprites/webgl/generateMultiTextureShader.js @@ -4,13 +4,13 @@ function generateMultiTextureShader(gl, maxTextures) { var vertexSrc = fs.readFileSync(__dirname + '/texture.vert', 'utf8'); - var fragmentSrc = 'precision lowp float;\n\n' + generateSampleSrc(maxTextures) + "\n\n" + fragTemplate + var fragmentSrc = fragTemplate - fragmentSrc = fragmentSrc.replace(/\%\%/gi, '16'); + fragmentSrc = fragmentSrc.replace(/\%\%/gi, maxTextures); + fragmentSrc = fragmentSrc.replace(/\%forloop\%/gi, generateSampleSrc(maxTextures)); var shader = new Shader(gl, vertexSrc, fragmentSrc); - var sampleValues = []; for (var i = 0; i < maxTextures; i++) { sampleValues[i] = i; @@ -24,7 +24,7 @@ function generateSampleSrc(maxTextures) { - var src = 'vec4 getSampleFromArray(sampler2D textures[%%], int ndx, vec2 uv) {\n\nvec4 color;' + var src = '' src += '\n'; src += '\n'; @@ -34,27 +34,29 @@ if(i > 0)src += '\nelse '; if(i < maxTextures-1)src += 'if(ndx == ' + i + ')'; src += '\n{'; - src += '\n\tcolor = texture2D(textures['+i+'], uv);'; + src += '\n\tcolor = texture2D(uSamplers['+i+'], vTextureCoord);'; src += '\n}'; }; src += '\n'; src += '\n'; - src += 'return color;'; - src += '\n}'; return src; } var fragTemplate = [ + 'precision lowp float;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', 'varying float vTextureId;', 'uniform sampler2D uSamplers[%%];', 'void main(void){', - 'gl_FragColor = getSampleFromArray(uSamplers, int(vTextureId), vTextureCoord) * vColor;', + 'vec4 color;', + 'int ndx = int(vTextureId);', + '%forloop%', + 'gl_FragColor = color * vColor;', '}' ].join('\n'); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index e43e8b5..00aaa6f 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -22,8 +22,8 @@ * @param baseTexture {PIXI.BaseTexture} The base texture source to create the texture from * @param [frame] {PIXI.Rectangle} The rectangle frame of the texture to show * @param [crop] {PIXI.Rectangle} The area of original texture - * @param [trim] {PIXI.Rectangle} Trimmed texture rectangle - * @param [rotate] {boolean} indicates whether the texture should be rotated by 90 degrees ( used by texture packer ) + * @param [trim] {PIXI.Rectangle} Trimmed rectangle of original texture + * @param [rotate] {number} indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} */ function Texture(baseTexture, frame, crop, trim, rotate) { @@ -55,15 +55,15 @@ this.baseTexture = baseTexture; /** - * The frame specifies the region of the base texture that this texture uses + * This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering, + * irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases) * * @member {PIXI.Rectangle} - * @private */ this._frame = frame; /** - * The texture trim data. + * This is the trimmed area of original texture, before it was put in atlas * * @member {PIXI.Rectangle} */ @@ -106,20 +106,22 @@ this.height = 0; /** - * This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering, - * irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases) + * This is the area of original texture, before it was put in atlas * * @member {PIXI.Rectangle} */ this.crop = crop || frame;//new math.Rectangle(0, 0, 1, 1); - /** - * Indicates whether the texture should be rotated by 90 degrees - * - * @private - * @member {boolean} - */ - this.rotate = !!rotate; + this._rotate = +(rotate || 0); + + if (rotate === true) { + // this is old texturepacker legacy, some games/libraries are passing "true" for rotated textures + this._rotate = 2; + } else { + if (this._rotate % 2 !== 0) { + throw 'attempt to use diamond-shaped UVs. If you are sure, set rotation manually'; + } + } if (baseTexture.hasLoaded) { @@ -168,10 +170,7 @@ this.noFrame = false; - this.width = frame.width; - this.height = frame.height; - - if (!this.trim && !this.rotate && (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height)) + if (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height) { throw new Error('Texture Error: frame does not fit inside the base Texture dimensions ' + this); } @@ -179,15 +178,9 @@ //this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded; this.valid = frame && frame.width && frame.height && this.baseTexture.hasLoaded; - if (this.trim) - { - this.width = this.trim.width; - this.height = this.trim.height; - this._frame.width = this.trim.width; - this._frame.height = this.trim.height; - } - else - { + if (!this.trim) { + this.width = frame.width; + this.height = frame.height; this.crop = frame; } @@ -196,6 +189,29 @@ this._updateUvs(); } } + }, + /** + * Indicates whether the texture is rotated inside the atlas + * set to 2 to compensate for texture packer rotation + * set to 6 to compensate for spine packer rotation + * can be used to rotate or mirror sprites + * See {@link PIXI.GroupD8} for explanation + * + * @member {number} + */ + rotate: { + get: function () + { + return this._rotate; + }, + set: function (rotate) + { + this._rotate = rotate; + if (this.valid) + { + this._updateUvs(); + } + } } }); @@ -294,7 +310,7 @@ this._uvs = new TextureUvs(); } - this._uvs.set(this.crop, this.baseTexture, this.rotate); + this._uvs.set(this._frame, this.baseTexture, this.rotate); }; /** diff --git a/src/core/textures/TextureUvs.js b/src/core/textures/TextureUvs.js index c5e4564..11bed5d 100644 --- a/src/core/textures/TextureUvs.js +++ b/src/core/textures/TextureUvs.js @@ -30,11 +30,13 @@ module.exports = TextureUvs; +var GroupD8 = require('../math/GroupD8'); + /** * Sets the texture Uvs based on the given frame information * @param frame {PIXI.Rectangle} * @param baseFrame {PIXI.Rectangle} - * @param rotate {boolean} Whether or not the frame is rotated + * @param rotate {number} Rotation of frame, see {@link PIXI.GroupD8} * @private */ TextureUvs.prototype.set = function (frame, baseFrame, rotate) @@ -44,17 +46,24 @@ if(rotate) { - this.x0 = (frame.x + frame.height) / tw; - this.y0 = frame.y / th; - - this.x1 = (frame.x + frame.height) / tw; - this.y1 = (frame.y + frame.width) / th; - - this.x2 = frame.x / tw; - this.y2 = (frame.y + frame.width) / th; - - this.x3 = frame.x / tw; - this.y3 = frame.y / th; + //width and height div 2 div baseFrame size + var w2 = frame.width / 2 / tw; + var h2 = frame.height / 2 / th; + //coordinates of center + var cX = frame.x / tw + w2; + var cY = frame.y / th + h2; + rotate = GroupD8.add(rotate, GroupD8.NW); //NW is top-left corner + this.x0 = cX + w2 * GroupD8.uX(rotate); + this.y0 = cY + h2 * GroupD8.uY(rotate); + rotate = GroupD8.add(rotate, 2); //rotate 90 degrees clockwise + this.x1 = cX + w2 * GroupD8.uX(rotate); + this.y1 = cY + h2 * GroupD8.uY(rotate); + rotate = GroupD8.add(rotate, 2); + this.x2 = cX + w2 * GroupD8.uX(rotate); + this.y2 = cY + h2 * GroupD8.uY(rotate); + rotate = GroupD8.add(rotate, 2); + this.x3 = cX + w2 * GroupD8.uX(rotate); + this.y3 = cY + h2 * GroupD8.uY(rotate); } else { diff --git a/package.json b/package.json index bc669c9..f149fa7 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ ], "dependencies": { "async": "^1.5.0", + "bit-twiddle": "^1.0.2", "brfs": "^1.4.1", "earcut": "^2.0.7", "eventemitter3": "^1.1.1", diff --git a/src/core/display/ObservablePoint.js b/src/core/display/ObservablePoint.js index fd7a04b..958661f 100644 --- a/src/core/display/ObservablePoint.js +++ b/src/core/display/ObservablePoint.js @@ -35,7 +35,7 @@ set: function (value) { this._x = value; - this.transform.dirty = true; + this.transform._versionLocal++; } }, @@ -47,7 +47,7 @@ set: function (value) { this._y = value; - this.transform.dirty = true; + this.transform._versionLocal++; } } }); @@ -63,6 +63,6 @@ { this._x = x || 0; this._y = y || ( (y !== 0) ? this._x : 0 ); - - this.transform.dirty = true; + + this.transform._versionLocal++; }; diff --git a/src/core/display/TransformStatic.js b/src/core/display/TransformStatic.js index 61161d0..b86aa2d 100644 --- a/src/core/display/TransformStatic.js +++ b/src/core/display/TransformStatic.js @@ -1,6 +1,7 @@ var math = require('../math'); var ObservablePoint = require('./ObservablePoint'); +var generatorId = 0; /** * The Point object represents a location in a two-dimensional coordinate system, where x represents * the horizontal axis and y represents the vertical axis. @@ -19,13 +20,17 @@ this.scale = new ObservablePoint(this,1, 1); this.pivot = new ObservablePoint(this, 0, 0); this.skew = new ObservablePoint(this, 0,0); - + this.rotation = 0; this._sr = Math.sin(0); this._cr = Math.cos(0); - - this.dirty = true; - this.updated = true; + + this._versionLocal = 0; + this._versionGlobal = 0; + this._dirtyLocal = 0; + this._dirtyParentVersion = -1; + this._dirtyParentId = -1; + this._transformId = ++generatorId; } TransformStatic.prototype.constructor = TransformStatic; @@ -36,21 +41,21 @@ var wt = this.worldTransform; var lt = this.localTransform; - if(this.dirty) + if(this._dirtyLocal !== this._versionLocal || + parentTransform._dirtyParentId !== parentTransform._transformId || + parentTransform._dirtyParentVersion !== parentTransform._versionGlobal ) { - // get the matrix values of the displayobject based on its transform properties.. - lt.a = this._cr * this.scale._x; - lt.b = this._sr * this.scale._x; - lt.c = -this._sr * this.scale._y; - lt.d = this._cr * this.scale._y; - lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); - lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); - } - - this.updated = this.dirty || parentTransform.updated; - - if(this.updated) - { + if(this._dirtyLocal !== this._versionLocal) + { + // get the matrix values of the displayobject based on its transform properties.. + lt.a = this._cr * this.scale._x; + lt.b = this._sr * this.scale._x; + lt.c = -this._sr * this.scale._y; + lt.d = this._cr * this.scale._y; + lt.tx = this.position._x - (this.pivot._x * lt.a + this.pivot._y * lt.c); + lt.ty = this.position._y - (this.pivot._x * lt.b + this.pivot._y * lt.d); + this._dirtyLocal = this._versionLocal; + } // concat the parent matrix with the objects transform. wt.a = lt.a * pt.a + lt.b * pt.c; wt.b = lt.a * pt.b + lt.b * pt.d; @@ -58,10 +63,12 @@ wt.d = lt.c * pt.b + lt.d * pt.d; wt.tx = lt.tx * pt.a + lt.ty * pt.c + pt.tx; wt.ty = lt.tx * pt.b + lt.ty * pt.d + pt.ty; + + this._dirtyParentId = parentTransform._transformId; + this._dirtyParentVersion = parentTransform._versionGlobal; + this._versionGlobal++; } - - this.dirty = false; -} +}; module.exports = TransformStatic; diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js new file mode 100644 index 0000000..1edb988 --- /dev/null +++ b/src/core/math/GroupD8.js @@ -0,0 +1,162 @@ +// Your friendly neighbour https://en.wikipedia.org/wiki/Dihedral_group of order 16 + +var ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1]; +var uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1]; +var vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1]; +var tempMatrices = []; +var Matrix = require('./Matrix'); + +var mul = []; + +function signum(x) { + if (x < 0) { + return -1; + } + if (x > 0) { + return 1; + } + return 0; +} + +function init() { + for (var i = 0; i < 16; i++) { + var row = []; + mul.push(row); + for (var j = 0; j < 16; j++) { + var _ux = signum(ux[i] * ux[j] + vx[i] * uy[j]); + var _uy = signum(uy[i] * ux[j] + vy[i] * uy[j]); + var _vx = signum(ux[i] * vx[j] + vx[i] * vy[j]); + var _vy = signum(uy[i] * vx[j] + vy[i] * vy[j]); + for (var k = 0; k < 16; k++) { + if (ux[k] === _ux && uy[k] === _uy && vx[k] === _vx && vy[k] === _vy) { + row.push(k); + break; + } + } + } + } + + for (i=0;i<16;i++) { + var mat = new Matrix(); + mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0); + tempMatrices.push(mat); + } +} + +init(); + +/** + * Implements Dihedral Group D_8, see [group D4]{@link http://mathworld.wolfram.com/DihedralGroupD4.html}, D8 is the same but with diagonals + * Used for texture rotations + * Vector xX(i), xY(i) is U-axis of sprite with rotation i + * Vector yY(i), yY(i) is V-axis of sprite with rotation i + * Rotations: 0 grad (0), 90 grad (2), 180 grad (4), 270 grad (6) + * Mirrors: vertical (8), main diagonal (10), horizontal (12), reverse diagonal (14) + * This is the small part of gameofbombs.com portal system. It works. + * @author Ivan @ivanpopelyshev + * + * @namespace PIXI.GroupD8 + */ +var GroupD8 = { + E: 0, + SE: 1, + S: 2, + SW: 3, + W: 4, + NW: 5, + N: 6, + NE: 7, + MIRROR_VERTICAL: 8, + MIRROR_HORIZONTAL: 12, + uX: function (ind) { + return ux[ind]; + }, + uY: function (ind) { + return uy[ind]; + }, + vX: function (ind) { + return vx[ind]; + }, + vY: function (ind) { + return vy[ind]; + }, + inv: function (rotation) { + if (rotation & 8) { + return rotation & 15; + } + return (-rotation) & 7; + }, + add: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][rotationFirst]; + }, + sub: function (rotationSecond, rotationFirst) { + return mul[rotationSecond][GroupD8.inv(rotationFirst)]; + }, + /** + * Adds 180 degrees to rotation. Commutative operation + * @param rotation + * @returns {number} + */ + rotate180: function (rotation) { + return rotation ^ 4; + }, + /** + * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * @param rotation + * @returns {boolean} + */ + isSwapWidthHeight: function(rotation) { + return (rotation & 3) === 2; + }, + byDirection: function (dx, dy) { + if (Math.abs(dx) * 2 <= Math.abs(dy)) { + if (dy >= 0) { + return GroupD8.S; + } + else { + return GroupD8.N; + } + } else if (Math.abs(dy) * 2 <= Math.abs(dx)) { + if (dx > 0) { + return GroupD8.E; + } + else { + return GroupD8.W; + } + } else { + if (dy > 0) { + if (dx > 0) { + return GroupD8.SE; + } + else { + return GroupD8.SW; + } + } + else if (dx > 0) { + return GroupD8.NE; + } + else { + return GroupD8.NW; + } + } + }, + /** + * Helps sprite to compensate texture packer rotation. + * @param matrix {PIXI.Matrix} sprite world matrix + * @param rotation {number} + * @param tx {number|*} sprite anchoring + * @param ty {number|*} sprite anchoring + */ + matrixAppendRotationInv: function (matrix, rotation, tx, ty) { + //Packer used "rotation", we use "inv(rotation)" + var mat = tempMatrices[GroupD8.inv(rotation)]; + tx = tx || 0; + ty = ty || 0; + mat.tx = tx; + mat.ty = ty; + matrix.append(mat); + } +}; + +module.exports = GroupD8; diff --git a/src/core/math/index.js b/src/core/math/index.js index fd0d9be..a43f6e7 100644 --- a/src/core/math/index.js +++ b/src/core/math/index.js @@ -11,6 +11,7 @@ Point: require('./Point'), Matrix: require('./Matrix'), + GroupD8: require('./GroupD8'), Circle: require('./shapes/Circle'), Ellipse: require('./shapes/Ellipse'), diff --git a/src/core/particles/webgl/ParticleRenderer.js b/src/core/particles/webgl/ParticleRenderer.js index 8481c92..0b0abd0 100644 --- a/src/core/particles/webgl/ParticleRenderer.js +++ b/src/core/particles/webgl/ParticleRenderer.js @@ -285,25 +285,25 @@ texture = sprite._texture; sx = sprite.scale.x; sy = sprite.scale.y; + var trim = texture.trim, crop = texture.crop; - if (texture.trim) + if (trim) { - // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. - trim = texture.trim; + // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space before transforming the sprite coords.. + w1 = trim.x - sprite.anchor.x * crop.width; + w0 = w1 + trim.width; - w1 = trim.x - sprite.anchor.x * trim.width; - w0 = w1 + texture.crop.width; + h1 = trim.y - sprite.anchor.y * crop.height; + h0 = h1 + trim.height; - h1 = trim.y - sprite.anchor.y * trim.height; - h0 = h1 + texture.crop.height; } else { - w0 = (texture._frame.width ) * (1-sprite.anchor.x); - w1 = (texture._frame.width ) * -sprite.anchor.x; + w0 = (crop.width ) * (1-sprite.anchor.x); + w1 = (crop.width ) * -sprite.anchor.x; - h0 = texture._frame.height * (1-sprite.anchor.y); - h1 = texture._frame.height * -sprite.anchor.y; + h0 = crop.height * (1-sprite.anchor.y); + h1 = crop.height * -sprite.anchor.y; } array[offset] = w1 * sx; diff --git a/src/core/renderers/canvas/utils/CanvasTinter.js b/src/core/renderers/canvas/utils/CanvasTinter.js index eedf175..8827558 100644 --- a/src/core/renderers/canvas/utils/CanvasTinter.js +++ b/src/core/renderers/canvas/utils/CanvasTinter.js @@ -68,11 +68,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; @@ -123,11 +119,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; @@ -165,11 +157,7 @@ var resolution = texture.baseTexture.resolution; - var crop = texture.crop.clone(); - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; + var crop = texture._frame; canvas.width = crop.width; canvas.height = crop.height; diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index b174736..970212f 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -111,12 +111,12 @@ width: { get: function () { - return Math.abs(this.scale.x) * this.texture._frame.width; + return Math.abs(this.scale.x) * this.texture.crop.width; }, set: function (value) { var sign = utils.sign(this.scale.x) || 1; - this.scale.x = sign * value / this.texture._frame.width; + this.scale.x = sign * value / this.texture.crop.width; this._width = value; } }, @@ -130,12 +130,12 @@ height: { get: function () { - return Math.abs(this.scale.y) * this.texture._frame.height; + return Math.abs(this.scale.y) * this.texture.crop.height; }, set: function (value) { var sign = utils.sign(this.scale.y) || 1; - this.scale.y = sign * value / this.texture._frame.height; + this.scale.y = sign * value / this.texture.crop.height; this._height = value; } }, @@ -191,12 +191,12 @@ // so if _width is 0 then width was not set.. if (this._width) { - this.scale.x = utils.sign(this.scale.x) * this._width / this.texture.frame.width; + this.scale.x = utils.sign(this.scale.x) * this._width / this.texture.crop.width; } if (this._height) { - this.scale.y = utils.sign(this.scale.y) * this._height / this.texture.frame.height; + this.scale.y = utils.sign(this.scale.y) * this._height / this.texture.crop.height; } }; @@ -207,25 +207,26 @@ a = wt.a, b = wt.b, c = wt.c, d = wt.d, tx = wt.tx, ty = wt.ty, vertexData = this.vertexData, w0, w1, h0, h1, - trim = texture.trim; - + trim = texture.trim, + crop = texture.crop; + if (trim) { // if the sprite is trimmed and is not a tilingsprite then we need to add the extra space before transforming the sprite coords.. - w1 = trim.x - this.anchor.x * trim.width; - w0 = w1 + texture.crop.width; + w1 = trim.x - this.anchor.x * crop.width; + w0 = w1 + trim.width; - h1 = trim.y - this.anchor.y * trim.height; - h0 = h1 + texture.crop.height; + h1 = trim.y - this.anchor.y * crop.height; + h0 = h1 + trim.height; } else { - w0 = (texture._frame.width ) * (1-this.anchor.x); - w1 = (texture._frame.width ) * -this.anchor.x; + w0 = (crop.width ) * (1-this.anchor.x); + w1 = (crop.width ) * -this.anchor.x; - h0 = texture._frame.height * (1-this.anchor.y); - h1 = texture._frame.height * -this.anchor.y; + h0 = crop.height * (1-this.anchor.y); + h1 = crop.height * -this.anchor.y; } // xy @@ -244,7 +245,6 @@ vertexData[6] = a * w1 + c * h0 + tx; vertexData[7] = d * h0 + b * w1 + ty; - } /** @@ -262,7 +262,7 @@ // set the vertex data this.caclulateVertices(); } - + renderer.setObjectRenderer(renderer.plugins.sprite); renderer.plugins.sprite.render(this); }; @@ -363,10 +363,10 @@ */ Sprite.prototype.getLocalBounds = function () { - this._bounds.x = -this._texture._frame.width * this.anchor.x; - this._bounds.y = -this._texture._frame.height * this.anchor.y; - this._bounds.width = this._texture._frame.width; - this._bounds.height = this._texture._frame.height; + this._bounds.x = -this._texture.crop.width * this.anchor.x; + this._bounds.y = -this._texture.crop.height * this.anchor.y; + this._bounds.width = this._texture.crop.width; + this._bounds.height = this._texture.crop.height; return this._bounds; }; @@ -380,8 +380,8 @@ { this.worldTransform.applyInverse(point, tempPoint); - var width = this._texture._frame.width; - var height = this._texture._frame.height; + var width = this._texture.crop.width; + var height = this._texture.crop.height; var x1 = -width * this.anchor.x; var y1; @@ -424,8 +424,8 @@ wt = this.worldTransform, dx, dy, - width, - height; + width = texture._frame.width, + height = texture._frame.height; renderer.context.globalAlpha = this.worldAlpha; @@ -436,45 +436,23 @@ renderer.context[renderer.smoothProperty] = smoothingEnabled; } - // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions - - if(texture.rotate) - { - width = texture.crop.height; - height = texture.crop.width; - - dx = (texture.trim) ? texture.trim.y - this.anchor.y * texture.trim.height : this.anchor.y * -texture._frame.height; - dy = (texture.trim) ? texture.trim.x - this.anchor.x * texture.trim.width : this.anchor.x * -texture._frame.width; - - dx += width; - - wt.tx = dy * wt.a + dx * wt.c + wt.tx; - wt.ty = dy * wt.b + dx * wt.d + wt.ty; - - var temp = wt.a; - wt.a = -wt.c; - wt.c = temp; - - temp = wt.b; - wt.b = -wt.d; - wt.d = temp; - + if (texture.trim) { + dx = texture.trim.width/2 + texture.trim.x - this.anchor.x * texture.crop.width; + dy = texture.trim.height/2 + texture.trim.y - this.anchor.y * texture.crop.height; + } else { + dx = (0.5 - this.anchor.x) * texture.crop.width; + dy = (0.5 - this.anchor.y) * texture.crop.height; + } + if(texture.rotate) { + wt.copy(canvasRenderWorldTransform); + wt = canvasRenderWorldTransform; + GroupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); // the anchor has already been applied above, so lets set it to zero dx = 0; dy = 0; - } - else - { - width = texture.crop.width; - height = texture.crop.height; - - dx = (texture.trim) ? texture.trim.x - this.anchor.x * texture.trim.width : this.anchor.x * -texture._frame.width; - dy = (texture.trim) ? texture.trim.y - this.anchor.y * texture.trim.height : this.anchor.y * -texture._frame.height; - } - - - + dx -= width/2; + dy -= height/2; // Allow for pixel rounding if (renderer.roundPixels) { @@ -492,7 +470,6 @@ } else { - renderer.context.setTransform( wt.a, wt.b, @@ -501,8 +478,6 @@ wt.tx * renderer.resolution, wt.ty * renderer.resolution ); - - } var resolution = texture.baseTexture.resolution; @@ -533,8 +508,8 @@ { renderer.context.drawImage( texture.baseTexture.source, - texture.crop.x * resolution, - texture.crop.y * resolution, + texture.frame.x * resolution, + texture.frame.y * resolution, width * resolution, height * resolution, dx * renderer.resolution, diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 61b6d12..36ac3ed 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -4,7 +4,8 @@ createIndicesForQuads = require('../../utils/createIndicesForQuads'), generateMultiTextureShader = require('./generateMultiTextureShader'), CONST = require('../../const'), - glCore = require('pixi-gl-core'); + glCore = require('pixi-gl-core'), + bitTwiddle = require('bit-twiddle'); /** * @author Mat Groves @@ -53,38 +54,44 @@ this.size = CONST.SPRITE_BATCH_SIZE; // 2000 is a nice balance between mobile / desktop // the total number of bytes in our batch - var numVerts = (this.size * 4) * this.vertByteSize; + var numVerts = this.size * 4 * this.vertByteSize; // the total number of indices in our batch, there are 6 points per quad. var numIndices = this.size * 6; + this.buffers = []; + for (var i = 1; i <= 2048; i*=2) { + var numVerts = i * 4 * this.vertByteSize; + this.buffers.push(new Buffer(numVerts, i)); + }; + /** * Holds the vertex data that will be sent to the vertex shader. * * @member {ArrayBuffer} */ - this.vertices = new ArrayBuffer(numVerts); + // this.vertices = new ArrayBuffer(numVerts); /** * View on the vertices as a Float32Array for positions * * @member {Float32Array} */ - this.positions = new Float32Array(this.vertices); + // this.positions = new Float32Array(this.vertices); /** * View on the vertices as a Uint32Array for uvs * * @member {Float32Array} */ - this.uvs = new Uint32Array(this.vertices); + //this.uvs = new Uint32Array(this.vertices); /** * View on the vertices as a Uint32Array for colors * * @member {Uint32Array} */ - this.colors = new Uint32Array(this.vertices); + //this.colors = new Uint32Array(this.vertices); /** * Holds the indices of the geometry (quads) to draw @@ -115,6 +122,8 @@ this.currentGroup = this.groups[this.groupCount++]; this.currentTexture = null; + + this.sprites = []; } @@ -215,45 +224,14 @@ this.textureCount++; } - // TODO trim?? - var index = this.currentIndex * this.vertByteSize; + // TODO add this variable to sprite.. + sprite._glBatchTextureId = nextTexture._id; + + this.sprites[this.currentIndex] = sprite; this.currentIndex++; - // upload the sprite elemetns... - // they have all ready been calculated so we just need to push them into the buffer. - var colors = this.colors; - var positions = this.positions; - var vertexData = sprite.vertexData - var tint = (sprite.tint >> 16) + (sprite.tint & 0xff00) + ((sprite.tint & 0xff) << 16) + (sprite.worldAlpha * 255 << 24); - var uvs = sprite.texture._uvs.uvs_uint32; - //xy - positions[index++] = vertexData[0]; - positions[index++] = vertexData[1]; - this.uvs[index++] = uvs[0]; - colors[index++] = tint; - positions[index++] = nextTexture._id; - // xy - positions[index++] = vertexData[2]; - positions[index++] = vertexData[3]; - this.uvs[index++] = uvs[1]; - colors[index++] = tint; - positions[index++] = nextTexture._id; - - // xy - positions[index++] = vertexData[4]; - positions[index++] = vertexData[5]; - this.uvs[index++] = uvs[2]; - colors[index++] = tint; - positions[index++] = nextTexture._id; - - // xy - positions[index++] = vertexData[6]; - positions[index++] = vertexData[7]; - this.uvs[index++] = uvs[3]; - colors[index++] = tint; - positions[index++] = nextTexture._id; }; /** @@ -266,6 +244,59 @@ var gl = this.renderer.gl; + var np2 = bitTwiddle.nextPow2(this.currentIndex); + var log2 = bitTwiddle.log2(np2); + + var buffer = this.buffers[log2]; + + var colors = buffer.colors; + var positions = buffer.positions; + var uvsBuffer = buffer.uvs; + var index = 0; + + //console.log(this.currentIndex); + //var array = + + for (var i = 0; i < this.currentIndex; i++) + { + // upload the sprite elemetns... + // they have all ready been calculated so we just need to push them into the buffer. + var sprite = this.sprites[i]; + + var vertexData = sprite.vertexData + var tint = (sprite.tint >> 16) + (sprite.tint & 0xff00) + ((sprite.tint & 0xff) << 16) + (sprite.worldAlpha * 255 << 24); + var uvs = sprite.texture._uvs.uvs_uint32; + var textureId = sprite._glBatchTextureId; + + //xy + positions[index++] = vertexData[0]; + positions[index++] = vertexData[1]; + uvsBuffer[index++] = uvs[0]; + colors[index++] = tint; + positions[index++] = textureId; + + // xy + positions[index++] = vertexData[2]; + positions[index++] = vertexData[3]; + uvsBuffer[index++] = uvs[1]; + colors[index++] = tint; + positions[index++] = textureId; + + // xy + positions[index++] = vertexData[4]; + positions[index++] = vertexData[5]; + uvsBuffer[index++] = uvs[2]; + colors[index++] = tint; + positions[index++] = textureId; + + // xy + positions[index++] = vertexData[6]; + positions[index++] = vertexData[7]; + uvsBuffer[index++] = uvs[3]; + colors[index++] = tint; + positions[index++] = textureId; + + }; this.currentGroup.size = this.currentIndex - this.currentGroup.start; for (var i = 0; i < this.currentGroup.textureCount; i++) @@ -276,19 +307,19 @@ // do some smart array stuff.. // double size so we dont alway subarray the elements.. // upload the verts to the buffer - if (this.currentBatchSize > ( this.size * 0.5 ) ) - { - this.vertexBuffer.upload(this.vertices, 0, true); - } - else - { + //if (this.currentBatchSize > ( this.size * 0.5 ) ) + //{ + // this.vertexBuffer.upload(this.vertices, 0, true); + // } + //else + //{ // o k .. sub array is SLOW>? - var view = this.positions.subarray(0, this.currentIndex * this.vertByteSize); - this.vertexBuffer.upload(view, 0, true); - } + // var view = this.positions.subarray(0, this.currentIndex * this.vertByteSize); + this.vertexBuffer.upload(buffer.vertices, 0, true); + //} // bind shader.. - this.renderer.bindShader(this.shader); + this.renderer.blendModeManager.setBlendMode( 0 ); /// render the groups.. @@ -319,6 +350,7 @@ */ SpriteRenderer.prototype.start = function () { + this.renderer.bindShader(this.shader); this.vao.bind(); }; @@ -353,3 +385,33 @@ this.sprites = null; this.shader = null; }; + +var Buffer = function(size, realSize) +{ + + this.realSize = realSize; + this.vertices = new ArrayBuffer(size); + + /** + * View on the vertices as a Float32Array for positions + * + * @member {Float32Array} + */ + this.positions = new Float32Array(this.vertices); + + /** + * View on the vertices as a Uint32Array for uvs + * + * @member {Float32Array} + */ + this.uvs = new Uint32Array(this.vertices); + + /** + * View on the vertices as a Uint32Array for colors + * + * @member {Uint32Array} + */ + this.colors = new Uint32Array(this.vertices); + + //this.buffer = +} \ No newline at end of file diff --git a/src/core/sprites/webgl/generateMultiTextureShader.js b/src/core/sprites/webgl/generateMultiTextureShader.js index c864d37..4134111 100644 --- a/src/core/sprites/webgl/generateMultiTextureShader.js +++ b/src/core/sprites/webgl/generateMultiTextureShader.js @@ -4,13 +4,13 @@ function generateMultiTextureShader(gl, maxTextures) { var vertexSrc = fs.readFileSync(__dirname + '/texture.vert', 'utf8'); - var fragmentSrc = 'precision lowp float;\n\n' + generateSampleSrc(maxTextures) + "\n\n" + fragTemplate + var fragmentSrc = fragTemplate - fragmentSrc = fragmentSrc.replace(/\%\%/gi, '16'); + fragmentSrc = fragmentSrc.replace(/\%\%/gi, maxTextures); + fragmentSrc = fragmentSrc.replace(/\%forloop\%/gi, generateSampleSrc(maxTextures)); var shader = new Shader(gl, vertexSrc, fragmentSrc); - var sampleValues = []; for (var i = 0; i < maxTextures; i++) { sampleValues[i] = i; @@ -24,7 +24,7 @@ function generateSampleSrc(maxTextures) { - var src = 'vec4 getSampleFromArray(sampler2D textures[%%], int ndx, vec2 uv) {\n\nvec4 color;' + var src = '' src += '\n'; src += '\n'; @@ -34,27 +34,29 @@ if(i > 0)src += '\nelse '; if(i < maxTextures-1)src += 'if(ndx == ' + i + ')'; src += '\n{'; - src += '\n\tcolor = texture2D(textures['+i+'], uv);'; + src += '\n\tcolor = texture2D(uSamplers['+i+'], vTextureCoord);'; src += '\n}'; }; src += '\n'; src += '\n'; - src += 'return color;'; - src += '\n}'; return src; } var fragTemplate = [ + 'precision lowp float;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', 'varying float vTextureId;', 'uniform sampler2D uSamplers[%%];', 'void main(void){', - 'gl_FragColor = getSampleFromArray(uSamplers, int(vTextureId), vTextureCoord) * vColor;', + 'vec4 color;', + 'int ndx = int(vTextureId);', + '%forloop%', + 'gl_FragColor = color * vColor;', '}' ].join('\n'); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index e43e8b5..00aaa6f 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -22,8 +22,8 @@ * @param baseTexture {PIXI.BaseTexture} The base texture source to create the texture from * @param [frame] {PIXI.Rectangle} The rectangle frame of the texture to show * @param [crop] {PIXI.Rectangle} The area of original texture - * @param [trim] {PIXI.Rectangle} Trimmed texture rectangle - * @param [rotate] {boolean} indicates whether the texture should be rotated by 90 degrees ( used by texture packer ) + * @param [trim] {PIXI.Rectangle} Trimmed rectangle of original texture + * @param [rotate] {number} indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} */ function Texture(baseTexture, frame, crop, trim, rotate) { @@ -55,15 +55,15 @@ this.baseTexture = baseTexture; /** - * The frame specifies the region of the base texture that this texture uses + * This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering, + * irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases) * * @member {PIXI.Rectangle} - * @private */ this._frame = frame; /** - * The texture trim data. + * This is the trimmed area of original texture, before it was put in atlas * * @member {PIXI.Rectangle} */ @@ -106,20 +106,22 @@ this.height = 0; /** - * This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering, - * irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases) + * This is the area of original texture, before it was put in atlas * * @member {PIXI.Rectangle} */ this.crop = crop || frame;//new math.Rectangle(0, 0, 1, 1); - /** - * Indicates whether the texture should be rotated by 90 degrees - * - * @private - * @member {boolean} - */ - this.rotate = !!rotate; + this._rotate = +(rotate || 0); + + if (rotate === true) { + // this is old texturepacker legacy, some games/libraries are passing "true" for rotated textures + this._rotate = 2; + } else { + if (this._rotate % 2 !== 0) { + throw 'attempt to use diamond-shaped UVs. If you are sure, set rotation manually'; + } + } if (baseTexture.hasLoaded) { @@ -168,10 +170,7 @@ this.noFrame = false; - this.width = frame.width; - this.height = frame.height; - - if (!this.trim && !this.rotate && (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height)) + if (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height) { throw new Error('Texture Error: frame does not fit inside the base Texture dimensions ' + this); } @@ -179,15 +178,9 @@ //this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded; this.valid = frame && frame.width && frame.height && this.baseTexture.hasLoaded; - if (this.trim) - { - this.width = this.trim.width; - this.height = this.trim.height; - this._frame.width = this.trim.width; - this._frame.height = this.trim.height; - } - else - { + if (!this.trim) { + this.width = frame.width; + this.height = frame.height; this.crop = frame; } @@ -196,6 +189,29 @@ this._updateUvs(); } } + }, + /** + * Indicates whether the texture is rotated inside the atlas + * set to 2 to compensate for texture packer rotation + * set to 6 to compensate for spine packer rotation + * can be used to rotate or mirror sprites + * See {@link PIXI.GroupD8} for explanation + * + * @member {number} + */ + rotate: { + get: function () + { + return this._rotate; + }, + set: function (rotate) + { + this._rotate = rotate; + if (this.valid) + { + this._updateUvs(); + } + } } }); @@ -294,7 +310,7 @@ this._uvs = new TextureUvs(); } - this._uvs.set(this.crop, this.baseTexture, this.rotate); + this._uvs.set(this._frame, this.baseTexture, this.rotate); }; /** diff --git a/src/core/textures/TextureUvs.js b/src/core/textures/TextureUvs.js index c5e4564..11bed5d 100644 --- a/src/core/textures/TextureUvs.js +++ b/src/core/textures/TextureUvs.js @@ -30,11 +30,13 @@ module.exports = TextureUvs; +var GroupD8 = require('../math/GroupD8'); + /** * Sets the texture Uvs based on the given frame information * @param frame {PIXI.Rectangle} * @param baseFrame {PIXI.Rectangle} - * @param rotate {boolean} Whether or not the frame is rotated + * @param rotate {number} Rotation of frame, see {@link PIXI.GroupD8} * @private */ TextureUvs.prototype.set = function (frame, baseFrame, rotate) @@ -44,17 +46,24 @@ if(rotate) { - this.x0 = (frame.x + frame.height) / tw; - this.y0 = frame.y / th; - - this.x1 = (frame.x + frame.height) / tw; - this.y1 = (frame.y + frame.width) / th; - - this.x2 = frame.x / tw; - this.y2 = (frame.y + frame.width) / th; - - this.x3 = frame.x / tw; - this.y3 = frame.y / th; + //width and height div 2 div baseFrame size + var w2 = frame.width / 2 / tw; + var h2 = frame.height / 2 / th; + //coordinates of center + var cX = frame.x / tw + w2; + var cY = frame.y / th + h2; + rotate = GroupD8.add(rotate, GroupD8.NW); //NW is top-left corner + this.x0 = cX + w2 * GroupD8.uX(rotate); + this.y0 = cY + h2 * GroupD8.uY(rotate); + rotate = GroupD8.add(rotate, 2); //rotate 90 degrees clockwise + this.x1 = cX + w2 * GroupD8.uX(rotate); + this.y1 = cY + h2 * GroupD8.uY(rotate); + rotate = GroupD8.add(rotate, 2); + this.x2 = cX + w2 * GroupD8.uX(rotate); + this.y2 = cY + h2 * GroupD8.uY(rotate); + rotate = GroupD8.add(rotate, 2); + this.x3 = cX + w2 * GroupD8.uX(rotate); + this.y3 = cY + h2 * GroupD8.uY(rotate); } else { diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js index b318d6f..fbd1ea5 100644 --- a/src/loaders/spritesheetParser.js +++ b/src/loaders/spritesheetParser.js @@ -35,14 +35,15 @@ if (rect) { - var size = null; + var frame = null; var trim = null; + var crop = new core.Rectangle(0, 0, frames[i].sourceSize.w / resolution, frames[i].sourceSize.h / resolution); if (frames[i].rotated) { - size = new core.Rectangle(rect.x, rect.y, rect.h, rect.w); + frame = new core.Rectangle(rect.x / resolution, rect.y / resolution, rect.h / resolution, rect.w / resolution); } else { - size = new core.Rectangle(rect.x, rect.y, rect.w, rect.h); + frame = new core.Rectangle(rect.x / resolution, rect.y / resolution, rect.w / resolution, rect.h / resolution); } // Check to see if the sprite is trimmed @@ -51,25 +52,12 @@ trim = new core.Rectangle( frames[i].spriteSourceSize.x / resolution, frames[i].spriteSourceSize.y / resolution, - frames[i].sourceSize.w / resolution, - frames[i].sourceSize.h / resolution + frames[i].spriteSourceSize.w / resolution, + frames[i].spriteSourceSize.h / resolution ); } - // flip the width and height! - if (frames[i].rotated) - { - var temp = size.width; - size.width = size.height; - size.height = temp; - } - - size.x /= resolution; - size.y /= resolution; - size.width /= resolution; - size.height /= resolution; - - resource.textures[i] = new core.Texture(res.texture.baseTexture, size, size.clone(), trim, frames[i].rotated); + resource.textures[i] = new core.Texture(res.texture.baseTexture, frame, crop, trim, frames[i].rotated ? 2 : 0); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions core.utils.TextureCache[i] = resource.textures[i];