diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 455d59c..0ce75d2 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -18,6 +18,14 @@ * let sprite2 = new PIXI.Sprite(texture); * ``` * + * Textures made from SVGs, loaded or not, cannot be used before the file finishes processing. You can check for this by checking the sprite's _textureID property. + * ```js + * var texture = PIXI.Texture.fromImage('assets/image.svg'); + * var sprite1 = new PIXI.Sprite(texture); + * //sprite1._textureID should not be undefined if the texture has finished processing the SVG file + * ``` + * You can use a ticker or rAF to ensure your sprites load the finished textures after processing. See issue #3068. + * * @class * @extends EventEmitter * @memberof PIXI diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 455d59c..0ce75d2 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -18,6 +18,14 @@ * let sprite2 = new PIXI.Sprite(texture); * ``` * + * Textures made from SVGs, loaded or not, cannot be used before the file finishes processing. You can check for this by checking the sprite's _textureID property. + * ```js + * var texture = PIXI.Texture.fromImage('assets/image.svg'); + * var sprite1 = new PIXI.Sprite(texture); + * //sprite1._textureID should not be undefined if the texture has finished processing the SVG file + * ``` + * You can use a ticker or rAF to ensure your sprites load the finished textures after processing. See issue #3068. + * * @class * @extends EventEmitter * @memberof PIXI diff --git a/src/extras/getGlobalPosition.js b/src/extras/getGlobalPosition.js index b71682f..a23761f 100644 --- a/src/extras/getGlobalPosition.js +++ b/src/extras/getGlobalPosition.js @@ -1,20 +1,20 @@ import * as core from '../core'; /** - * Returns the global position of the displayObject + * Returns the global position of the displayObject. Does not depend on object scale, rotation and pivot. * * @memberof PIXI.DisplayObject# * @param {Point} point - the point to write the global value to. If null a new point will be returned + * @param {boolean} skipUpdate - setting to true will stop the transforms of the scene graph from + * being updated. This means the calculation returned MAY be out of date BUT will give you a + * nice performance boost * @return {Point} The updated point */ -core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point()) +core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point(), skipUpdate = false) { if (this.parent) { - this.displayObjectUpdateTransform(); - - point.x = this.worldTransform.tx; - point.y = this.worldTransform.ty; + this.parent.toGlobal(this.position, point, skipUpdate); } else { diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 455d59c..0ce75d2 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -18,6 +18,14 @@ * let sprite2 = new PIXI.Sprite(texture); * ``` * + * Textures made from SVGs, loaded or not, cannot be used before the file finishes processing. You can check for this by checking the sprite's _textureID property. + * ```js + * var texture = PIXI.Texture.fromImage('assets/image.svg'); + * var sprite1 = new PIXI.Sprite(texture); + * //sprite1._textureID should not be undefined if the texture has finished processing the SVG file + * ``` + * You can use a ticker or rAF to ensure your sprites load the finished textures after processing. See issue #3068. + * * @class * @extends EventEmitter * @memberof PIXI diff --git a/src/extras/getGlobalPosition.js b/src/extras/getGlobalPosition.js index b71682f..a23761f 100644 --- a/src/extras/getGlobalPosition.js +++ b/src/extras/getGlobalPosition.js @@ -1,20 +1,20 @@ import * as core from '../core'; /** - * Returns the global position of the displayObject + * Returns the global position of the displayObject. Does not depend on object scale, rotation and pivot. * * @memberof PIXI.DisplayObject# * @param {Point} point - the point to write the global value to. If null a new point will be returned + * @param {boolean} skipUpdate - setting to true will stop the transforms of the scene graph from + * being updated. This means the calculation returned MAY be out of date BUT will give you a + * nice performance boost * @return {Point} The updated point */ -core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point()) +core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point(), skipUpdate = false) { if (this.parent) { - this.displayObjectUpdateTransform(); - - point.x = this.worldTransform.tx; - point.y = this.worldTransform.ty; + this.parent.toGlobal(this.position, point, skipUpdate); } else { diff --git a/src/filters/blur/BlurFilter.js b/src/filters/blur/BlurFilter.js index 4919d10..5b3c3a9 100644 --- a/src/filters/blur/BlurFilter.js +++ b/src/filters/blur/BlurFilter.js @@ -68,7 +68,7 @@ set blur(value) { this.blurXFilter.blur = this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -113,7 +113,7 @@ set blurX(value) { this.blurXFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -136,6 +136,6 @@ set blurY(value) { this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } } diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 455d59c..0ce75d2 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -18,6 +18,14 @@ * let sprite2 = new PIXI.Sprite(texture); * ``` * + * Textures made from SVGs, loaded or not, cannot be used before the file finishes processing. You can check for this by checking the sprite's _textureID property. + * ```js + * var texture = PIXI.Texture.fromImage('assets/image.svg'); + * var sprite1 = new PIXI.Sprite(texture); + * //sprite1._textureID should not be undefined if the texture has finished processing the SVG file + * ``` + * You can use a ticker or rAF to ensure your sprites load the finished textures after processing. See issue #3068. + * * @class * @extends EventEmitter * @memberof PIXI diff --git a/src/extras/getGlobalPosition.js b/src/extras/getGlobalPosition.js index b71682f..a23761f 100644 --- a/src/extras/getGlobalPosition.js +++ b/src/extras/getGlobalPosition.js @@ -1,20 +1,20 @@ import * as core from '../core'; /** - * Returns the global position of the displayObject + * Returns the global position of the displayObject. Does not depend on object scale, rotation and pivot. * * @memberof PIXI.DisplayObject# * @param {Point} point - the point to write the global value to. If null a new point will be returned + * @param {boolean} skipUpdate - setting to true will stop the transforms of the scene graph from + * being updated. This means the calculation returned MAY be out of date BUT will give you a + * nice performance boost * @return {Point} The updated point */ -core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point()) +core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point(), skipUpdate = false) { if (this.parent) { - this.displayObjectUpdateTransform(); - - point.x = this.worldTransform.tx; - point.y = this.worldTransform.ty; + this.parent.toGlobal(this.position, point, skipUpdate); } else { diff --git a/src/filters/blur/BlurFilter.js b/src/filters/blur/BlurFilter.js index 4919d10..5b3c3a9 100644 --- a/src/filters/blur/BlurFilter.js +++ b/src/filters/blur/BlurFilter.js @@ -68,7 +68,7 @@ set blur(value) { this.blurXFilter.blur = this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -113,7 +113,7 @@ set blurX(value) { this.blurXFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -136,6 +136,6 @@ set blurY(value) { this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } } diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 4719b6a..2ced6dc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,6 +1,4 @@ import * as core from '../core'; -import glCore from 'pixi-gl-core'; -import Shader from './webgl/MeshShader'; const tempPoint = new core.Point(); const tempPolygon = new core.Polygon(); @@ -59,11 +57,17 @@ this.indices = indices || new Uint16Array([0, 1, 3, 2]); /** - * Whether the Mesh is dirty or not + * Version of mesh uvs are dirty or not * * @member {number} */ this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ this.indexDirty = 0; /** @@ -122,62 +126,8 @@ */ _renderWebGL(renderer) { - // get rid of any thing that may be batching. - renderer.flush(); - - // renderer.plugins.mesh.render(this); - const gl = renderer.gl; - let glData = this._glDatas[renderer.CONTEXT_UID]; - - if (!glData) - { - glData = { - shader: new Shader(gl), - vertexBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.vertices, gl.STREAM_DRAW), - uvBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.uvs, gl.STREAM_DRAW), - indexBuffer: glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW), - // build the vao object that will render.. - vao: new glCore.VertexArrayObject(gl), - dirty: this.dirty, - indexDirty: this.indexDirty, - }; - - // build the vao object that will render.. - glData.vao = new glCore.VertexArrayObject(gl) - .addIndex(glData.indexBuffer) - .addAttribute(glData.vertexBuffer, glData.shader.attributes.aVertexPosition, gl.FLOAT, false, 2 * 4, 0) - .addAttribute(glData.uvBuffer, glData.shader.attributes.aTextureCoord, gl.FLOAT, false, 2 * 4, 0); - - this._glDatas[renderer.CONTEXT_UID] = glData; - } - - if (this.dirty !== glData.dirty) - { - glData.dirty = this.dirty; - glData.uvBuffer.upload(); - } - - if (this.indexDirty !== glData.indexDirty) - { - glData.indexDirty = this.indexDirty; - glData.indexBuffer.upload(); - } - - glData.vertexBuffer.upload(); - - renderer.bindShader(glData.shader); - renderer.bindTexture(this._texture, 0); - renderer.state.setBlendMode(this.blendMode); - - glData.shader.uniforms.translationMatrix = this.worldTransform.toArray(true); - glData.shader.uniforms.alpha = this.worldAlpha; - glData.shader.uniforms.tint = this.tintRgb; - - const drawMode = this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; - - glData.vao.bind() - .draw(drawMode, this.indices.length) - .unbind(); + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); } /** @@ -188,240 +138,7 @@ */ _renderCanvas(renderer) { - const context = renderer.context; - - const transform = this.worldTransform; - const res = renderer.resolution; - - if (renderer.roundPixels) - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - (transform.tx * res) | 0, - (transform.ty * res) | 0 - ); - } - else - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - transform.tx * res, - transform.ty * res - ); - } - - if (this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) - { - this._renderCanvasTriangleMesh(context); - } - else - { - this._renderCanvasTriangles(context); - } - } - - /** - * Draws the object in Triangle Mesh mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - The current drawing context - */ - _renderCanvasTriangleMesh(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const length = vertices.length / 2; - - // this.count++; - - for (let i = 0; i < length - 2; i++) - { - // draw some triangles! - const index = i * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index, (index + 2), (index + 4)); - } - } - - /** - * Draws the object in triangle mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - */ - _renderCanvasTriangles(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const indices = this.indices; - const length = indices.length; - // this.count++; - - for (let i = 0; i < length; i += 3) - { - // draw some triangles! - const index0 = indices[i] * 2; - const index1 = indices[i + 1] * 2; - const index2 = indices[i + 2] * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2); - } - } - - /** - * Draws one of the triangles that form this Mesh - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - * @param {Float32Array} vertices - a reference to the vertices of the Mesh - * @param {Float32Array} uvs - a reference to the uvs of the Mesh - * @param {number} index0 - the index of the first vertex - * @param {number} index1 - the index of the second vertex - * @param {number} index2 - the index of the third vertex - */ - _renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2) - { - const base = this._texture.baseTexture; - const textureSource = base.source; - const textureWidth = base.width; - const textureHeight = base.height; - - const u0 = uvs[index0] * base.width; - const u1 = uvs[index1] * base.width; - const u2 = uvs[index2] * base.width; - const v0 = uvs[index0 + 1] * base.height; - const v1 = uvs[index1 + 1] * base.height; - const v2 = uvs[index2 + 1] * base.height; - - let x0 = vertices[index0]; - let x1 = vertices[index1]; - let x2 = vertices[index2]; - let y0 = vertices[index0 + 1]; - let y1 = vertices[index1 + 1]; - let y2 = vertices[index2 + 1]; - - if (this.canvasPadding > 0) - { - const paddingX = this.canvasPadding / this.worldTransform.a; - const paddingY = this.canvasPadding / this.worldTransform.d; - const centerX = (x0 + x1 + x2) / 3; - const centerY = (y0 + y1 + y2) / 3; - - let normX = x0 - centerX; - let normY = y0 - centerY; - - let dist = Math.sqrt((normX * normX) + (normY * normY)); - - x0 = centerX + ((normX / dist) * (dist + paddingX)); - y0 = centerY + ((normY / dist) * (dist + paddingY)); - - // - - normX = x1 - centerX; - normY = y1 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x1 = centerX + ((normX / dist) * (dist + paddingX)); - y1 = centerY + ((normY / dist) * (dist + paddingY)); - - normX = x2 - centerX; - normY = y2 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x2 = centerX + ((normX / dist) * (dist + paddingX)); - y2 = centerY + ((normY / dist) * (dist + paddingY)); - } - - context.save(); - context.beginPath(); - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - - context.closePath(); - - context.clip(); - - // Compute matrix transform - const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); - const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); - const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); - const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); - const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); - const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); - const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); - - context.transform( - deltaA / delta, - deltaD / delta, - deltaB / delta, - deltaE / delta, - deltaC / delta, - deltaF / delta - ); - - context.drawImage( - textureSource, - 0, - 0, - textureWidth * base.resolution, - textureHeight * base.resolution, - 0, - 0, - textureWidth, - textureHeight - ); - - context.restore(); - } - - /** - * Renders a flat Mesh - * - * @private - * @param {PIXI.mesh.Mesh} mesh - The Mesh to render - */ - renderMeshFlat(mesh) - { - const context = this.context; - const vertices = mesh.vertices; - const length = vertices.length / 2; - - // this.count++; - - context.beginPath(); - - for (let i = 1; i < length - 2; ++i) - { - // draw some triangles! - const index = i * 2; - - const x0 = vertices[index]; - const y0 = vertices[index + 1]; - - const x1 = vertices[index + 2]; - const y1 = vertices[index + 3]; - - const x2 = vertices[index + 4]; - const y2 = vertices[index + 5]; - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - } - - context.fillStyle = '#FF0000'; - context.fill(); - context.closePath(); + renderer.plugins.mesh.render(this); } /** diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 455d59c..0ce75d2 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -18,6 +18,14 @@ * let sprite2 = new PIXI.Sprite(texture); * ``` * + * Textures made from SVGs, loaded or not, cannot be used before the file finishes processing. You can check for this by checking the sprite's _textureID property. + * ```js + * var texture = PIXI.Texture.fromImage('assets/image.svg'); + * var sprite1 = new PIXI.Sprite(texture); + * //sprite1._textureID should not be undefined if the texture has finished processing the SVG file + * ``` + * You can use a ticker or rAF to ensure your sprites load the finished textures after processing. See issue #3068. + * * @class * @extends EventEmitter * @memberof PIXI diff --git a/src/extras/getGlobalPosition.js b/src/extras/getGlobalPosition.js index b71682f..a23761f 100644 --- a/src/extras/getGlobalPosition.js +++ b/src/extras/getGlobalPosition.js @@ -1,20 +1,20 @@ import * as core from '../core'; /** - * Returns the global position of the displayObject + * Returns the global position of the displayObject. Does not depend on object scale, rotation and pivot. * * @memberof PIXI.DisplayObject# * @param {Point} point - the point to write the global value to. If null a new point will be returned + * @param {boolean} skipUpdate - setting to true will stop the transforms of the scene graph from + * being updated. This means the calculation returned MAY be out of date BUT will give you a + * nice performance boost * @return {Point} The updated point */ -core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point()) +core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point(), skipUpdate = false) { if (this.parent) { - this.displayObjectUpdateTransform(); - - point.x = this.worldTransform.tx; - point.y = this.worldTransform.ty; + this.parent.toGlobal(this.position, point, skipUpdate); } else { diff --git a/src/filters/blur/BlurFilter.js b/src/filters/blur/BlurFilter.js index 4919d10..5b3c3a9 100644 --- a/src/filters/blur/BlurFilter.js +++ b/src/filters/blur/BlurFilter.js @@ -68,7 +68,7 @@ set blur(value) { this.blurXFilter.blur = this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -113,7 +113,7 @@ set blurX(value) { this.blurXFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -136,6 +136,6 @@ set blurY(value) { this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } } diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 4719b6a..2ced6dc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,6 +1,4 @@ import * as core from '../core'; -import glCore from 'pixi-gl-core'; -import Shader from './webgl/MeshShader'; const tempPoint = new core.Point(); const tempPolygon = new core.Polygon(); @@ -59,11 +57,17 @@ this.indices = indices || new Uint16Array([0, 1, 3, 2]); /** - * Whether the Mesh is dirty or not + * Version of mesh uvs are dirty or not * * @member {number} */ this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ this.indexDirty = 0; /** @@ -122,62 +126,8 @@ */ _renderWebGL(renderer) { - // get rid of any thing that may be batching. - renderer.flush(); - - // renderer.plugins.mesh.render(this); - const gl = renderer.gl; - let glData = this._glDatas[renderer.CONTEXT_UID]; - - if (!glData) - { - glData = { - shader: new Shader(gl), - vertexBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.vertices, gl.STREAM_DRAW), - uvBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.uvs, gl.STREAM_DRAW), - indexBuffer: glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW), - // build the vao object that will render.. - vao: new glCore.VertexArrayObject(gl), - dirty: this.dirty, - indexDirty: this.indexDirty, - }; - - // build the vao object that will render.. - glData.vao = new glCore.VertexArrayObject(gl) - .addIndex(glData.indexBuffer) - .addAttribute(glData.vertexBuffer, glData.shader.attributes.aVertexPosition, gl.FLOAT, false, 2 * 4, 0) - .addAttribute(glData.uvBuffer, glData.shader.attributes.aTextureCoord, gl.FLOAT, false, 2 * 4, 0); - - this._glDatas[renderer.CONTEXT_UID] = glData; - } - - if (this.dirty !== glData.dirty) - { - glData.dirty = this.dirty; - glData.uvBuffer.upload(); - } - - if (this.indexDirty !== glData.indexDirty) - { - glData.indexDirty = this.indexDirty; - glData.indexBuffer.upload(); - } - - glData.vertexBuffer.upload(); - - renderer.bindShader(glData.shader); - renderer.bindTexture(this._texture, 0); - renderer.state.setBlendMode(this.blendMode); - - glData.shader.uniforms.translationMatrix = this.worldTransform.toArray(true); - glData.shader.uniforms.alpha = this.worldAlpha; - glData.shader.uniforms.tint = this.tintRgb; - - const drawMode = this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; - - glData.vao.bind() - .draw(drawMode, this.indices.length) - .unbind(); + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); } /** @@ -188,240 +138,7 @@ */ _renderCanvas(renderer) { - const context = renderer.context; - - const transform = this.worldTransform; - const res = renderer.resolution; - - if (renderer.roundPixels) - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - (transform.tx * res) | 0, - (transform.ty * res) | 0 - ); - } - else - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - transform.tx * res, - transform.ty * res - ); - } - - if (this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) - { - this._renderCanvasTriangleMesh(context); - } - else - { - this._renderCanvasTriangles(context); - } - } - - /** - * Draws the object in Triangle Mesh mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - The current drawing context - */ - _renderCanvasTriangleMesh(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const length = vertices.length / 2; - - // this.count++; - - for (let i = 0; i < length - 2; i++) - { - // draw some triangles! - const index = i * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index, (index + 2), (index + 4)); - } - } - - /** - * Draws the object in triangle mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - */ - _renderCanvasTriangles(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const indices = this.indices; - const length = indices.length; - // this.count++; - - for (let i = 0; i < length; i += 3) - { - // draw some triangles! - const index0 = indices[i] * 2; - const index1 = indices[i + 1] * 2; - const index2 = indices[i + 2] * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2); - } - } - - /** - * Draws one of the triangles that form this Mesh - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - * @param {Float32Array} vertices - a reference to the vertices of the Mesh - * @param {Float32Array} uvs - a reference to the uvs of the Mesh - * @param {number} index0 - the index of the first vertex - * @param {number} index1 - the index of the second vertex - * @param {number} index2 - the index of the third vertex - */ - _renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2) - { - const base = this._texture.baseTexture; - const textureSource = base.source; - const textureWidth = base.width; - const textureHeight = base.height; - - const u0 = uvs[index0] * base.width; - const u1 = uvs[index1] * base.width; - const u2 = uvs[index2] * base.width; - const v0 = uvs[index0 + 1] * base.height; - const v1 = uvs[index1 + 1] * base.height; - const v2 = uvs[index2 + 1] * base.height; - - let x0 = vertices[index0]; - let x1 = vertices[index1]; - let x2 = vertices[index2]; - let y0 = vertices[index0 + 1]; - let y1 = vertices[index1 + 1]; - let y2 = vertices[index2 + 1]; - - if (this.canvasPadding > 0) - { - const paddingX = this.canvasPadding / this.worldTransform.a; - const paddingY = this.canvasPadding / this.worldTransform.d; - const centerX = (x0 + x1 + x2) / 3; - const centerY = (y0 + y1 + y2) / 3; - - let normX = x0 - centerX; - let normY = y0 - centerY; - - let dist = Math.sqrt((normX * normX) + (normY * normY)); - - x0 = centerX + ((normX / dist) * (dist + paddingX)); - y0 = centerY + ((normY / dist) * (dist + paddingY)); - - // - - normX = x1 - centerX; - normY = y1 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x1 = centerX + ((normX / dist) * (dist + paddingX)); - y1 = centerY + ((normY / dist) * (dist + paddingY)); - - normX = x2 - centerX; - normY = y2 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x2 = centerX + ((normX / dist) * (dist + paddingX)); - y2 = centerY + ((normY / dist) * (dist + paddingY)); - } - - context.save(); - context.beginPath(); - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - - context.closePath(); - - context.clip(); - - // Compute matrix transform - const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); - const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); - const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); - const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); - const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); - const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); - const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); - - context.transform( - deltaA / delta, - deltaD / delta, - deltaB / delta, - deltaE / delta, - deltaC / delta, - deltaF / delta - ); - - context.drawImage( - textureSource, - 0, - 0, - textureWidth * base.resolution, - textureHeight * base.resolution, - 0, - 0, - textureWidth, - textureHeight - ); - - context.restore(); - } - - /** - * Renders a flat Mesh - * - * @private - * @param {PIXI.mesh.Mesh} mesh - The Mesh to render - */ - renderMeshFlat(mesh) - { - const context = this.context; - const vertices = mesh.vertices; - const length = vertices.length / 2; - - // this.count++; - - context.beginPath(); - - for (let i = 1; i < length - 2; ++i) - { - // draw some triangles! - const index = i * 2; - - const x0 = vertices[index]; - const y0 = vertices[index + 1]; - - const x1 = vertices[index + 2]; - const y1 = vertices[index + 3]; - - const x2 = vertices[index + 4]; - const y2 = vertices[index + 5]; - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - } - - context.fillStyle = '#FF0000'; - context.fill(); - context.closePath(); + renderer.plugins.mesh.render(this); } /** diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js new file mode 100644 index 0000000..a1ad74c --- /dev/null +++ b/src/mesh/canvas/CanvasMeshRenderer.js @@ -0,0 +1,276 @@ +import * as core from '../../core'; +import { default as Mesh } from '../Mesh'; + +/** + * Renderer dedicated to meshes. + * + * @class + * @private + * @memberof PIXI + */ +export default class MeshSpriteRenderer +{ + /** + * @param {PIXI.CanvasRenderer} renderer - The renderer this downport works for + */ + constructor(renderer) + { + this.renderer = renderer; + } + + /** + * Renders the Mesh + * + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + render(mesh) + { + const renderer = this.renderer; + const context = renderer.context; + + const transform = mesh.worldTransform; + const res = renderer.resolution; + + if (renderer.roundPixels) + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + (transform.tx * res) | 0, + (transform.ty * res) | 0 + ); + } + else + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + transform.tx * res, + transform.ty * res + ); + } + + if (mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) + { + this._renderTriangleMesh(mesh); + } + else + { + this._renderTriangles(mesh); + } + } + + /** + * Draws the object in Triangle Mesh mode + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + _renderTriangleMesh(mesh) + { + // draw triangles!! + const length = mesh.vertices.length / 2; + + for (let i = 0; i < length - 2; i++) + { + // draw some triangles! + const index = i * 2; + + this._renderDrawTriangle(mesh, index, (index + 2), (index + 4)); + } + } + + /** + * Draws the object in triangle mode using canvas + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + */ + _renderTriangles(mesh) + { + // draw triangles!! + const indices = mesh.indices; + const length = indices.length; + + for (let i = 0; i < length; i += 3) + { + // draw some triangles! + const index0 = indices[i] * 2; + const index1 = indices[i + 1] * 2; + const index2 = indices[i + 2] * 2; + + this._renderDrawTriangle(mesh, index0, index1, index2); + } + } + + /** + * Draws one of the triangles that from the Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + * @param {number} index0 - the index of the first vertex + * @param {number} index1 - the index of the second vertex + * @param {number} index2 - the index of the third vertex + */ + _renderDrawTriangle(mesh, index0, index1, index2) + { + const context = this.renderer.context; + const uvs = mesh.uvs; + const vertices = mesh.vertices; + const texture = mesh._texture; + + if (!texture.valid) + { + return; + } + + const base = texture.baseTexture; + const textureSource = base.source; + const textureWidth = base.width; + const textureHeight = base.height; + + const u0 = uvs[index0] * base.width; + const u1 = uvs[index1] * base.width; + const u2 = uvs[index2] * base.width; + const v0 = uvs[index0 + 1] * base.height; + const v1 = uvs[index1 + 1] * base.height; + const v2 = uvs[index2 + 1] * base.height; + + let x0 = vertices[index0]; + let x1 = vertices[index1]; + let x2 = vertices[index2]; + let y0 = vertices[index0 + 1]; + let y1 = vertices[index1 + 1]; + let y2 = vertices[index2 + 1]; + + if (mesh.canvasPadding > 0) + { + const paddingX = mesh.canvasPadding / mesh.worldTransform.a; + const paddingY = mesh.canvasPadding / mesh.worldTransform.d; + const centerX = (x0 + x1 + x2) / 3; + const centerY = (y0 + y1 + y2) / 3; + + let normX = x0 - centerX; + let normY = y0 - centerY; + + let dist = Math.sqrt((normX * normX) + (normY * normY)); + + x0 = centerX + ((normX / dist) * (dist + paddingX)); + y0 = centerY + ((normY / dist) * (dist + paddingY)); + + // + + normX = x1 - centerX; + normY = y1 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x1 = centerX + ((normX / dist) * (dist + paddingX)); + y1 = centerY + ((normY / dist) * (dist + paddingY)); + + normX = x2 - centerX; + normY = y2 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x2 = centerX + ((normX / dist) * (dist + paddingX)); + y2 = centerY + ((normY / dist) * (dist + paddingY)); + } + + context.save(); + context.beginPath(); + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + + context.closePath(); + + context.clip(); + + // Compute matrix transform + const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); + const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); + const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); + const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); + const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); + const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); + const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); + + context.transform( + deltaA / delta, + deltaD / delta, + deltaB / delta, + deltaE / delta, + deltaC / delta, + deltaF / delta + ); + + context.drawImage( + textureSource, + 0, + 0, + textureWidth * base.resolution, + textureHeight * base.resolution, + 0, + 0, + textureWidth, + textureHeight + ); + + context.restore(); + } + + /** + * Renders a flat Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - The Mesh to render + */ + renderMeshFlat(mesh) + { + const context = this.renderer.context; + const vertices = mesh.vertices; + const length = vertices.length / 2; + + // this.count++; + + context.beginPath(); + + for (let i = 1; i < length - 2; ++i) + { + // draw some triangles! + const index = i * 2; + + const x0 = vertices[index]; + const y0 = vertices[index + 1]; + + const x1 = vertices[index + 2]; + const y1 = vertices[index + 3]; + + const x2 = vertices[index + 4]; + const y2 = vertices[index + 5]; + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + } + + context.fillStyle = '#FF0000'; + context.fill(); + context.closePath(); + } + + /** + * destroy the the renderer. + * + */ + destroy() + { + this.renderer = null; + } +} + +core.CanvasRenderer.registerPlugin('mesh', MeshSpriteRenderer); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 455d59c..0ce75d2 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -18,6 +18,14 @@ * let sprite2 = new PIXI.Sprite(texture); * ``` * + * Textures made from SVGs, loaded or not, cannot be used before the file finishes processing. You can check for this by checking the sprite's _textureID property. + * ```js + * var texture = PIXI.Texture.fromImage('assets/image.svg'); + * var sprite1 = new PIXI.Sprite(texture); + * //sprite1._textureID should not be undefined if the texture has finished processing the SVG file + * ``` + * You can use a ticker or rAF to ensure your sprites load the finished textures after processing. See issue #3068. + * * @class * @extends EventEmitter * @memberof PIXI diff --git a/src/extras/getGlobalPosition.js b/src/extras/getGlobalPosition.js index b71682f..a23761f 100644 --- a/src/extras/getGlobalPosition.js +++ b/src/extras/getGlobalPosition.js @@ -1,20 +1,20 @@ import * as core from '../core'; /** - * Returns the global position of the displayObject + * Returns the global position of the displayObject. Does not depend on object scale, rotation and pivot. * * @memberof PIXI.DisplayObject# * @param {Point} point - the point to write the global value to. If null a new point will be returned + * @param {boolean} skipUpdate - setting to true will stop the transforms of the scene graph from + * being updated. This means the calculation returned MAY be out of date BUT will give you a + * nice performance boost * @return {Point} The updated point */ -core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point()) +core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point(), skipUpdate = false) { if (this.parent) { - this.displayObjectUpdateTransform(); - - point.x = this.worldTransform.tx; - point.y = this.worldTransform.ty; + this.parent.toGlobal(this.position, point, skipUpdate); } else { diff --git a/src/filters/blur/BlurFilter.js b/src/filters/blur/BlurFilter.js index 4919d10..5b3c3a9 100644 --- a/src/filters/blur/BlurFilter.js +++ b/src/filters/blur/BlurFilter.js @@ -68,7 +68,7 @@ set blur(value) { this.blurXFilter.blur = this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -113,7 +113,7 @@ set blurX(value) { this.blurXFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -136,6 +136,6 @@ set blurY(value) { this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } } diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 4719b6a..2ced6dc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,6 +1,4 @@ import * as core from '../core'; -import glCore from 'pixi-gl-core'; -import Shader from './webgl/MeshShader'; const tempPoint = new core.Point(); const tempPolygon = new core.Polygon(); @@ -59,11 +57,17 @@ this.indices = indices || new Uint16Array([0, 1, 3, 2]); /** - * Whether the Mesh is dirty or not + * Version of mesh uvs are dirty or not * * @member {number} */ this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ this.indexDirty = 0; /** @@ -122,62 +126,8 @@ */ _renderWebGL(renderer) { - // get rid of any thing that may be batching. - renderer.flush(); - - // renderer.plugins.mesh.render(this); - const gl = renderer.gl; - let glData = this._glDatas[renderer.CONTEXT_UID]; - - if (!glData) - { - glData = { - shader: new Shader(gl), - vertexBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.vertices, gl.STREAM_DRAW), - uvBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.uvs, gl.STREAM_DRAW), - indexBuffer: glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW), - // build the vao object that will render.. - vao: new glCore.VertexArrayObject(gl), - dirty: this.dirty, - indexDirty: this.indexDirty, - }; - - // build the vao object that will render.. - glData.vao = new glCore.VertexArrayObject(gl) - .addIndex(glData.indexBuffer) - .addAttribute(glData.vertexBuffer, glData.shader.attributes.aVertexPosition, gl.FLOAT, false, 2 * 4, 0) - .addAttribute(glData.uvBuffer, glData.shader.attributes.aTextureCoord, gl.FLOAT, false, 2 * 4, 0); - - this._glDatas[renderer.CONTEXT_UID] = glData; - } - - if (this.dirty !== glData.dirty) - { - glData.dirty = this.dirty; - glData.uvBuffer.upload(); - } - - if (this.indexDirty !== glData.indexDirty) - { - glData.indexDirty = this.indexDirty; - glData.indexBuffer.upload(); - } - - glData.vertexBuffer.upload(); - - renderer.bindShader(glData.shader); - renderer.bindTexture(this._texture, 0); - renderer.state.setBlendMode(this.blendMode); - - glData.shader.uniforms.translationMatrix = this.worldTransform.toArray(true); - glData.shader.uniforms.alpha = this.worldAlpha; - glData.shader.uniforms.tint = this.tintRgb; - - const drawMode = this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; - - glData.vao.bind() - .draw(drawMode, this.indices.length) - .unbind(); + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); } /** @@ -188,240 +138,7 @@ */ _renderCanvas(renderer) { - const context = renderer.context; - - const transform = this.worldTransform; - const res = renderer.resolution; - - if (renderer.roundPixels) - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - (transform.tx * res) | 0, - (transform.ty * res) | 0 - ); - } - else - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - transform.tx * res, - transform.ty * res - ); - } - - if (this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) - { - this._renderCanvasTriangleMesh(context); - } - else - { - this._renderCanvasTriangles(context); - } - } - - /** - * Draws the object in Triangle Mesh mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - The current drawing context - */ - _renderCanvasTriangleMesh(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const length = vertices.length / 2; - - // this.count++; - - for (let i = 0; i < length - 2; i++) - { - // draw some triangles! - const index = i * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index, (index + 2), (index + 4)); - } - } - - /** - * Draws the object in triangle mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - */ - _renderCanvasTriangles(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const indices = this.indices; - const length = indices.length; - // this.count++; - - for (let i = 0; i < length; i += 3) - { - // draw some triangles! - const index0 = indices[i] * 2; - const index1 = indices[i + 1] * 2; - const index2 = indices[i + 2] * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2); - } - } - - /** - * Draws one of the triangles that form this Mesh - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - * @param {Float32Array} vertices - a reference to the vertices of the Mesh - * @param {Float32Array} uvs - a reference to the uvs of the Mesh - * @param {number} index0 - the index of the first vertex - * @param {number} index1 - the index of the second vertex - * @param {number} index2 - the index of the third vertex - */ - _renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2) - { - const base = this._texture.baseTexture; - const textureSource = base.source; - const textureWidth = base.width; - const textureHeight = base.height; - - const u0 = uvs[index0] * base.width; - const u1 = uvs[index1] * base.width; - const u2 = uvs[index2] * base.width; - const v0 = uvs[index0 + 1] * base.height; - const v1 = uvs[index1 + 1] * base.height; - const v2 = uvs[index2 + 1] * base.height; - - let x0 = vertices[index0]; - let x1 = vertices[index1]; - let x2 = vertices[index2]; - let y0 = vertices[index0 + 1]; - let y1 = vertices[index1 + 1]; - let y2 = vertices[index2 + 1]; - - if (this.canvasPadding > 0) - { - const paddingX = this.canvasPadding / this.worldTransform.a; - const paddingY = this.canvasPadding / this.worldTransform.d; - const centerX = (x0 + x1 + x2) / 3; - const centerY = (y0 + y1 + y2) / 3; - - let normX = x0 - centerX; - let normY = y0 - centerY; - - let dist = Math.sqrt((normX * normX) + (normY * normY)); - - x0 = centerX + ((normX / dist) * (dist + paddingX)); - y0 = centerY + ((normY / dist) * (dist + paddingY)); - - // - - normX = x1 - centerX; - normY = y1 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x1 = centerX + ((normX / dist) * (dist + paddingX)); - y1 = centerY + ((normY / dist) * (dist + paddingY)); - - normX = x2 - centerX; - normY = y2 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x2 = centerX + ((normX / dist) * (dist + paddingX)); - y2 = centerY + ((normY / dist) * (dist + paddingY)); - } - - context.save(); - context.beginPath(); - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - - context.closePath(); - - context.clip(); - - // Compute matrix transform - const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); - const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); - const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); - const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); - const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); - const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); - const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); - - context.transform( - deltaA / delta, - deltaD / delta, - deltaB / delta, - deltaE / delta, - deltaC / delta, - deltaF / delta - ); - - context.drawImage( - textureSource, - 0, - 0, - textureWidth * base.resolution, - textureHeight * base.resolution, - 0, - 0, - textureWidth, - textureHeight - ); - - context.restore(); - } - - /** - * Renders a flat Mesh - * - * @private - * @param {PIXI.mesh.Mesh} mesh - The Mesh to render - */ - renderMeshFlat(mesh) - { - const context = this.context; - const vertices = mesh.vertices; - const length = vertices.length / 2; - - // this.count++; - - context.beginPath(); - - for (let i = 1; i < length - 2; ++i) - { - // draw some triangles! - const index = i * 2; - - const x0 = vertices[index]; - const y0 = vertices[index + 1]; - - const x1 = vertices[index + 2]; - const y1 = vertices[index + 3]; - - const x2 = vertices[index + 4]; - const y2 = vertices[index + 5]; - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - } - - context.fillStyle = '#FF0000'; - context.fill(); - context.closePath(); + renderer.plugins.mesh.render(this); } /** diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js new file mode 100644 index 0000000..a1ad74c --- /dev/null +++ b/src/mesh/canvas/CanvasMeshRenderer.js @@ -0,0 +1,276 @@ +import * as core from '../../core'; +import { default as Mesh } from '../Mesh'; + +/** + * Renderer dedicated to meshes. + * + * @class + * @private + * @memberof PIXI + */ +export default class MeshSpriteRenderer +{ + /** + * @param {PIXI.CanvasRenderer} renderer - The renderer this downport works for + */ + constructor(renderer) + { + this.renderer = renderer; + } + + /** + * Renders the Mesh + * + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + render(mesh) + { + const renderer = this.renderer; + const context = renderer.context; + + const transform = mesh.worldTransform; + const res = renderer.resolution; + + if (renderer.roundPixels) + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + (transform.tx * res) | 0, + (transform.ty * res) | 0 + ); + } + else + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + transform.tx * res, + transform.ty * res + ); + } + + if (mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) + { + this._renderTriangleMesh(mesh); + } + else + { + this._renderTriangles(mesh); + } + } + + /** + * Draws the object in Triangle Mesh mode + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + _renderTriangleMesh(mesh) + { + // draw triangles!! + const length = mesh.vertices.length / 2; + + for (let i = 0; i < length - 2; i++) + { + // draw some triangles! + const index = i * 2; + + this._renderDrawTriangle(mesh, index, (index + 2), (index + 4)); + } + } + + /** + * Draws the object in triangle mode using canvas + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + */ + _renderTriangles(mesh) + { + // draw triangles!! + const indices = mesh.indices; + const length = indices.length; + + for (let i = 0; i < length; i += 3) + { + // draw some triangles! + const index0 = indices[i] * 2; + const index1 = indices[i + 1] * 2; + const index2 = indices[i + 2] * 2; + + this._renderDrawTriangle(mesh, index0, index1, index2); + } + } + + /** + * Draws one of the triangles that from the Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + * @param {number} index0 - the index of the first vertex + * @param {number} index1 - the index of the second vertex + * @param {number} index2 - the index of the third vertex + */ + _renderDrawTriangle(mesh, index0, index1, index2) + { + const context = this.renderer.context; + const uvs = mesh.uvs; + const vertices = mesh.vertices; + const texture = mesh._texture; + + if (!texture.valid) + { + return; + } + + const base = texture.baseTexture; + const textureSource = base.source; + const textureWidth = base.width; + const textureHeight = base.height; + + const u0 = uvs[index0] * base.width; + const u1 = uvs[index1] * base.width; + const u2 = uvs[index2] * base.width; + const v0 = uvs[index0 + 1] * base.height; + const v1 = uvs[index1 + 1] * base.height; + const v2 = uvs[index2 + 1] * base.height; + + let x0 = vertices[index0]; + let x1 = vertices[index1]; + let x2 = vertices[index2]; + let y0 = vertices[index0 + 1]; + let y1 = vertices[index1 + 1]; + let y2 = vertices[index2 + 1]; + + if (mesh.canvasPadding > 0) + { + const paddingX = mesh.canvasPadding / mesh.worldTransform.a; + const paddingY = mesh.canvasPadding / mesh.worldTransform.d; + const centerX = (x0 + x1 + x2) / 3; + const centerY = (y0 + y1 + y2) / 3; + + let normX = x0 - centerX; + let normY = y0 - centerY; + + let dist = Math.sqrt((normX * normX) + (normY * normY)); + + x0 = centerX + ((normX / dist) * (dist + paddingX)); + y0 = centerY + ((normY / dist) * (dist + paddingY)); + + // + + normX = x1 - centerX; + normY = y1 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x1 = centerX + ((normX / dist) * (dist + paddingX)); + y1 = centerY + ((normY / dist) * (dist + paddingY)); + + normX = x2 - centerX; + normY = y2 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x2 = centerX + ((normX / dist) * (dist + paddingX)); + y2 = centerY + ((normY / dist) * (dist + paddingY)); + } + + context.save(); + context.beginPath(); + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + + context.closePath(); + + context.clip(); + + // Compute matrix transform + const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); + const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); + const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); + const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); + const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); + const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); + const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); + + context.transform( + deltaA / delta, + deltaD / delta, + deltaB / delta, + deltaE / delta, + deltaC / delta, + deltaF / delta + ); + + context.drawImage( + textureSource, + 0, + 0, + textureWidth * base.resolution, + textureHeight * base.resolution, + 0, + 0, + textureWidth, + textureHeight + ); + + context.restore(); + } + + /** + * Renders a flat Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - The Mesh to render + */ + renderMeshFlat(mesh) + { + const context = this.renderer.context; + const vertices = mesh.vertices; + const length = vertices.length / 2; + + // this.count++; + + context.beginPath(); + + for (let i = 1; i < length - 2; ++i) + { + // draw some triangles! + const index = i * 2; + + const x0 = vertices[index]; + const y0 = vertices[index + 1]; + + const x1 = vertices[index + 2]; + const y1 = vertices[index + 3]; + + const x2 = vertices[index + 4]; + const y2 = vertices[index + 5]; + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + } + + context.fillStyle = '#FF0000'; + context.fill(); + context.closePath(); + } + + /** + * destroy the the renderer. + * + */ + destroy() + { + this.renderer = null; + } +} + +core.CanvasRenderer.registerPlugin('mesh', MeshSpriteRenderer); diff --git a/src/mesh/index.js b/src/mesh/index.js index 473d22a..4d1cad6 100644 --- a/src/mesh/index.js +++ b/src/mesh/index.js @@ -2,7 +2,8 @@ * @namespace PIXI.mesh */ export { default as Mesh } from './Mesh'; +export { default as MeshRenderer } from './webgl/MeshRenderer'; +export { default as CanvasMeshRenderer } from './canvas/CanvasMeshRenderer'; export { default as Plane } from './Plane'; export { default as NineSlicePlane } from './NineSlicePlane'; export { default as Rope } from './Rope'; -export { default as MeshShader } from './webgl/MeshShader'; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 455d59c..0ce75d2 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -18,6 +18,14 @@ * let sprite2 = new PIXI.Sprite(texture); * ``` * + * Textures made from SVGs, loaded or not, cannot be used before the file finishes processing. You can check for this by checking the sprite's _textureID property. + * ```js + * var texture = PIXI.Texture.fromImage('assets/image.svg'); + * var sprite1 = new PIXI.Sprite(texture); + * //sprite1._textureID should not be undefined if the texture has finished processing the SVG file + * ``` + * You can use a ticker or rAF to ensure your sprites load the finished textures after processing. See issue #3068. + * * @class * @extends EventEmitter * @memberof PIXI diff --git a/src/extras/getGlobalPosition.js b/src/extras/getGlobalPosition.js index b71682f..a23761f 100644 --- a/src/extras/getGlobalPosition.js +++ b/src/extras/getGlobalPosition.js @@ -1,20 +1,20 @@ import * as core from '../core'; /** - * Returns the global position of the displayObject + * Returns the global position of the displayObject. Does not depend on object scale, rotation and pivot. * * @memberof PIXI.DisplayObject# * @param {Point} point - the point to write the global value to. If null a new point will be returned + * @param {boolean} skipUpdate - setting to true will stop the transforms of the scene graph from + * being updated. This means the calculation returned MAY be out of date BUT will give you a + * nice performance boost * @return {Point} The updated point */ -core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point()) +core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point(), skipUpdate = false) { if (this.parent) { - this.displayObjectUpdateTransform(); - - point.x = this.worldTransform.tx; - point.y = this.worldTransform.ty; + this.parent.toGlobal(this.position, point, skipUpdate); } else { diff --git a/src/filters/blur/BlurFilter.js b/src/filters/blur/BlurFilter.js index 4919d10..5b3c3a9 100644 --- a/src/filters/blur/BlurFilter.js +++ b/src/filters/blur/BlurFilter.js @@ -68,7 +68,7 @@ set blur(value) { this.blurXFilter.blur = this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -113,7 +113,7 @@ set blurX(value) { this.blurXFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -136,6 +136,6 @@ set blurY(value) { this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } } diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 4719b6a..2ced6dc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,6 +1,4 @@ import * as core from '../core'; -import glCore from 'pixi-gl-core'; -import Shader from './webgl/MeshShader'; const tempPoint = new core.Point(); const tempPolygon = new core.Polygon(); @@ -59,11 +57,17 @@ this.indices = indices || new Uint16Array([0, 1, 3, 2]); /** - * Whether the Mesh is dirty or not + * Version of mesh uvs are dirty or not * * @member {number} */ this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ this.indexDirty = 0; /** @@ -122,62 +126,8 @@ */ _renderWebGL(renderer) { - // get rid of any thing that may be batching. - renderer.flush(); - - // renderer.plugins.mesh.render(this); - const gl = renderer.gl; - let glData = this._glDatas[renderer.CONTEXT_UID]; - - if (!glData) - { - glData = { - shader: new Shader(gl), - vertexBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.vertices, gl.STREAM_DRAW), - uvBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.uvs, gl.STREAM_DRAW), - indexBuffer: glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW), - // build the vao object that will render.. - vao: new glCore.VertexArrayObject(gl), - dirty: this.dirty, - indexDirty: this.indexDirty, - }; - - // build the vao object that will render.. - glData.vao = new glCore.VertexArrayObject(gl) - .addIndex(glData.indexBuffer) - .addAttribute(glData.vertexBuffer, glData.shader.attributes.aVertexPosition, gl.FLOAT, false, 2 * 4, 0) - .addAttribute(glData.uvBuffer, glData.shader.attributes.aTextureCoord, gl.FLOAT, false, 2 * 4, 0); - - this._glDatas[renderer.CONTEXT_UID] = glData; - } - - if (this.dirty !== glData.dirty) - { - glData.dirty = this.dirty; - glData.uvBuffer.upload(); - } - - if (this.indexDirty !== glData.indexDirty) - { - glData.indexDirty = this.indexDirty; - glData.indexBuffer.upload(); - } - - glData.vertexBuffer.upload(); - - renderer.bindShader(glData.shader); - renderer.bindTexture(this._texture, 0); - renderer.state.setBlendMode(this.blendMode); - - glData.shader.uniforms.translationMatrix = this.worldTransform.toArray(true); - glData.shader.uniforms.alpha = this.worldAlpha; - glData.shader.uniforms.tint = this.tintRgb; - - const drawMode = this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; - - glData.vao.bind() - .draw(drawMode, this.indices.length) - .unbind(); + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); } /** @@ -188,240 +138,7 @@ */ _renderCanvas(renderer) { - const context = renderer.context; - - const transform = this.worldTransform; - const res = renderer.resolution; - - if (renderer.roundPixels) - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - (transform.tx * res) | 0, - (transform.ty * res) | 0 - ); - } - else - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - transform.tx * res, - transform.ty * res - ); - } - - if (this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) - { - this._renderCanvasTriangleMesh(context); - } - else - { - this._renderCanvasTriangles(context); - } - } - - /** - * Draws the object in Triangle Mesh mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - The current drawing context - */ - _renderCanvasTriangleMesh(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const length = vertices.length / 2; - - // this.count++; - - for (let i = 0; i < length - 2; i++) - { - // draw some triangles! - const index = i * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index, (index + 2), (index + 4)); - } - } - - /** - * Draws the object in triangle mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - */ - _renderCanvasTriangles(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const indices = this.indices; - const length = indices.length; - // this.count++; - - for (let i = 0; i < length; i += 3) - { - // draw some triangles! - const index0 = indices[i] * 2; - const index1 = indices[i + 1] * 2; - const index2 = indices[i + 2] * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2); - } - } - - /** - * Draws one of the triangles that form this Mesh - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - * @param {Float32Array} vertices - a reference to the vertices of the Mesh - * @param {Float32Array} uvs - a reference to the uvs of the Mesh - * @param {number} index0 - the index of the first vertex - * @param {number} index1 - the index of the second vertex - * @param {number} index2 - the index of the third vertex - */ - _renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2) - { - const base = this._texture.baseTexture; - const textureSource = base.source; - const textureWidth = base.width; - const textureHeight = base.height; - - const u0 = uvs[index0] * base.width; - const u1 = uvs[index1] * base.width; - const u2 = uvs[index2] * base.width; - const v0 = uvs[index0 + 1] * base.height; - const v1 = uvs[index1 + 1] * base.height; - const v2 = uvs[index2 + 1] * base.height; - - let x0 = vertices[index0]; - let x1 = vertices[index1]; - let x2 = vertices[index2]; - let y0 = vertices[index0 + 1]; - let y1 = vertices[index1 + 1]; - let y2 = vertices[index2 + 1]; - - if (this.canvasPadding > 0) - { - const paddingX = this.canvasPadding / this.worldTransform.a; - const paddingY = this.canvasPadding / this.worldTransform.d; - const centerX = (x0 + x1 + x2) / 3; - const centerY = (y0 + y1 + y2) / 3; - - let normX = x0 - centerX; - let normY = y0 - centerY; - - let dist = Math.sqrt((normX * normX) + (normY * normY)); - - x0 = centerX + ((normX / dist) * (dist + paddingX)); - y0 = centerY + ((normY / dist) * (dist + paddingY)); - - // - - normX = x1 - centerX; - normY = y1 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x1 = centerX + ((normX / dist) * (dist + paddingX)); - y1 = centerY + ((normY / dist) * (dist + paddingY)); - - normX = x2 - centerX; - normY = y2 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x2 = centerX + ((normX / dist) * (dist + paddingX)); - y2 = centerY + ((normY / dist) * (dist + paddingY)); - } - - context.save(); - context.beginPath(); - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - - context.closePath(); - - context.clip(); - - // Compute matrix transform - const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); - const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); - const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); - const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); - const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); - const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); - const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); - - context.transform( - deltaA / delta, - deltaD / delta, - deltaB / delta, - deltaE / delta, - deltaC / delta, - deltaF / delta - ); - - context.drawImage( - textureSource, - 0, - 0, - textureWidth * base.resolution, - textureHeight * base.resolution, - 0, - 0, - textureWidth, - textureHeight - ); - - context.restore(); - } - - /** - * Renders a flat Mesh - * - * @private - * @param {PIXI.mesh.Mesh} mesh - The Mesh to render - */ - renderMeshFlat(mesh) - { - const context = this.context; - const vertices = mesh.vertices; - const length = vertices.length / 2; - - // this.count++; - - context.beginPath(); - - for (let i = 1; i < length - 2; ++i) - { - // draw some triangles! - const index = i * 2; - - const x0 = vertices[index]; - const y0 = vertices[index + 1]; - - const x1 = vertices[index + 2]; - const y1 = vertices[index + 3]; - - const x2 = vertices[index + 4]; - const y2 = vertices[index + 5]; - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - } - - context.fillStyle = '#FF0000'; - context.fill(); - context.closePath(); + renderer.plugins.mesh.render(this); } /** diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js new file mode 100644 index 0000000..a1ad74c --- /dev/null +++ b/src/mesh/canvas/CanvasMeshRenderer.js @@ -0,0 +1,276 @@ +import * as core from '../../core'; +import { default as Mesh } from '../Mesh'; + +/** + * Renderer dedicated to meshes. + * + * @class + * @private + * @memberof PIXI + */ +export default class MeshSpriteRenderer +{ + /** + * @param {PIXI.CanvasRenderer} renderer - The renderer this downport works for + */ + constructor(renderer) + { + this.renderer = renderer; + } + + /** + * Renders the Mesh + * + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + render(mesh) + { + const renderer = this.renderer; + const context = renderer.context; + + const transform = mesh.worldTransform; + const res = renderer.resolution; + + if (renderer.roundPixels) + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + (transform.tx * res) | 0, + (transform.ty * res) | 0 + ); + } + else + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + transform.tx * res, + transform.ty * res + ); + } + + if (mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) + { + this._renderTriangleMesh(mesh); + } + else + { + this._renderTriangles(mesh); + } + } + + /** + * Draws the object in Triangle Mesh mode + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + _renderTriangleMesh(mesh) + { + // draw triangles!! + const length = mesh.vertices.length / 2; + + for (let i = 0; i < length - 2; i++) + { + // draw some triangles! + const index = i * 2; + + this._renderDrawTriangle(mesh, index, (index + 2), (index + 4)); + } + } + + /** + * Draws the object in triangle mode using canvas + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + */ + _renderTriangles(mesh) + { + // draw triangles!! + const indices = mesh.indices; + const length = indices.length; + + for (let i = 0; i < length; i += 3) + { + // draw some triangles! + const index0 = indices[i] * 2; + const index1 = indices[i + 1] * 2; + const index2 = indices[i + 2] * 2; + + this._renderDrawTriangle(mesh, index0, index1, index2); + } + } + + /** + * Draws one of the triangles that from the Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + * @param {number} index0 - the index of the first vertex + * @param {number} index1 - the index of the second vertex + * @param {number} index2 - the index of the third vertex + */ + _renderDrawTriangle(mesh, index0, index1, index2) + { + const context = this.renderer.context; + const uvs = mesh.uvs; + const vertices = mesh.vertices; + const texture = mesh._texture; + + if (!texture.valid) + { + return; + } + + const base = texture.baseTexture; + const textureSource = base.source; + const textureWidth = base.width; + const textureHeight = base.height; + + const u0 = uvs[index0] * base.width; + const u1 = uvs[index1] * base.width; + const u2 = uvs[index2] * base.width; + const v0 = uvs[index0 + 1] * base.height; + const v1 = uvs[index1 + 1] * base.height; + const v2 = uvs[index2 + 1] * base.height; + + let x0 = vertices[index0]; + let x1 = vertices[index1]; + let x2 = vertices[index2]; + let y0 = vertices[index0 + 1]; + let y1 = vertices[index1 + 1]; + let y2 = vertices[index2 + 1]; + + if (mesh.canvasPadding > 0) + { + const paddingX = mesh.canvasPadding / mesh.worldTransform.a; + const paddingY = mesh.canvasPadding / mesh.worldTransform.d; + const centerX = (x0 + x1 + x2) / 3; + const centerY = (y0 + y1 + y2) / 3; + + let normX = x0 - centerX; + let normY = y0 - centerY; + + let dist = Math.sqrt((normX * normX) + (normY * normY)); + + x0 = centerX + ((normX / dist) * (dist + paddingX)); + y0 = centerY + ((normY / dist) * (dist + paddingY)); + + // + + normX = x1 - centerX; + normY = y1 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x1 = centerX + ((normX / dist) * (dist + paddingX)); + y1 = centerY + ((normY / dist) * (dist + paddingY)); + + normX = x2 - centerX; + normY = y2 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x2 = centerX + ((normX / dist) * (dist + paddingX)); + y2 = centerY + ((normY / dist) * (dist + paddingY)); + } + + context.save(); + context.beginPath(); + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + + context.closePath(); + + context.clip(); + + // Compute matrix transform + const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); + const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); + const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); + const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); + const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); + const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); + const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); + + context.transform( + deltaA / delta, + deltaD / delta, + deltaB / delta, + deltaE / delta, + deltaC / delta, + deltaF / delta + ); + + context.drawImage( + textureSource, + 0, + 0, + textureWidth * base.resolution, + textureHeight * base.resolution, + 0, + 0, + textureWidth, + textureHeight + ); + + context.restore(); + } + + /** + * Renders a flat Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - The Mesh to render + */ + renderMeshFlat(mesh) + { + const context = this.renderer.context; + const vertices = mesh.vertices; + const length = vertices.length / 2; + + // this.count++; + + context.beginPath(); + + for (let i = 1; i < length - 2; ++i) + { + // draw some triangles! + const index = i * 2; + + const x0 = vertices[index]; + const y0 = vertices[index + 1]; + + const x1 = vertices[index + 2]; + const y1 = vertices[index + 3]; + + const x2 = vertices[index + 4]; + const y2 = vertices[index + 5]; + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + } + + context.fillStyle = '#FF0000'; + context.fill(); + context.closePath(); + } + + /** + * destroy the the renderer. + * + */ + destroy() + { + this.renderer = null; + } +} + +core.CanvasRenderer.registerPlugin('mesh', MeshSpriteRenderer); diff --git a/src/mesh/index.js b/src/mesh/index.js index 473d22a..4d1cad6 100644 --- a/src/mesh/index.js +++ b/src/mesh/index.js @@ -2,7 +2,8 @@ * @namespace PIXI.mesh */ export { default as Mesh } from './Mesh'; +export { default as MeshRenderer } from './webgl/MeshRenderer'; +export { default as CanvasMeshRenderer } from './canvas/CanvasMeshRenderer'; export { default as Plane } from './Plane'; export { default as NineSlicePlane } from './NineSlicePlane'; export { default as Rope } from './Rope'; -export { default as MeshShader } from './webgl/MeshShader'; diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js new file mode 100644 index 0000000..73d78e6 --- /dev/null +++ b/src/mesh/webgl/MeshRenderer.js @@ -0,0 +1,108 @@ +import * as core from '../../core'; +import glCore from 'pixi-gl-core'; +import { default as Mesh } from '../Mesh'; + +const glslify = require('glslify'); // eslint-disable-line no-undef + +/** + * WebGL renderer plugin for tiling sprites + */ +export class MeshRenderer extends core.ObjectRenderer { + + /** + * constructor for renderer + * + * @param {WebGLRenderer} renderer The renderer this tiling awesomeness works for. + */ + constructor(renderer) + { + super(renderer); + + this.shader = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + onContextChange() + { + const gl = this.renderer.gl; + + this.shader = new core.Shader(gl, + glslify('./mesh.vert'), + glslify('./mesh.frag')); + } + + /** + * renders mesh + * + * @param {PIXI.mesh.Mesh} mesh mesh instance + */ + render(mesh) + { + const renderer = this.renderer; + const gl = renderer.gl; + const texture = mesh._texture; + + if (!texture.valid) + { + return; + } + + let glData = mesh._glDatas[renderer.CONTEXT_UID]; + + if (!glData) + { + glData = { + shader: this.shader, + vertexBuffer: glCore.GLBuffer.createVertexBuffer(gl, mesh.vertices, gl.STREAM_DRAW), + uvBuffer: glCore.GLBuffer.createVertexBuffer(gl, mesh.uvs, gl.STREAM_DRAW), + indexBuffer: glCore.GLBuffer.createIndexBuffer(gl, mesh.indices, gl.STATIC_DRAW), + // build the vao object that will render.. + vao: new glCore.VertexArrayObject(gl), + dirty: mesh.dirty, + indexDirty: mesh.indexDirty, + }; + + // build the vao object that will render.. + glData.vao = new glCore.VertexArrayObject(gl) + .addIndex(glData.indexBuffer) + .addAttribute(glData.vertexBuffer, glData.shader.attributes.aVertexPosition, gl.FLOAT, false, 2 * 4, 0) + .addAttribute(glData.uvBuffer, glData.shader.attributes.aTextureCoord, gl.FLOAT, false, 2 * 4, 0); + + mesh._glDatas[renderer.CONTEXT_UID] = glData; + } + + if (mesh.dirty !== glData.dirty) + { + glData.dirty = mesh.dirty; + glData.uvBuffer.upload(); + } + + if (mesh.indexDirty !== glData.indexDirty) + { + glData.indexDirty = mesh.indexDirty; + glData.indexBuffer.upload(); + } + + glData.vertexBuffer.upload(); + + renderer.bindShader(glData.shader); + renderer.bindTexture(texture, 0); + renderer.state.setBlendMode(mesh.blendMode); + + glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); + glData.shader.uniforms.alpha = mesh.worldAlpha; + glData.shader.uniforms.tint = mesh.tintRgb; + + const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; + + glData.vao.bind() + .draw(drawMode, mesh.indices.length) + .unbind(); + } +} + +core.WebGLRenderer.registerPlugin('mesh', MeshRenderer); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 455d59c..0ce75d2 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -18,6 +18,14 @@ * let sprite2 = new PIXI.Sprite(texture); * ``` * + * Textures made from SVGs, loaded or not, cannot be used before the file finishes processing. You can check for this by checking the sprite's _textureID property. + * ```js + * var texture = PIXI.Texture.fromImage('assets/image.svg'); + * var sprite1 = new PIXI.Sprite(texture); + * //sprite1._textureID should not be undefined if the texture has finished processing the SVG file + * ``` + * You can use a ticker or rAF to ensure your sprites load the finished textures after processing. See issue #3068. + * * @class * @extends EventEmitter * @memberof PIXI diff --git a/src/extras/getGlobalPosition.js b/src/extras/getGlobalPosition.js index b71682f..a23761f 100644 --- a/src/extras/getGlobalPosition.js +++ b/src/extras/getGlobalPosition.js @@ -1,20 +1,20 @@ import * as core from '../core'; /** - * Returns the global position of the displayObject + * Returns the global position of the displayObject. Does not depend on object scale, rotation and pivot. * * @memberof PIXI.DisplayObject# * @param {Point} point - the point to write the global value to. If null a new point will be returned + * @param {boolean} skipUpdate - setting to true will stop the transforms of the scene graph from + * being updated. This means the calculation returned MAY be out of date BUT will give you a + * nice performance boost * @return {Point} The updated point */ -core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point()) +core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point(), skipUpdate = false) { if (this.parent) { - this.displayObjectUpdateTransform(); - - point.x = this.worldTransform.tx; - point.y = this.worldTransform.ty; + this.parent.toGlobal(this.position, point, skipUpdate); } else { diff --git a/src/filters/blur/BlurFilter.js b/src/filters/blur/BlurFilter.js index 4919d10..5b3c3a9 100644 --- a/src/filters/blur/BlurFilter.js +++ b/src/filters/blur/BlurFilter.js @@ -68,7 +68,7 @@ set blur(value) { this.blurXFilter.blur = this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -113,7 +113,7 @@ set blurX(value) { this.blurXFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -136,6 +136,6 @@ set blurY(value) { this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } } diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 4719b6a..2ced6dc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,6 +1,4 @@ import * as core from '../core'; -import glCore from 'pixi-gl-core'; -import Shader from './webgl/MeshShader'; const tempPoint = new core.Point(); const tempPolygon = new core.Polygon(); @@ -59,11 +57,17 @@ this.indices = indices || new Uint16Array([0, 1, 3, 2]); /** - * Whether the Mesh is dirty or not + * Version of mesh uvs are dirty or not * * @member {number} */ this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ this.indexDirty = 0; /** @@ -122,62 +126,8 @@ */ _renderWebGL(renderer) { - // get rid of any thing that may be batching. - renderer.flush(); - - // renderer.plugins.mesh.render(this); - const gl = renderer.gl; - let glData = this._glDatas[renderer.CONTEXT_UID]; - - if (!glData) - { - glData = { - shader: new Shader(gl), - vertexBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.vertices, gl.STREAM_DRAW), - uvBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.uvs, gl.STREAM_DRAW), - indexBuffer: glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW), - // build the vao object that will render.. - vao: new glCore.VertexArrayObject(gl), - dirty: this.dirty, - indexDirty: this.indexDirty, - }; - - // build the vao object that will render.. - glData.vao = new glCore.VertexArrayObject(gl) - .addIndex(glData.indexBuffer) - .addAttribute(glData.vertexBuffer, glData.shader.attributes.aVertexPosition, gl.FLOAT, false, 2 * 4, 0) - .addAttribute(glData.uvBuffer, glData.shader.attributes.aTextureCoord, gl.FLOAT, false, 2 * 4, 0); - - this._glDatas[renderer.CONTEXT_UID] = glData; - } - - if (this.dirty !== glData.dirty) - { - glData.dirty = this.dirty; - glData.uvBuffer.upload(); - } - - if (this.indexDirty !== glData.indexDirty) - { - glData.indexDirty = this.indexDirty; - glData.indexBuffer.upload(); - } - - glData.vertexBuffer.upload(); - - renderer.bindShader(glData.shader); - renderer.bindTexture(this._texture, 0); - renderer.state.setBlendMode(this.blendMode); - - glData.shader.uniforms.translationMatrix = this.worldTransform.toArray(true); - glData.shader.uniforms.alpha = this.worldAlpha; - glData.shader.uniforms.tint = this.tintRgb; - - const drawMode = this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; - - glData.vao.bind() - .draw(drawMode, this.indices.length) - .unbind(); + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); } /** @@ -188,240 +138,7 @@ */ _renderCanvas(renderer) { - const context = renderer.context; - - const transform = this.worldTransform; - const res = renderer.resolution; - - if (renderer.roundPixels) - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - (transform.tx * res) | 0, - (transform.ty * res) | 0 - ); - } - else - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - transform.tx * res, - transform.ty * res - ); - } - - if (this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) - { - this._renderCanvasTriangleMesh(context); - } - else - { - this._renderCanvasTriangles(context); - } - } - - /** - * Draws the object in Triangle Mesh mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - The current drawing context - */ - _renderCanvasTriangleMesh(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const length = vertices.length / 2; - - // this.count++; - - for (let i = 0; i < length - 2; i++) - { - // draw some triangles! - const index = i * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index, (index + 2), (index + 4)); - } - } - - /** - * Draws the object in triangle mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - */ - _renderCanvasTriangles(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const indices = this.indices; - const length = indices.length; - // this.count++; - - for (let i = 0; i < length; i += 3) - { - // draw some triangles! - const index0 = indices[i] * 2; - const index1 = indices[i + 1] * 2; - const index2 = indices[i + 2] * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2); - } - } - - /** - * Draws one of the triangles that form this Mesh - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - * @param {Float32Array} vertices - a reference to the vertices of the Mesh - * @param {Float32Array} uvs - a reference to the uvs of the Mesh - * @param {number} index0 - the index of the first vertex - * @param {number} index1 - the index of the second vertex - * @param {number} index2 - the index of the third vertex - */ - _renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2) - { - const base = this._texture.baseTexture; - const textureSource = base.source; - const textureWidth = base.width; - const textureHeight = base.height; - - const u0 = uvs[index0] * base.width; - const u1 = uvs[index1] * base.width; - const u2 = uvs[index2] * base.width; - const v0 = uvs[index0 + 1] * base.height; - const v1 = uvs[index1 + 1] * base.height; - const v2 = uvs[index2 + 1] * base.height; - - let x0 = vertices[index0]; - let x1 = vertices[index1]; - let x2 = vertices[index2]; - let y0 = vertices[index0 + 1]; - let y1 = vertices[index1 + 1]; - let y2 = vertices[index2 + 1]; - - if (this.canvasPadding > 0) - { - const paddingX = this.canvasPadding / this.worldTransform.a; - const paddingY = this.canvasPadding / this.worldTransform.d; - const centerX = (x0 + x1 + x2) / 3; - const centerY = (y0 + y1 + y2) / 3; - - let normX = x0 - centerX; - let normY = y0 - centerY; - - let dist = Math.sqrt((normX * normX) + (normY * normY)); - - x0 = centerX + ((normX / dist) * (dist + paddingX)); - y0 = centerY + ((normY / dist) * (dist + paddingY)); - - // - - normX = x1 - centerX; - normY = y1 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x1 = centerX + ((normX / dist) * (dist + paddingX)); - y1 = centerY + ((normY / dist) * (dist + paddingY)); - - normX = x2 - centerX; - normY = y2 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x2 = centerX + ((normX / dist) * (dist + paddingX)); - y2 = centerY + ((normY / dist) * (dist + paddingY)); - } - - context.save(); - context.beginPath(); - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - - context.closePath(); - - context.clip(); - - // Compute matrix transform - const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); - const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); - const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); - const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); - const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); - const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); - const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); - - context.transform( - deltaA / delta, - deltaD / delta, - deltaB / delta, - deltaE / delta, - deltaC / delta, - deltaF / delta - ); - - context.drawImage( - textureSource, - 0, - 0, - textureWidth * base.resolution, - textureHeight * base.resolution, - 0, - 0, - textureWidth, - textureHeight - ); - - context.restore(); - } - - /** - * Renders a flat Mesh - * - * @private - * @param {PIXI.mesh.Mesh} mesh - The Mesh to render - */ - renderMeshFlat(mesh) - { - const context = this.context; - const vertices = mesh.vertices; - const length = vertices.length / 2; - - // this.count++; - - context.beginPath(); - - for (let i = 1; i < length - 2; ++i) - { - // draw some triangles! - const index = i * 2; - - const x0 = vertices[index]; - const y0 = vertices[index + 1]; - - const x1 = vertices[index + 2]; - const y1 = vertices[index + 3]; - - const x2 = vertices[index + 4]; - const y2 = vertices[index + 5]; - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - } - - context.fillStyle = '#FF0000'; - context.fill(); - context.closePath(); + renderer.plugins.mesh.render(this); } /** diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js new file mode 100644 index 0000000..a1ad74c --- /dev/null +++ b/src/mesh/canvas/CanvasMeshRenderer.js @@ -0,0 +1,276 @@ +import * as core from '../../core'; +import { default as Mesh } from '../Mesh'; + +/** + * Renderer dedicated to meshes. + * + * @class + * @private + * @memberof PIXI + */ +export default class MeshSpriteRenderer +{ + /** + * @param {PIXI.CanvasRenderer} renderer - The renderer this downport works for + */ + constructor(renderer) + { + this.renderer = renderer; + } + + /** + * Renders the Mesh + * + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + render(mesh) + { + const renderer = this.renderer; + const context = renderer.context; + + const transform = mesh.worldTransform; + const res = renderer.resolution; + + if (renderer.roundPixels) + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + (transform.tx * res) | 0, + (transform.ty * res) | 0 + ); + } + else + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + transform.tx * res, + transform.ty * res + ); + } + + if (mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) + { + this._renderTriangleMesh(mesh); + } + else + { + this._renderTriangles(mesh); + } + } + + /** + * Draws the object in Triangle Mesh mode + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + _renderTriangleMesh(mesh) + { + // draw triangles!! + const length = mesh.vertices.length / 2; + + for (let i = 0; i < length - 2; i++) + { + // draw some triangles! + const index = i * 2; + + this._renderDrawTriangle(mesh, index, (index + 2), (index + 4)); + } + } + + /** + * Draws the object in triangle mode using canvas + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + */ + _renderTriangles(mesh) + { + // draw triangles!! + const indices = mesh.indices; + const length = indices.length; + + for (let i = 0; i < length; i += 3) + { + // draw some triangles! + const index0 = indices[i] * 2; + const index1 = indices[i + 1] * 2; + const index2 = indices[i + 2] * 2; + + this._renderDrawTriangle(mesh, index0, index1, index2); + } + } + + /** + * Draws one of the triangles that from the Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + * @param {number} index0 - the index of the first vertex + * @param {number} index1 - the index of the second vertex + * @param {number} index2 - the index of the third vertex + */ + _renderDrawTriangle(mesh, index0, index1, index2) + { + const context = this.renderer.context; + const uvs = mesh.uvs; + const vertices = mesh.vertices; + const texture = mesh._texture; + + if (!texture.valid) + { + return; + } + + const base = texture.baseTexture; + const textureSource = base.source; + const textureWidth = base.width; + const textureHeight = base.height; + + const u0 = uvs[index0] * base.width; + const u1 = uvs[index1] * base.width; + const u2 = uvs[index2] * base.width; + const v0 = uvs[index0 + 1] * base.height; + const v1 = uvs[index1 + 1] * base.height; + const v2 = uvs[index2 + 1] * base.height; + + let x0 = vertices[index0]; + let x1 = vertices[index1]; + let x2 = vertices[index2]; + let y0 = vertices[index0 + 1]; + let y1 = vertices[index1 + 1]; + let y2 = vertices[index2 + 1]; + + if (mesh.canvasPadding > 0) + { + const paddingX = mesh.canvasPadding / mesh.worldTransform.a; + const paddingY = mesh.canvasPadding / mesh.worldTransform.d; + const centerX = (x0 + x1 + x2) / 3; + const centerY = (y0 + y1 + y2) / 3; + + let normX = x0 - centerX; + let normY = y0 - centerY; + + let dist = Math.sqrt((normX * normX) + (normY * normY)); + + x0 = centerX + ((normX / dist) * (dist + paddingX)); + y0 = centerY + ((normY / dist) * (dist + paddingY)); + + // + + normX = x1 - centerX; + normY = y1 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x1 = centerX + ((normX / dist) * (dist + paddingX)); + y1 = centerY + ((normY / dist) * (dist + paddingY)); + + normX = x2 - centerX; + normY = y2 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x2 = centerX + ((normX / dist) * (dist + paddingX)); + y2 = centerY + ((normY / dist) * (dist + paddingY)); + } + + context.save(); + context.beginPath(); + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + + context.closePath(); + + context.clip(); + + // Compute matrix transform + const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); + const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); + const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); + const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); + const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); + const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); + const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); + + context.transform( + deltaA / delta, + deltaD / delta, + deltaB / delta, + deltaE / delta, + deltaC / delta, + deltaF / delta + ); + + context.drawImage( + textureSource, + 0, + 0, + textureWidth * base.resolution, + textureHeight * base.resolution, + 0, + 0, + textureWidth, + textureHeight + ); + + context.restore(); + } + + /** + * Renders a flat Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - The Mesh to render + */ + renderMeshFlat(mesh) + { + const context = this.renderer.context; + const vertices = mesh.vertices; + const length = vertices.length / 2; + + // this.count++; + + context.beginPath(); + + for (let i = 1; i < length - 2; ++i) + { + // draw some triangles! + const index = i * 2; + + const x0 = vertices[index]; + const y0 = vertices[index + 1]; + + const x1 = vertices[index + 2]; + const y1 = vertices[index + 3]; + + const x2 = vertices[index + 4]; + const y2 = vertices[index + 5]; + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + } + + context.fillStyle = '#FF0000'; + context.fill(); + context.closePath(); + } + + /** + * destroy the the renderer. + * + */ + destroy() + { + this.renderer = null; + } +} + +core.CanvasRenderer.registerPlugin('mesh', MeshSpriteRenderer); diff --git a/src/mesh/index.js b/src/mesh/index.js index 473d22a..4d1cad6 100644 --- a/src/mesh/index.js +++ b/src/mesh/index.js @@ -2,7 +2,8 @@ * @namespace PIXI.mesh */ export { default as Mesh } from './Mesh'; +export { default as MeshRenderer } from './webgl/MeshRenderer'; +export { default as CanvasMeshRenderer } from './canvas/CanvasMeshRenderer'; export { default as Plane } from './Plane'; export { default as NineSlicePlane } from './NineSlicePlane'; export { default as Rope } from './Rope'; -export { default as MeshShader } from './webgl/MeshShader'; diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js new file mode 100644 index 0000000..73d78e6 --- /dev/null +++ b/src/mesh/webgl/MeshRenderer.js @@ -0,0 +1,108 @@ +import * as core from '../../core'; +import glCore from 'pixi-gl-core'; +import { default as Mesh } from '../Mesh'; + +const glslify = require('glslify'); // eslint-disable-line no-undef + +/** + * WebGL renderer plugin for tiling sprites + */ +export class MeshRenderer extends core.ObjectRenderer { + + /** + * constructor for renderer + * + * @param {WebGLRenderer} renderer The renderer this tiling awesomeness works for. + */ + constructor(renderer) + { + super(renderer); + + this.shader = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + onContextChange() + { + const gl = this.renderer.gl; + + this.shader = new core.Shader(gl, + glslify('./mesh.vert'), + glslify('./mesh.frag')); + } + + /** + * renders mesh + * + * @param {PIXI.mesh.Mesh} mesh mesh instance + */ + render(mesh) + { + const renderer = this.renderer; + const gl = renderer.gl; + const texture = mesh._texture; + + if (!texture.valid) + { + return; + } + + let glData = mesh._glDatas[renderer.CONTEXT_UID]; + + if (!glData) + { + glData = { + shader: this.shader, + vertexBuffer: glCore.GLBuffer.createVertexBuffer(gl, mesh.vertices, gl.STREAM_DRAW), + uvBuffer: glCore.GLBuffer.createVertexBuffer(gl, mesh.uvs, gl.STREAM_DRAW), + indexBuffer: glCore.GLBuffer.createIndexBuffer(gl, mesh.indices, gl.STATIC_DRAW), + // build the vao object that will render.. + vao: new glCore.VertexArrayObject(gl), + dirty: mesh.dirty, + indexDirty: mesh.indexDirty, + }; + + // build the vao object that will render.. + glData.vao = new glCore.VertexArrayObject(gl) + .addIndex(glData.indexBuffer) + .addAttribute(glData.vertexBuffer, glData.shader.attributes.aVertexPosition, gl.FLOAT, false, 2 * 4, 0) + .addAttribute(glData.uvBuffer, glData.shader.attributes.aTextureCoord, gl.FLOAT, false, 2 * 4, 0); + + mesh._glDatas[renderer.CONTEXT_UID] = glData; + } + + if (mesh.dirty !== glData.dirty) + { + glData.dirty = mesh.dirty; + glData.uvBuffer.upload(); + } + + if (mesh.indexDirty !== glData.indexDirty) + { + glData.indexDirty = mesh.indexDirty; + glData.indexBuffer.upload(); + } + + glData.vertexBuffer.upload(); + + renderer.bindShader(glData.shader); + renderer.bindTexture(texture, 0); + renderer.state.setBlendMode(mesh.blendMode); + + glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); + glData.shader.uniforms.alpha = mesh.worldAlpha; + glData.shader.uniforms.tint = mesh.tintRgb; + + const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; + + glData.vao.bind() + .draw(drawMode, mesh.indices.length) + .unbind(); + } +} + +core.WebGLRenderer.registerPlugin('mesh', MeshRenderer); diff --git a/src/mesh/webgl/MeshShader.js b/src/mesh/webgl/MeshShader.js deleted file mode 100644 index 1bbdc0e..0000000 --- a/src/mesh/webgl/MeshShader.js +++ /dev/null @@ -1,46 +0,0 @@ -import Shader from '../../core/Shader'; - -/** - * @class - * @extends PIXI.Shader - * @memberof PIXI.mesh - */ -export default class MeshShader extends Shader -{ - /** - * @param {WebGLRenderingContext} gl - The WebGLRenderingContext. - */ - constructor(gl) - { - super( - gl, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'varying vec2 vTextureCoord;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', // eslint-disable-line max-len - ' vTextureCoord = aTextureCoord;', - '}', - ].join('\n'), - [ - 'varying vec2 vTextureCoord;', - 'uniform float alpha;', - 'uniform vec3 tint;', - - 'uniform sampler2D uSampler;', - - 'void main(void){', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha);', - // ' gl_FragColor = vec4(1.0);', - '}', - ].join('\n') - ); - } -} diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 455d59c..0ce75d2 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -18,6 +18,14 @@ * let sprite2 = new PIXI.Sprite(texture); * ``` * + * Textures made from SVGs, loaded or not, cannot be used before the file finishes processing. You can check for this by checking the sprite's _textureID property. + * ```js + * var texture = PIXI.Texture.fromImage('assets/image.svg'); + * var sprite1 = new PIXI.Sprite(texture); + * //sprite1._textureID should not be undefined if the texture has finished processing the SVG file + * ``` + * You can use a ticker or rAF to ensure your sprites load the finished textures after processing. See issue #3068. + * * @class * @extends EventEmitter * @memberof PIXI diff --git a/src/extras/getGlobalPosition.js b/src/extras/getGlobalPosition.js index b71682f..a23761f 100644 --- a/src/extras/getGlobalPosition.js +++ b/src/extras/getGlobalPosition.js @@ -1,20 +1,20 @@ import * as core from '../core'; /** - * Returns the global position of the displayObject + * Returns the global position of the displayObject. Does not depend on object scale, rotation and pivot. * * @memberof PIXI.DisplayObject# * @param {Point} point - the point to write the global value to. If null a new point will be returned + * @param {boolean} skipUpdate - setting to true will stop the transforms of the scene graph from + * being updated. This means the calculation returned MAY be out of date BUT will give you a + * nice performance boost * @return {Point} The updated point */ -core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point()) +core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point(), skipUpdate = false) { if (this.parent) { - this.displayObjectUpdateTransform(); - - point.x = this.worldTransform.tx; - point.y = this.worldTransform.ty; + this.parent.toGlobal(this.position, point, skipUpdate); } else { diff --git a/src/filters/blur/BlurFilter.js b/src/filters/blur/BlurFilter.js index 4919d10..5b3c3a9 100644 --- a/src/filters/blur/BlurFilter.js +++ b/src/filters/blur/BlurFilter.js @@ -68,7 +68,7 @@ set blur(value) { this.blurXFilter.blur = this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -113,7 +113,7 @@ set blurX(value) { this.blurXFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -136,6 +136,6 @@ set blurY(value) { this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } } diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 4719b6a..2ced6dc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,6 +1,4 @@ import * as core from '../core'; -import glCore from 'pixi-gl-core'; -import Shader from './webgl/MeshShader'; const tempPoint = new core.Point(); const tempPolygon = new core.Polygon(); @@ -59,11 +57,17 @@ this.indices = indices || new Uint16Array([0, 1, 3, 2]); /** - * Whether the Mesh is dirty or not + * Version of mesh uvs are dirty or not * * @member {number} */ this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ this.indexDirty = 0; /** @@ -122,62 +126,8 @@ */ _renderWebGL(renderer) { - // get rid of any thing that may be batching. - renderer.flush(); - - // renderer.plugins.mesh.render(this); - const gl = renderer.gl; - let glData = this._glDatas[renderer.CONTEXT_UID]; - - if (!glData) - { - glData = { - shader: new Shader(gl), - vertexBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.vertices, gl.STREAM_DRAW), - uvBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.uvs, gl.STREAM_DRAW), - indexBuffer: glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW), - // build the vao object that will render.. - vao: new glCore.VertexArrayObject(gl), - dirty: this.dirty, - indexDirty: this.indexDirty, - }; - - // build the vao object that will render.. - glData.vao = new glCore.VertexArrayObject(gl) - .addIndex(glData.indexBuffer) - .addAttribute(glData.vertexBuffer, glData.shader.attributes.aVertexPosition, gl.FLOAT, false, 2 * 4, 0) - .addAttribute(glData.uvBuffer, glData.shader.attributes.aTextureCoord, gl.FLOAT, false, 2 * 4, 0); - - this._glDatas[renderer.CONTEXT_UID] = glData; - } - - if (this.dirty !== glData.dirty) - { - glData.dirty = this.dirty; - glData.uvBuffer.upload(); - } - - if (this.indexDirty !== glData.indexDirty) - { - glData.indexDirty = this.indexDirty; - glData.indexBuffer.upload(); - } - - glData.vertexBuffer.upload(); - - renderer.bindShader(glData.shader); - renderer.bindTexture(this._texture, 0); - renderer.state.setBlendMode(this.blendMode); - - glData.shader.uniforms.translationMatrix = this.worldTransform.toArray(true); - glData.shader.uniforms.alpha = this.worldAlpha; - glData.shader.uniforms.tint = this.tintRgb; - - const drawMode = this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; - - glData.vao.bind() - .draw(drawMode, this.indices.length) - .unbind(); + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); } /** @@ -188,240 +138,7 @@ */ _renderCanvas(renderer) { - const context = renderer.context; - - const transform = this.worldTransform; - const res = renderer.resolution; - - if (renderer.roundPixels) - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - (transform.tx * res) | 0, - (transform.ty * res) | 0 - ); - } - else - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - transform.tx * res, - transform.ty * res - ); - } - - if (this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) - { - this._renderCanvasTriangleMesh(context); - } - else - { - this._renderCanvasTriangles(context); - } - } - - /** - * Draws the object in Triangle Mesh mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - The current drawing context - */ - _renderCanvasTriangleMesh(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const length = vertices.length / 2; - - // this.count++; - - for (let i = 0; i < length - 2; i++) - { - // draw some triangles! - const index = i * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index, (index + 2), (index + 4)); - } - } - - /** - * Draws the object in triangle mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - */ - _renderCanvasTriangles(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const indices = this.indices; - const length = indices.length; - // this.count++; - - for (let i = 0; i < length; i += 3) - { - // draw some triangles! - const index0 = indices[i] * 2; - const index1 = indices[i + 1] * 2; - const index2 = indices[i + 2] * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2); - } - } - - /** - * Draws one of the triangles that form this Mesh - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - * @param {Float32Array} vertices - a reference to the vertices of the Mesh - * @param {Float32Array} uvs - a reference to the uvs of the Mesh - * @param {number} index0 - the index of the first vertex - * @param {number} index1 - the index of the second vertex - * @param {number} index2 - the index of the third vertex - */ - _renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2) - { - const base = this._texture.baseTexture; - const textureSource = base.source; - const textureWidth = base.width; - const textureHeight = base.height; - - const u0 = uvs[index0] * base.width; - const u1 = uvs[index1] * base.width; - const u2 = uvs[index2] * base.width; - const v0 = uvs[index0 + 1] * base.height; - const v1 = uvs[index1 + 1] * base.height; - const v2 = uvs[index2 + 1] * base.height; - - let x0 = vertices[index0]; - let x1 = vertices[index1]; - let x2 = vertices[index2]; - let y0 = vertices[index0 + 1]; - let y1 = vertices[index1 + 1]; - let y2 = vertices[index2 + 1]; - - if (this.canvasPadding > 0) - { - const paddingX = this.canvasPadding / this.worldTransform.a; - const paddingY = this.canvasPadding / this.worldTransform.d; - const centerX = (x0 + x1 + x2) / 3; - const centerY = (y0 + y1 + y2) / 3; - - let normX = x0 - centerX; - let normY = y0 - centerY; - - let dist = Math.sqrt((normX * normX) + (normY * normY)); - - x0 = centerX + ((normX / dist) * (dist + paddingX)); - y0 = centerY + ((normY / dist) * (dist + paddingY)); - - // - - normX = x1 - centerX; - normY = y1 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x1 = centerX + ((normX / dist) * (dist + paddingX)); - y1 = centerY + ((normY / dist) * (dist + paddingY)); - - normX = x2 - centerX; - normY = y2 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x2 = centerX + ((normX / dist) * (dist + paddingX)); - y2 = centerY + ((normY / dist) * (dist + paddingY)); - } - - context.save(); - context.beginPath(); - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - - context.closePath(); - - context.clip(); - - // Compute matrix transform - const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); - const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); - const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); - const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); - const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); - const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); - const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); - - context.transform( - deltaA / delta, - deltaD / delta, - deltaB / delta, - deltaE / delta, - deltaC / delta, - deltaF / delta - ); - - context.drawImage( - textureSource, - 0, - 0, - textureWidth * base.resolution, - textureHeight * base.resolution, - 0, - 0, - textureWidth, - textureHeight - ); - - context.restore(); - } - - /** - * Renders a flat Mesh - * - * @private - * @param {PIXI.mesh.Mesh} mesh - The Mesh to render - */ - renderMeshFlat(mesh) - { - const context = this.context; - const vertices = mesh.vertices; - const length = vertices.length / 2; - - // this.count++; - - context.beginPath(); - - for (let i = 1; i < length - 2; ++i) - { - // draw some triangles! - const index = i * 2; - - const x0 = vertices[index]; - const y0 = vertices[index + 1]; - - const x1 = vertices[index + 2]; - const y1 = vertices[index + 3]; - - const x2 = vertices[index + 4]; - const y2 = vertices[index + 5]; - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - } - - context.fillStyle = '#FF0000'; - context.fill(); - context.closePath(); + renderer.plugins.mesh.render(this); } /** diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js new file mode 100644 index 0000000..a1ad74c --- /dev/null +++ b/src/mesh/canvas/CanvasMeshRenderer.js @@ -0,0 +1,276 @@ +import * as core from '../../core'; +import { default as Mesh } from '../Mesh'; + +/** + * Renderer dedicated to meshes. + * + * @class + * @private + * @memberof PIXI + */ +export default class MeshSpriteRenderer +{ + /** + * @param {PIXI.CanvasRenderer} renderer - The renderer this downport works for + */ + constructor(renderer) + { + this.renderer = renderer; + } + + /** + * Renders the Mesh + * + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + render(mesh) + { + const renderer = this.renderer; + const context = renderer.context; + + const transform = mesh.worldTransform; + const res = renderer.resolution; + + if (renderer.roundPixels) + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + (transform.tx * res) | 0, + (transform.ty * res) | 0 + ); + } + else + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + transform.tx * res, + transform.ty * res + ); + } + + if (mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) + { + this._renderTriangleMesh(mesh); + } + else + { + this._renderTriangles(mesh); + } + } + + /** + * Draws the object in Triangle Mesh mode + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + _renderTriangleMesh(mesh) + { + // draw triangles!! + const length = mesh.vertices.length / 2; + + for (let i = 0; i < length - 2; i++) + { + // draw some triangles! + const index = i * 2; + + this._renderDrawTriangle(mesh, index, (index + 2), (index + 4)); + } + } + + /** + * Draws the object in triangle mode using canvas + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + */ + _renderTriangles(mesh) + { + // draw triangles!! + const indices = mesh.indices; + const length = indices.length; + + for (let i = 0; i < length; i += 3) + { + // draw some triangles! + const index0 = indices[i] * 2; + const index1 = indices[i + 1] * 2; + const index2 = indices[i + 2] * 2; + + this._renderDrawTriangle(mesh, index0, index1, index2); + } + } + + /** + * Draws one of the triangles that from the Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + * @param {number} index0 - the index of the first vertex + * @param {number} index1 - the index of the second vertex + * @param {number} index2 - the index of the third vertex + */ + _renderDrawTriangle(mesh, index0, index1, index2) + { + const context = this.renderer.context; + const uvs = mesh.uvs; + const vertices = mesh.vertices; + const texture = mesh._texture; + + if (!texture.valid) + { + return; + } + + const base = texture.baseTexture; + const textureSource = base.source; + const textureWidth = base.width; + const textureHeight = base.height; + + const u0 = uvs[index0] * base.width; + const u1 = uvs[index1] * base.width; + const u2 = uvs[index2] * base.width; + const v0 = uvs[index0 + 1] * base.height; + const v1 = uvs[index1 + 1] * base.height; + const v2 = uvs[index2 + 1] * base.height; + + let x0 = vertices[index0]; + let x1 = vertices[index1]; + let x2 = vertices[index2]; + let y0 = vertices[index0 + 1]; + let y1 = vertices[index1 + 1]; + let y2 = vertices[index2 + 1]; + + if (mesh.canvasPadding > 0) + { + const paddingX = mesh.canvasPadding / mesh.worldTransform.a; + const paddingY = mesh.canvasPadding / mesh.worldTransform.d; + const centerX = (x0 + x1 + x2) / 3; + const centerY = (y0 + y1 + y2) / 3; + + let normX = x0 - centerX; + let normY = y0 - centerY; + + let dist = Math.sqrt((normX * normX) + (normY * normY)); + + x0 = centerX + ((normX / dist) * (dist + paddingX)); + y0 = centerY + ((normY / dist) * (dist + paddingY)); + + // + + normX = x1 - centerX; + normY = y1 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x1 = centerX + ((normX / dist) * (dist + paddingX)); + y1 = centerY + ((normY / dist) * (dist + paddingY)); + + normX = x2 - centerX; + normY = y2 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x2 = centerX + ((normX / dist) * (dist + paddingX)); + y2 = centerY + ((normY / dist) * (dist + paddingY)); + } + + context.save(); + context.beginPath(); + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + + context.closePath(); + + context.clip(); + + // Compute matrix transform + const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); + const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); + const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); + const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); + const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); + const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); + const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); + + context.transform( + deltaA / delta, + deltaD / delta, + deltaB / delta, + deltaE / delta, + deltaC / delta, + deltaF / delta + ); + + context.drawImage( + textureSource, + 0, + 0, + textureWidth * base.resolution, + textureHeight * base.resolution, + 0, + 0, + textureWidth, + textureHeight + ); + + context.restore(); + } + + /** + * Renders a flat Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - The Mesh to render + */ + renderMeshFlat(mesh) + { + const context = this.renderer.context; + const vertices = mesh.vertices; + const length = vertices.length / 2; + + // this.count++; + + context.beginPath(); + + for (let i = 1; i < length - 2; ++i) + { + // draw some triangles! + const index = i * 2; + + const x0 = vertices[index]; + const y0 = vertices[index + 1]; + + const x1 = vertices[index + 2]; + const y1 = vertices[index + 3]; + + const x2 = vertices[index + 4]; + const y2 = vertices[index + 5]; + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + } + + context.fillStyle = '#FF0000'; + context.fill(); + context.closePath(); + } + + /** + * destroy the the renderer. + * + */ + destroy() + { + this.renderer = null; + } +} + +core.CanvasRenderer.registerPlugin('mesh', MeshSpriteRenderer); diff --git a/src/mesh/index.js b/src/mesh/index.js index 473d22a..4d1cad6 100644 --- a/src/mesh/index.js +++ b/src/mesh/index.js @@ -2,7 +2,8 @@ * @namespace PIXI.mesh */ export { default as Mesh } from './Mesh'; +export { default as MeshRenderer } from './webgl/MeshRenderer'; +export { default as CanvasMeshRenderer } from './canvas/CanvasMeshRenderer'; export { default as Plane } from './Plane'; export { default as NineSlicePlane } from './NineSlicePlane'; export { default as Rope } from './Rope'; -export { default as MeshShader } from './webgl/MeshShader'; diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js new file mode 100644 index 0000000..73d78e6 --- /dev/null +++ b/src/mesh/webgl/MeshRenderer.js @@ -0,0 +1,108 @@ +import * as core from '../../core'; +import glCore from 'pixi-gl-core'; +import { default as Mesh } from '../Mesh'; + +const glslify = require('glslify'); // eslint-disable-line no-undef + +/** + * WebGL renderer plugin for tiling sprites + */ +export class MeshRenderer extends core.ObjectRenderer { + + /** + * constructor for renderer + * + * @param {WebGLRenderer} renderer The renderer this tiling awesomeness works for. + */ + constructor(renderer) + { + super(renderer); + + this.shader = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + onContextChange() + { + const gl = this.renderer.gl; + + this.shader = new core.Shader(gl, + glslify('./mesh.vert'), + glslify('./mesh.frag')); + } + + /** + * renders mesh + * + * @param {PIXI.mesh.Mesh} mesh mesh instance + */ + render(mesh) + { + const renderer = this.renderer; + const gl = renderer.gl; + const texture = mesh._texture; + + if (!texture.valid) + { + return; + } + + let glData = mesh._glDatas[renderer.CONTEXT_UID]; + + if (!glData) + { + glData = { + shader: this.shader, + vertexBuffer: glCore.GLBuffer.createVertexBuffer(gl, mesh.vertices, gl.STREAM_DRAW), + uvBuffer: glCore.GLBuffer.createVertexBuffer(gl, mesh.uvs, gl.STREAM_DRAW), + indexBuffer: glCore.GLBuffer.createIndexBuffer(gl, mesh.indices, gl.STATIC_DRAW), + // build the vao object that will render.. + vao: new glCore.VertexArrayObject(gl), + dirty: mesh.dirty, + indexDirty: mesh.indexDirty, + }; + + // build the vao object that will render.. + glData.vao = new glCore.VertexArrayObject(gl) + .addIndex(glData.indexBuffer) + .addAttribute(glData.vertexBuffer, glData.shader.attributes.aVertexPosition, gl.FLOAT, false, 2 * 4, 0) + .addAttribute(glData.uvBuffer, glData.shader.attributes.aTextureCoord, gl.FLOAT, false, 2 * 4, 0); + + mesh._glDatas[renderer.CONTEXT_UID] = glData; + } + + if (mesh.dirty !== glData.dirty) + { + glData.dirty = mesh.dirty; + glData.uvBuffer.upload(); + } + + if (mesh.indexDirty !== glData.indexDirty) + { + glData.indexDirty = mesh.indexDirty; + glData.indexBuffer.upload(); + } + + glData.vertexBuffer.upload(); + + renderer.bindShader(glData.shader); + renderer.bindTexture(texture, 0); + renderer.state.setBlendMode(mesh.blendMode); + + glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); + glData.shader.uniforms.alpha = mesh.worldAlpha; + glData.shader.uniforms.tint = mesh.tintRgb; + + const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; + + glData.vao.bind() + .draw(drawMode, mesh.indices.length) + .unbind(); + } +} + +core.WebGLRenderer.registerPlugin('mesh', MeshRenderer); diff --git a/src/mesh/webgl/MeshShader.js b/src/mesh/webgl/MeshShader.js deleted file mode 100644 index 1bbdc0e..0000000 --- a/src/mesh/webgl/MeshShader.js +++ /dev/null @@ -1,46 +0,0 @@ -import Shader from '../../core/Shader'; - -/** - * @class - * @extends PIXI.Shader - * @memberof PIXI.mesh - */ -export default class MeshShader extends Shader -{ - /** - * @param {WebGLRenderingContext} gl - The WebGLRenderingContext. - */ - constructor(gl) - { - super( - gl, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'varying vec2 vTextureCoord;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', // eslint-disable-line max-len - ' vTextureCoord = aTextureCoord;', - '}', - ].join('\n'), - [ - 'varying vec2 vTextureCoord;', - 'uniform float alpha;', - 'uniform vec3 tint;', - - 'uniform sampler2D uSampler;', - - 'void main(void){', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha);', - // ' gl_FragColor = vec4(1.0);', - '}', - ].join('\n') - ); - } -} diff --git a/src/mesh/webgl/mesh.frag b/src/mesh/webgl/mesh.frag new file mode 100644 index 0000000..9e0b634 --- /dev/null +++ b/src/mesh/webgl/mesh.frag @@ -0,0 +1,10 @@ +varying vec2 vTextureCoord; +uniform float alpha; +uniform vec3 tint; + +uniform sampler2D uSampler; + +void main(void) +{ + gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha); +} diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 455d59c..0ce75d2 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -18,6 +18,14 @@ * let sprite2 = new PIXI.Sprite(texture); * ``` * + * Textures made from SVGs, loaded or not, cannot be used before the file finishes processing. You can check for this by checking the sprite's _textureID property. + * ```js + * var texture = PIXI.Texture.fromImage('assets/image.svg'); + * var sprite1 = new PIXI.Sprite(texture); + * //sprite1._textureID should not be undefined if the texture has finished processing the SVG file + * ``` + * You can use a ticker or rAF to ensure your sprites load the finished textures after processing. See issue #3068. + * * @class * @extends EventEmitter * @memberof PIXI diff --git a/src/extras/getGlobalPosition.js b/src/extras/getGlobalPosition.js index b71682f..a23761f 100644 --- a/src/extras/getGlobalPosition.js +++ b/src/extras/getGlobalPosition.js @@ -1,20 +1,20 @@ import * as core from '../core'; /** - * Returns the global position of the displayObject + * Returns the global position of the displayObject. Does not depend on object scale, rotation and pivot. * * @memberof PIXI.DisplayObject# * @param {Point} point - the point to write the global value to. If null a new point will be returned + * @param {boolean} skipUpdate - setting to true will stop the transforms of the scene graph from + * being updated. This means the calculation returned MAY be out of date BUT will give you a + * nice performance boost * @return {Point} The updated point */ -core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point()) +core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point(), skipUpdate = false) { if (this.parent) { - this.displayObjectUpdateTransform(); - - point.x = this.worldTransform.tx; - point.y = this.worldTransform.ty; + this.parent.toGlobal(this.position, point, skipUpdate); } else { diff --git a/src/filters/blur/BlurFilter.js b/src/filters/blur/BlurFilter.js index 4919d10..5b3c3a9 100644 --- a/src/filters/blur/BlurFilter.js +++ b/src/filters/blur/BlurFilter.js @@ -68,7 +68,7 @@ set blur(value) { this.blurXFilter.blur = this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -113,7 +113,7 @@ set blurX(value) { this.blurXFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -136,6 +136,6 @@ set blurY(value) { this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } } diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 4719b6a..2ced6dc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,6 +1,4 @@ import * as core from '../core'; -import glCore from 'pixi-gl-core'; -import Shader from './webgl/MeshShader'; const tempPoint = new core.Point(); const tempPolygon = new core.Polygon(); @@ -59,11 +57,17 @@ this.indices = indices || new Uint16Array([0, 1, 3, 2]); /** - * Whether the Mesh is dirty or not + * Version of mesh uvs are dirty or not * * @member {number} */ this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ this.indexDirty = 0; /** @@ -122,62 +126,8 @@ */ _renderWebGL(renderer) { - // get rid of any thing that may be batching. - renderer.flush(); - - // renderer.plugins.mesh.render(this); - const gl = renderer.gl; - let glData = this._glDatas[renderer.CONTEXT_UID]; - - if (!glData) - { - glData = { - shader: new Shader(gl), - vertexBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.vertices, gl.STREAM_DRAW), - uvBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.uvs, gl.STREAM_DRAW), - indexBuffer: glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW), - // build the vao object that will render.. - vao: new glCore.VertexArrayObject(gl), - dirty: this.dirty, - indexDirty: this.indexDirty, - }; - - // build the vao object that will render.. - glData.vao = new glCore.VertexArrayObject(gl) - .addIndex(glData.indexBuffer) - .addAttribute(glData.vertexBuffer, glData.shader.attributes.aVertexPosition, gl.FLOAT, false, 2 * 4, 0) - .addAttribute(glData.uvBuffer, glData.shader.attributes.aTextureCoord, gl.FLOAT, false, 2 * 4, 0); - - this._glDatas[renderer.CONTEXT_UID] = glData; - } - - if (this.dirty !== glData.dirty) - { - glData.dirty = this.dirty; - glData.uvBuffer.upload(); - } - - if (this.indexDirty !== glData.indexDirty) - { - glData.indexDirty = this.indexDirty; - glData.indexBuffer.upload(); - } - - glData.vertexBuffer.upload(); - - renderer.bindShader(glData.shader); - renderer.bindTexture(this._texture, 0); - renderer.state.setBlendMode(this.blendMode); - - glData.shader.uniforms.translationMatrix = this.worldTransform.toArray(true); - glData.shader.uniforms.alpha = this.worldAlpha; - glData.shader.uniforms.tint = this.tintRgb; - - const drawMode = this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; - - glData.vao.bind() - .draw(drawMode, this.indices.length) - .unbind(); + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); } /** @@ -188,240 +138,7 @@ */ _renderCanvas(renderer) { - const context = renderer.context; - - const transform = this.worldTransform; - const res = renderer.resolution; - - if (renderer.roundPixels) - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - (transform.tx * res) | 0, - (transform.ty * res) | 0 - ); - } - else - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - transform.tx * res, - transform.ty * res - ); - } - - if (this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) - { - this._renderCanvasTriangleMesh(context); - } - else - { - this._renderCanvasTriangles(context); - } - } - - /** - * Draws the object in Triangle Mesh mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - The current drawing context - */ - _renderCanvasTriangleMesh(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const length = vertices.length / 2; - - // this.count++; - - for (let i = 0; i < length - 2; i++) - { - // draw some triangles! - const index = i * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index, (index + 2), (index + 4)); - } - } - - /** - * Draws the object in triangle mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - */ - _renderCanvasTriangles(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const indices = this.indices; - const length = indices.length; - // this.count++; - - for (let i = 0; i < length; i += 3) - { - // draw some triangles! - const index0 = indices[i] * 2; - const index1 = indices[i + 1] * 2; - const index2 = indices[i + 2] * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2); - } - } - - /** - * Draws one of the triangles that form this Mesh - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - * @param {Float32Array} vertices - a reference to the vertices of the Mesh - * @param {Float32Array} uvs - a reference to the uvs of the Mesh - * @param {number} index0 - the index of the first vertex - * @param {number} index1 - the index of the second vertex - * @param {number} index2 - the index of the third vertex - */ - _renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2) - { - const base = this._texture.baseTexture; - const textureSource = base.source; - const textureWidth = base.width; - const textureHeight = base.height; - - const u0 = uvs[index0] * base.width; - const u1 = uvs[index1] * base.width; - const u2 = uvs[index2] * base.width; - const v0 = uvs[index0 + 1] * base.height; - const v1 = uvs[index1 + 1] * base.height; - const v2 = uvs[index2 + 1] * base.height; - - let x0 = vertices[index0]; - let x1 = vertices[index1]; - let x2 = vertices[index2]; - let y0 = vertices[index0 + 1]; - let y1 = vertices[index1 + 1]; - let y2 = vertices[index2 + 1]; - - if (this.canvasPadding > 0) - { - const paddingX = this.canvasPadding / this.worldTransform.a; - const paddingY = this.canvasPadding / this.worldTransform.d; - const centerX = (x0 + x1 + x2) / 3; - const centerY = (y0 + y1 + y2) / 3; - - let normX = x0 - centerX; - let normY = y0 - centerY; - - let dist = Math.sqrt((normX * normX) + (normY * normY)); - - x0 = centerX + ((normX / dist) * (dist + paddingX)); - y0 = centerY + ((normY / dist) * (dist + paddingY)); - - // - - normX = x1 - centerX; - normY = y1 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x1 = centerX + ((normX / dist) * (dist + paddingX)); - y1 = centerY + ((normY / dist) * (dist + paddingY)); - - normX = x2 - centerX; - normY = y2 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x2 = centerX + ((normX / dist) * (dist + paddingX)); - y2 = centerY + ((normY / dist) * (dist + paddingY)); - } - - context.save(); - context.beginPath(); - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - - context.closePath(); - - context.clip(); - - // Compute matrix transform - const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); - const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); - const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); - const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); - const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); - const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); - const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); - - context.transform( - deltaA / delta, - deltaD / delta, - deltaB / delta, - deltaE / delta, - deltaC / delta, - deltaF / delta - ); - - context.drawImage( - textureSource, - 0, - 0, - textureWidth * base.resolution, - textureHeight * base.resolution, - 0, - 0, - textureWidth, - textureHeight - ); - - context.restore(); - } - - /** - * Renders a flat Mesh - * - * @private - * @param {PIXI.mesh.Mesh} mesh - The Mesh to render - */ - renderMeshFlat(mesh) - { - const context = this.context; - const vertices = mesh.vertices; - const length = vertices.length / 2; - - // this.count++; - - context.beginPath(); - - for (let i = 1; i < length - 2; ++i) - { - // draw some triangles! - const index = i * 2; - - const x0 = vertices[index]; - const y0 = vertices[index + 1]; - - const x1 = vertices[index + 2]; - const y1 = vertices[index + 3]; - - const x2 = vertices[index + 4]; - const y2 = vertices[index + 5]; - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - } - - context.fillStyle = '#FF0000'; - context.fill(); - context.closePath(); + renderer.plugins.mesh.render(this); } /** diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js new file mode 100644 index 0000000..a1ad74c --- /dev/null +++ b/src/mesh/canvas/CanvasMeshRenderer.js @@ -0,0 +1,276 @@ +import * as core from '../../core'; +import { default as Mesh } from '../Mesh'; + +/** + * Renderer dedicated to meshes. + * + * @class + * @private + * @memberof PIXI + */ +export default class MeshSpriteRenderer +{ + /** + * @param {PIXI.CanvasRenderer} renderer - The renderer this downport works for + */ + constructor(renderer) + { + this.renderer = renderer; + } + + /** + * Renders the Mesh + * + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + render(mesh) + { + const renderer = this.renderer; + const context = renderer.context; + + const transform = mesh.worldTransform; + const res = renderer.resolution; + + if (renderer.roundPixels) + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + (transform.tx * res) | 0, + (transform.ty * res) | 0 + ); + } + else + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + transform.tx * res, + transform.ty * res + ); + } + + if (mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) + { + this._renderTriangleMesh(mesh); + } + else + { + this._renderTriangles(mesh); + } + } + + /** + * Draws the object in Triangle Mesh mode + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + _renderTriangleMesh(mesh) + { + // draw triangles!! + const length = mesh.vertices.length / 2; + + for (let i = 0; i < length - 2; i++) + { + // draw some triangles! + const index = i * 2; + + this._renderDrawTriangle(mesh, index, (index + 2), (index + 4)); + } + } + + /** + * Draws the object in triangle mode using canvas + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + */ + _renderTriangles(mesh) + { + // draw triangles!! + const indices = mesh.indices; + const length = indices.length; + + for (let i = 0; i < length; i += 3) + { + // draw some triangles! + const index0 = indices[i] * 2; + const index1 = indices[i + 1] * 2; + const index2 = indices[i + 2] * 2; + + this._renderDrawTriangle(mesh, index0, index1, index2); + } + } + + /** + * Draws one of the triangles that from the Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + * @param {number} index0 - the index of the first vertex + * @param {number} index1 - the index of the second vertex + * @param {number} index2 - the index of the third vertex + */ + _renderDrawTriangle(mesh, index0, index1, index2) + { + const context = this.renderer.context; + const uvs = mesh.uvs; + const vertices = mesh.vertices; + const texture = mesh._texture; + + if (!texture.valid) + { + return; + } + + const base = texture.baseTexture; + const textureSource = base.source; + const textureWidth = base.width; + const textureHeight = base.height; + + const u0 = uvs[index0] * base.width; + const u1 = uvs[index1] * base.width; + const u2 = uvs[index2] * base.width; + const v0 = uvs[index0 + 1] * base.height; + const v1 = uvs[index1 + 1] * base.height; + const v2 = uvs[index2 + 1] * base.height; + + let x0 = vertices[index0]; + let x1 = vertices[index1]; + let x2 = vertices[index2]; + let y0 = vertices[index0 + 1]; + let y1 = vertices[index1 + 1]; + let y2 = vertices[index2 + 1]; + + if (mesh.canvasPadding > 0) + { + const paddingX = mesh.canvasPadding / mesh.worldTransform.a; + const paddingY = mesh.canvasPadding / mesh.worldTransform.d; + const centerX = (x0 + x1 + x2) / 3; + const centerY = (y0 + y1 + y2) / 3; + + let normX = x0 - centerX; + let normY = y0 - centerY; + + let dist = Math.sqrt((normX * normX) + (normY * normY)); + + x0 = centerX + ((normX / dist) * (dist + paddingX)); + y0 = centerY + ((normY / dist) * (dist + paddingY)); + + // + + normX = x1 - centerX; + normY = y1 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x1 = centerX + ((normX / dist) * (dist + paddingX)); + y1 = centerY + ((normY / dist) * (dist + paddingY)); + + normX = x2 - centerX; + normY = y2 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x2 = centerX + ((normX / dist) * (dist + paddingX)); + y2 = centerY + ((normY / dist) * (dist + paddingY)); + } + + context.save(); + context.beginPath(); + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + + context.closePath(); + + context.clip(); + + // Compute matrix transform + const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); + const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); + const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); + const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); + const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); + const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); + const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); + + context.transform( + deltaA / delta, + deltaD / delta, + deltaB / delta, + deltaE / delta, + deltaC / delta, + deltaF / delta + ); + + context.drawImage( + textureSource, + 0, + 0, + textureWidth * base.resolution, + textureHeight * base.resolution, + 0, + 0, + textureWidth, + textureHeight + ); + + context.restore(); + } + + /** + * Renders a flat Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - The Mesh to render + */ + renderMeshFlat(mesh) + { + const context = this.renderer.context; + const vertices = mesh.vertices; + const length = vertices.length / 2; + + // this.count++; + + context.beginPath(); + + for (let i = 1; i < length - 2; ++i) + { + // draw some triangles! + const index = i * 2; + + const x0 = vertices[index]; + const y0 = vertices[index + 1]; + + const x1 = vertices[index + 2]; + const y1 = vertices[index + 3]; + + const x2 = vertices[index + 4]; + const y2 = vertices[index + 5]; + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + } + + context.fillStyle = '#FF0000'; + context.fill(); + context.closePath(); + } + + /** + * destroy the the renderer. + * + */ + destroy() + { + this.renderer = null; + } +} + +core.CanvasRenderer.registerPlugin('mesh', MeshSpriteRenderer); diff --git a/src/mesh/index.js b/src/mesh/index.js index 473d22a..4d1cad6 100644 --- a/src/mesh/index.js +++ b/src/mesh/index.js @@ -2,7 +2,8 @@ * @namespace PIXI.mesh */ export { default as Mesh } from './Mesh'; +export { default as MeshRenderer } from './webgl/MeshRenderer'; +export { default as CanvasMeshRenderer } from './canvas/CanvasMeshRenderer'; export { default as Plane } from './Plane'; export { default as NineSlicePlane } from './NineSlicePlane'; export { default as Rope } from './Rope'; -export { default as MeshShader } from './webgl/MeshShader'; diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js new file mode 100644 index 0000000..73d78e6 --- /dev/null +++ b/src/mesh/webgl/MeshRenderer.js @@ -0,0 +1,108 @@ +import * as core from '../../core'; +import glCore from 'pixi-gl-core'; +import { default as Mesh } from '../Mesh'; + +const glslify = require('glslify'); // eslint-disable-line no-undef + +/** + * WebGL renderer plugin for tiling sprites + */ +export class MeshRenderer extends core.ObjectRenderer { + + /** + * constructor for renderer + * + * @param {WebGLRenderer} renderer The renderer this tiling awesomeness works for. + */ + constructor(renderer) + { + super(renderer); + + this.shader = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + onContextChange() + { + const gl = this.renderer.gl; + + this.shader = new core.Shader(gl, + glslify('./mesh.vert'), + glslify('./mesh.frag')); + } + + /** + * renders mesh + * + * @param {PIXI.mesh.Mesh} mesh mesh instance + */ + render(mesh) + { + const renderer = this.renderer; + const gl = renderer.gl; + const texture = mesh._texture; + + if (!texture.valid) + { + return; + } + + let glData = mesh._glDatas[renderer.CONTEXT_UID]; + + if (!glData) + { + glData = { + shader: this.shader, + vertexBuffer: glCore.GLBuffer.createVertexBuffer(gl, mesh.vertices, gl.STREAM_DRAW), + uvBuffer: glCore.GLBuffer.createVertexBuffer(gl, mesh.uvs, gl.STREAM_DRAW), + indexBuffer: glCore.GLBuffer.createIndexBuffer(gl, mesh.indices, gl.STATIC_DRAW), + // build the vao object that will render.. + vao: new glCore.VertexArrayObject(gl), + dirty: mesh.dirty, + indexDirty: mesh.indexDirty, + }; + + // build the vao object that will render.. + glData.vao = new glCore.VertexArrayObject(gl) + .addIndex(glData.indexBuffer) + .addAttribute(glData.vertexBuffer, glData.shader.attributes.aVertexPosition, gl.FLOAT, false, 2 * 4, 0) + .addAttribute(glData.uvBuffer, glData.shader.attributes.aTextureCoord, gl.FLOAT, false, 2 * 4, 0); + + mesh._glDatas[renderer.CONTEXT_UID] = glData; + } + + if (mesh.dirty !== glData.dirty) + { + glData.dirty = mesh.dirty; + glData.uvBuffer.upload(); + } + + if (mesh.indexDirty !== glData.indexDirty) + { + glData.indexDirty = mesh.indexDirty; + glData.indexBuffer.upload(); + } + + glData.vertexBuffer.upload(); + + renderer.bindShader(glData.shader); + renderer.bindTexture(texture, 0); + renderer.state.setBlendMode(mesh.blendMode); + + glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); + glData.shader.uniforms.alpha = mesh.worldAlpha; + glData.shader.uniforms.tint = mesh.tintRgb; + + const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; + + glData.vao.bind() + .draw(drawMode, mesh.indices.length) + .unbind(); + } +} + +core.WebGLRenderer.registerPlugin('mesh', MeshRenderer); diff --git a/src/mesh/webgl/MeshShader.js b/src/mesh/webgl/MeshShader.js deleted file mode 100644 index 1bbdc0e..0000000 --- a/src/mesh/webgl/MeshShader.js +++ /dev/null @@ -1,46 +0,0 @@ -import Shader from '../../core/Shader'; - -/** - * @class - * @extends PIXI.Shader - * @memberof PIXI.mesh - */ -export default class MeshShader extends Shader -{ - /** - * @param {WebGLRenderingContext} gl - The WebGLRenderingContext. - */ - constructor(gl) - { - super( - gl, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'varying vec2 vTextureCoord;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', // eslint-disable-line max-len - ' vTextureCoord = aTextureCoord;', - '}', - ].join('\n'), - [ - 'varying vec2 vTextureCoord;', - 'uniform float alpha;', - 'uniform vec3 tint;', - - 'uniform sampler2D uSampler;', - - 'void main(void){', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha);', - // ' gl_FragColor = vec4(1.0);', - '}', - ].join('\n') - ); - } -} diff --git a/src/mesh/webgl/mesh.frag b/src/mesh/webgl/mesh.frag new file mode 100644 index 0000000..9e0b634 --- /dev/null +++ b/src/mesh/webgl/mesh.frag @@ -0,0 +1,10 @@ +varying vec2 vTextureCoord; +uniform float alpha; +uniform vec3 tint; + +uniform sampler2D uSampler; + +void main(void) +{ + gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha); +} diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert new file mode 100644 index 0000000..a337aef --- /dev/null +++ b/src/mesh/webgl/mesh.vert @@ -0,0 +1,14 @@ +attribute vec2 aVertexPosition; +attribute vec2 aTextureCoord; + +uniform mat3 translationMatrix; +uniform mat3 projectionMatrix; + +varying vec2 vTextureCoord; + +void main(void) +{ + gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); + + vTextureCoord = aTextureCoord; +} diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 455d59c..0ce75d2 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -18,6 +18,14 @@ * let sprite2 = new PIXI.Sprite(texture); * ``` * + * Textures made from SVGs, loaded or not, cannot be used before the file finishes processing. You can check for this by checking the sprite's _textureID property. + * ```js + * var texture = PIXI.Texture.fromImage('assets/image.svg'); + * var sprite1 = new PIXI.Sprite(texture); + * //sprite1._textureID should not be undefined if the texture has finished processing the SVG file + * ``` + * You can use a ticker or rAF to ensure your sprites load the finished textures after processing. See issue #3068. + * * @class * @extends EventEmitter * @memberof PIXI diff --git a/src/extras/getGlobalPosition.js b/src/extras/getGlobalPosition.js index b71682f..a23761f 100644 --- a/src/extras/getGlobalPosition.js +++ b/src/extras/getGlobalPosition.js @@ -1,20 +1,20 @@ import * as core from '../core'; /** - * Returns the global position of the displayObject + * Returns the global position of the displayObject. Does not depend on object scale, rotation and pivot. * * @memberof PIXI.DisplayObject# * @param {Point} point - the point to write the global value to. If null a new point will be returned + * @param {boolean} skipUpdate - setting to true will stop the transforms of the scene graph from + * being updated. This means the calculation returned MAY be out of date BUT will give you a + * nice performance boost * @return {Point} The updated point */ -core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point()) +core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point(), skipUpdate = false) { if (this.parent) { - this.displayObjectUpdateTransform(); - - point.x = this.worldTransform.tx; - point.y = this.worldTransform.ty; + this.parent.toGlobal(this.position, point, skipUpdate); } else { diff --git a/src/filters/blur/BlurFilter.js b/src/filters/blur/BlurFilter.js index 4919d10..5b3c3a9 100644 --- a/src/filters/blur/BlurFilter.js +++ b/src/filters/blur/BlurFilter.js @@ -68,7 +68,7 @@ set blur(value) { this.blurXFilter.blur = this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -113,7 +113,7 @@ set blurX(value) { this.blurXFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -136,6 +136,6 @@ set blurY(value) { this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } } diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 4719b6a..2ced6dc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,6 +1,4 @@ import * as core from '../core'; -import glCore from 'pixi-gl-core'; -import Shader from './webgl/MeshShader'; const tempPoint = new core.Point(); const tempPolygon = new core.Polygon(); @@ -59,11 +57,17 @@ this.indices = indices || new Uint16Array([0, 1, 3, 2]); /** - * Whether the Mesh is dirty or not + * Version of mesh uvs are dirty or not * * @member {number} */ this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ this.indexDirty = 0; /** @@ -122,62 +126,8 @@ */ _renderWebGL(renderer) { - // get rid of any thing that may be batching. - renderer.flush(); - - // renderer.plugins.mesh.render(this); - const gl = renderer.gl; - let glData = this._glDatas[renderer.CONTEXT_UID]; - - if (!glData) - { - glData = { - shader: new Shader(gl), - vertexBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.vertices, gl.STREAM_DRAW), - uvBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.uvs, gl.STREAM_DRAW), - indexBuffer: glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW), - // build the vao object that will render.. - vao: new glCore.VertexArrayObject(gl), - dirty: this.dirty, - indexDirty: this.indexDirty, - }; - - // build the vao object that will render.. - glData.vao = new glCore.VertexArrayObject(gl) - .addIndex(glData.indexBuffer) - .addAttribute(glData.vertexBuffer, glData.shader.attributes.aVertexPosition, gl.FLOAT, false, 2 * 4, 0) - .addAttribute(glData.uvBuffer, glData.shader.attributes.aTextureCoord, gl.FLOAT, false, 2 * 4, 0); - - this._glDatas[renderer.CONTEXT_UID] = glData; - } - - if (this.dirty !== glData.dirty) - { - glData.dirty = this.dirty; - glData.uvBuffer.upload(); - } - - if (this.indexDirty !== glData.indexDirty) - { - glData.indexDirty = this.indexDirty; - glData.indexBuffer.upload(); - } - - glData.vertexBuffer.upload(); - - renderer.bindShader(glData.shader); - renderer.bindTexture(this._texture, 0); - renderer.state.setBlendMode(this.blendMode); - - glData.shader.uniforms.translationMatrix = this.worldTransform.toArray(true); - glData.shader.uniforms.alpha = this.worldAlpha; - glData.shader.uniforms.tint = this.tintRgb; - - const drawMode = this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; - - glData.vao.bind() - .draw(drawMode, this.indices.length) - .unbind(); + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); } /** @@ -188,240 +138,7 @@ */ _renderCanvas(renderer) { - const context = renderer.context; - - const transform = this.worldTransform; - const res = renderer.resolution; - - if (renderer.roundPixels) - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - (transform.tx * res) | 0, - (transform.ty * res) | 0 - ); - } - else - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - transform.tx * res, - transform.ty * res - ); - } - - if (this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) - { - this._renderCanvasTriangleMesh(context); - } - else - { - this._renderCanvasTriangles(context); - } - } - - /** - * Draws the object in Triangle Mesh mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - The current drawing context - */ - _renderCanvasTriangleMesh(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const length = vertices.length / 2; - - // this.count++; - - for (let i = 0; i < length - 2; i++) - { - // draw some triangles! - const index = i * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index, (index + 2), (index + 4)); - } - } - - /** - * Draws the object in triangle mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - */ - _renderCanvasTriangles(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const indices = this.indices; - const length = indices.length; - // this.count++; - - for (let i = 0; i < length; i += 3) - { - // draw some triangles! - const index0 = indices[i] * 2; - const index1 = indices[i + 1] * 2; - const index2 = indices[i + 2] * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2); - } - } - - /** - * Draws one of the triangles that form this Mesh - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - * @param {Float32Array} vertices - a reference to the vertices of the Mesh - * @param {Float32Array} uvs - a reference to the uvs of the Mesh - * @param {number} index0 - the index of the first vertex - * @param {number} index1 - the index of the second vertex - * @param {number} index2 - the index of the third vertex - */ - _renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2) - { - const base = this._texture.baseTexture; - const textureSource = base.source; - const textureWidth = base.width; - const textureHeight = base.height; - - const u0 = uvs[index0] * base.width; - const u1 = uvs[index1] * base.width; - const u2 = uvs[index2] * base.width; - const v0 = uvs[index0 + 1] * base.height; - const v1 = uvs[index1 + 1] * base.height; - const v2 = uvs[index2 + 1] * base.height; - - let x0 = vertices[index0]; - let x1 = vertices[index1]; - let x2 = vertices[index2]; - let y0 = vertices[index0 + 1]; - let y1 = vertices[index1 + 1]; - let y2 = vertices[index2 + 1]; - - if (this.canvasPadding > 0) - { - const paddingX = this.canvasPadding / this.worldTransform.a; - const paddingY = this.canvasPadding / this.worldTransform.d; - const centerX = (x0 + x1 + x2) / 3; - const centerY = (y0 + y1 + y2) / 3; - - let normX = x0 - centerX; - let normY = y0 - centerY; - - let dist = Math.sqrt((normX * normX) + (normY * normY)); - - x0 = centerX + ((normX / dist) * (dist + paddingX)); - y0 = centerY + ((normY / dist) * (dist + paddingY)); - - // - - normX = x1 - centerX; - normY = y1 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x1 = centerX + ((normX / dist) * (dist + paddingX)); - y1 = centerY + ((normY / dist) * (dist + paddingY)); - - normX = x2 - centerX; - normY = y2 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x2 = centerX + ((normX / dist) * (dist + paddingX)); - y2 = centerY + ((normY / dist) * (dist + paddingY)); - } - - context.save(); - context.beginPath(); - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - - context.closePath(); - - context.clip(); - - // Compute matrix transform - const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); - const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); - const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); - const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); - const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); - const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); - const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); - - context.transform( - deltaA / delta, - deltaD / delta, - deltaB / delta, - deltaE / delta, - deltaC / delta, - deltaF / delta - ); - - context.drawImage( - textureSource, - 0, - 0, - textureWidth * base.resolution, - textureHeight * base.resolution, - 0, - 0, - textureWidth, - textureHeight - ); - - context.restore(); - } - - /** - * Renders a flat Mesh - * - * @private - * @param {PIXI.mesh.Mesh} mesh - The Mesh to render - */ - renderMeshFlat(mesh) - { - const context = this.context; - const vertices = mesh.vertices; - const length = vertices.length / 2; - - // this.count++; - - context.beginPath(); - - for (let i = 1; i < length - 2; ++i) - { - // draw some triangles! - const index = i * 2; - - const x0 = vertices[index]; - const y0 = vertices[index + 1]; - - const x1 = vertices[index + 2]; - const y1 = vertices[index + 3]; - - const x2 = vertices[index + 4]; - const y2 = vertices[index + 5]; - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - } - - context.fillStyle = '#FF0000'; - context.fill(); - context.closePath(); + renderer.plugins.mesh.render(this); } /** diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js new file mode 100644 index 0000000..a1ad74c --- /dev/null +++ b/src/mesh/canvas/CanvasMeshRenderer.js @@ -0,0 +1,276 @@ +import * as core from '../../core'; +import { default as Mesh } from '../Mesh'; + +/** + * Renderer dedicated to meshes. + * + * @class + * @private + * @memberof PIXI + */ +export default class MeshSpriteRenderer +{ + /** + * @param {PIXI.CanvasRenderer} renderer - The renderer this downport works for + */ + constructor(renderer) + { + this.renderer = renderer; + } + + /** + * Renders the Mesh + * + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + render(mesh) + { + const renderer = this.renderer; + const context = renderer.context; + + const transform = mesh.worldTransform; + const res = renderer.resolution; + + if (renderer.roundPixels) + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + (transform.tx * res) | 0, + (transform.ty * res) | 0 + ); + } + else + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + transform.tx * res, + transform.ty * res + ); + } + + if (mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) + { + this._renderTriangleMesh(mesh); + } + else + { + this._renderTriangles(mesh); + } + } + + /** + * Draws the object in Triangle Mesh mode + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + _renderTriangleMesh(mesh) + { + // draw triangles!! + const length = mesh.vertices.length / 2; + + for (let i = 0; i < length - 2; i++) + { + // draw some triangles! + const index = i * 2; + + this._renderDrawTriangle(mesh, index, (index + 2), (index + 4)); + } + } + + /** + * Draws the object in triangle mode using canvas + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + */ + _renderTriangles(mesh) + { + // draw triangles!! + const indices = mesh.indices; + const length = indices.length; + + for (let i = 0; i < length; i += 3) + { + // draw some triangles! + const index0 = indices[i] * 2; + const index1 = indices[i + 1] * 2; + const index2 = indices[i + 2] * 2; + + this._renderDrawTriangle(mesh, index0, index1, index2); + } + } + + /** + * Draws one of the triangles that from the Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + * @param {number} index0 - the index of the first vertex + * @param {number} index1 - the index of the second vertex + * @param {number} index2 - the index of the third vertex + */ + _renderDrawTriangle(mesh, index0, index1, index2) + { + const context = this.renderer.context; + const uvs = mesh.uvs; + const vertices = mesh.vertices; + const texture = mesh._texture; + + if (!texture.valid) + { + return; + } + + const base = texture.baseTexture; + const textureSource = base.source; + const textureWidth = base.width; + const textureHeight = base.height; + + const u0 = uvs[index0] * base.width; + const u1 = uvs[index1] * base.width; + const u2 = uvs[index2] * base.width; + const v0 = uvs[index0 + 1] * base.height; + const v1 = uvs[index1 + 1] * base.height; + const v2 = uvs[index2 + 1] * base.height; + + let x0 = vertices[index0]; + let x1 = vertices[index1]; + let x2 = vertices[index2]; + let y0 = vertices[index0 + 1]; + let y1 = vertices[index1 + 1]; + let y2 = vertices[index2 + 1]; + + if (mesh.canvasPadding > 0) + { + const paddingX = mesh.canvasPadding / mesh.worldTransform.a; + const paddingY = mesh.canvasPadding / mesh.worldTransform.d; + const centerX = (x0 + x1 + x2) / 3; + const centerY = (y0 + y1 + y2) / 3; + + let normX = x0 - centerX; + let normY = y0 - centerY; + + let dist = Math.sqrt((normX * normX) + (normY * normY)); + + x0 = centerX + ((normX / dist) * (dist + paddingX)); + y0 = centerY + ((normY / dist) * (dist + paddingY)); + + // + + normX = x1 - centerX; + normY = y1 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x1 = centerX + ((normX / dist) * (dist + paddingX)); + y1 = centerY + ((normY / dist) * (dist + paddingY)); + + normX = x2 - centerX; + normY = y2 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x2 = centerX + ((normX / dist) * (dist + paddingX)); + y2 = centerY + ((normY / dist) * (dist + paddingY)); + } + + context.save(); + context.beginPath(); + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + + context.closePath(); + + context.clip(); + + // Compute matrix transform + const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); + const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); + const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); + const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); + const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); + const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); + const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); + + context.transform( + deltaA / delta, + deltaD / delta, + deltaB / delta, + deltaE / delta, + deltaC / delta, + deltaF / delta + ); + + context.drawImage( + textureSource, + 0, + 0, + textureWidth * base.resolution, + textureHeight * base.resolution, + 0, + 0, + textureWidth, + textureHeight + ); + + context.restore(); + } + + /** + * Renders a flat Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - The Mesh to render + */ + renderMeshFlat(mesh) + { + const context = this.renderer.context; + const vertices = mesh.vertices; + const length = vertices.length / 2; + + // this.count++; + + context.beginPath(); + + for (let i = 1; i < length - 2; ++i) + { + // draw some triangles! + const index = i * 2; + + const x0 = vertices[index]; + const y0 = vertices[index + 1]; + + const x1 = vertices[index + 2]; + const y1 = vertices[index + 3]; + + const x2 = vertices[index + 4]; + const y2 = vertices[index + 5]; + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + } + + context.fillStyle = '#FF0000'; + context.fill(); + context.closePath(); + } + + /** + * destroy the the renderer. + * + */ + destroy() + { + this.renderer = null; + } +} + +core.CanvasRenderer.registerPlugin('mesh', MeshSpriteRenderer); diff --git a/src/mesh/index.js b/src/mesh/index.js index 473d22a..4d1cad6 100644 --- a/src/mesh/index.js +++ b/src/mesh/index.js @@ -2,7 +2,8 @@ * @namespace PIXI.mesh */ export { default as Mesh } from './Mesh'; +export { default as MeshRenderer } from './webgl/MeshRenderer'; +export { default as CanvasMeshRenderer } from './canvas/CanvasMeshRenderer'; export { default as Plane } from './Plane'; export { default as NineSlicePlane } from './NineSlicePlane'; export { default as Rope } from './Rope'; -export { default as MeshShader } from './webgl/MeshShader'; diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js new file mode 100644 index 0000000..73d78e6 --- /dev/null +++ b/src/mesh/webgl/MeshRenderer.js @@ -0,0 +1,108 @@ +import * as core from '../../core'; +import glCore from 'pixi-gl-core'; +import { default as Mesh } from '../Mesh'; + +const glslify = require('glslify'); // eslint-disable-line no-undef + +/** + * WebGL renderer plugin for tiling sprites + */ +export class MeshRenderer extends core.ObjectRenderer { + + /** + * constructor for renderer + * + * @param {WebGLRenderer} renderer The renderer this tiling awesomeness works for. + */ + constructor(renderer) + { + super(renderer); + + this.shader = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + onContextChange() + { + const gl = this.renderer.gl; + + this.shader = new core.Shader(gl, + glslify('./mesh.vert'), + glslify('./mesh.frag')); + } + + /** + * renders mesh + * + * @param {PIXI.mesh.Mesh} mesh mesh instance + */ + render(mesh) + { + const renderer = this.renderer; + const gl = renderer.gl; + const texture = mesh._texture; + + if (!texture.valid) + { + return; + } + + let glData = mesh._glDatas[renderer.CONTEXT_UID]; + + if (!glData) + { + glData = { + shader: this.shader, + vertexBuffer: glCore.GLBuffer.createVertexBuffer(gl, mesh.vertices, gl.STREAM_DRAW), + uvBuffer: glCore.GLBuffer.createVertexBuffer(gl, mesh.uvs, gl.STREAM_DRAW), + indexBuffer: glCore.GLBuffer.createIndexBuffer(gl, mesh.indices, gl.STATIC_DRAW), + // build the vao object that will render.. + vao: new glCore.VertexArrayObject(gl), + dirty: mesh.dirty, + indexDirty: mesh.indexDirty, + }; + + // build the vao object that will render.. + glData.vao = new glCore.VertexArrayObject(gl) + .addIndex(glData.indexBuffer) + .addAttribute(glData.vertexBuffer, glData.shader.attributes.aVertexPosition, gl.FLOAT, false, 2 * 4, 0) + .addAttribute(glData.uvBuffer, glData.shader.attributes.aTextureCoord, gl.FLOAT, false, 2 * 4, 0); + + mesh._glDatas[renderer.CONTEXT_UID] = glData; + } + + if (mesh.dirty !== glData.dirty) + { + glData.dirty = mesh.dirty; + glData.uvBuffer.upload(); + } + + if (mesh.indexDirty !== glData.indexDirty) + { + glData.indexDirty = mesh.indexDirty; + glData.indexBuffer.upload(); + } + + glData.vertexBuffer.upload(); + + renderer.bindShader(glData.shader); + renderer.bindTexture(texture, 0); + renderer.state.setBlendMode(mesh.blendMode); + + glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); + glData.shader.uniforms.alpha = mesh.worldAlpha; + glData.shader.uniforms.tint = mesh.tintRgb; + + const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; + + glData.vao.bind() + .draw(drawMode, mesh.indices.length) + .unbind(); + } +} + +core.WebGLRenderer.registerPlugin('mesh', MeshRenderer); diff --git a/src/mesh/webgl/MeshShader.js b/src/mesh/webgl/MeshShader.js deleted file mode 100644 index 1bbdc0e..0000000 --- a/src/mesh/webgl/MeshShader.js +++ /dev/null @@ -1,46 +0,0 @@ -import Shader from '../../core/Shader'; - -/** - * @class - * @extends PIXI.Shader - * @memberof PIXI.mesh - */ -export default class MeshShader extends Shader -{ - /** - * @param {WebGLRenderingContext} gl - The WebGLRenderingContext. - */ - constructor(gl) - { - super( - gl, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'varying vec2 vTextureCoord;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', // eslint-disable-line max-len - ' vTextureCoord = aTextureCoord;', - '}', - ].join('\n'), - [ - 'varying vec2 vTextureCoord;', - 'uniform float alpha;', - 'uniform vec3 tint;', - - 'uniform sampler2D uSampler;', - - 'void main(void){', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha);', - // ' gl_FragColor = vec4(1.0);', - '}', - ].join('\n') - ); - } -} diff --git a/src/mesh/webgl/mesh.frag b/src/mesh/webgl/mesh.frag new file mode 100644 index 0000000..9e0b634 --- /dev/null +++ b/src/mesh/webgl/mesh.frag @@ -0,0 +1,10 @@ +varying vec2 vTextureCoord; +uniform float alpha; +uniform vec3 tint; + +uniform sampler2D uSampler; + +void main(void) +{ + gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha); +} diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert new file mode 100644 index 0000000..a337aef --- /dev/null +++ b/src/mesh/webgl/mesh.vert @@ -0,0 +1,14 @@ +attribute vec2 aVertexPosition; +attribute vec2 aTextureCoord; + +uniform mat3 translationMatrix; +uniform mat3 projectionMatrix; + +varying vec2 vTextureCoord; + +void main(void) +{ + gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); + + vTextureCoord = aTextureCoord; +} diff --git a/test/core/Container.js b/test/core/Container.js index 5ee605b..be3bf1a 100644 --- a/test/core/Container.js +++ b/test/core/Container.js @@ -301,6 +301,12 @@ { container.swapChildren(child1, child2); }); + + // second call required to complete returned index coverage + assertCallToOnChildrenChanged(container, 0, () => + { + container.swapChildren(child1, child2); + }); }); it('should not call onChildrenChange if supplied children are equal', () => @@ -385,6 +391,175 @@ }); }); + describe('removeChildren', () => + { + it('should remove all children when no arguments supplied', () => + { + const container = new PIXI.Container(); + let removed = []; + + container.addChild(new PIXI.DisplayObject(), new PIXI.DisplayObject(), new PIXI.DisplayObject()); + + expect(container.children.length).to.be.equals(3); + + removed = container.removeChildren(); + + expect(container.children.length).to.be.equals(0); + expect(removed.length).to.be.equals(3); + }); + + it('should return empty array if no children', () => + { + const container = new PIXI.Container(); + const removed = container.removeChildren(); + + expect(removed.length).to.be.equals(0); + }); + + it('should handle a range greater than length', () => + { + const container = new PIXI.Container(); + let removed = []; + + container.addChild(new PIXI.DisplayObject()); + + removed = container.removeChildren(0, 2); + expect(removed.length).to.be.equals(1); + }); + + it('should throw outside acceptable range', () => + { + const container = new PIXI.Container(); + + container.addChild(new PIXI.DisplayObject()); + + expect(() => container.removeChildren(2)) + .to.throw('removeChildren: numeric values are outside the acceptable range.'); + expect(() => container.removeChildren(-1)) + .to.throw('removeChildren: numeric values are outside the acceptable range.'); + expect(() => container.removeChildren(-1, 1)) + .to.throw('removeChildren: numeric values are outside the acceptable range.'); + }); + }); + + describe('destroy', () => + { + it('should not destroy children by default', () => + { + const container = new PIXI.Container(); + const child = new PIXI.DisplayObject(); + + container.addChild(child); + container.destroy(); + + expect(container.children.length).to.be.equals(0); + expect(child.transform).to.not.be.null; + }); + + it('should allow children destroy', () => + { + let container = new PIXI.Container(); + let child = new PIXI.DisplayObject(); + + container.addChild(child); + container.destroy({ children: true }); + + expect(container.children.length).to.be.equals(0); + expect(container.transform).to.be.null; + expect(child.transform).to.be.null; + + container = new PIXI.Container(); + child = new PIXI.DisplayObject(); + + container.addChild(child); + container.destroy(true); + + expect(container.children.length).to.be.equals(0); + expect(container.transform).to.be.null; + expect(child.transform).to.be.null; + }); + }); + + describe('width', () => + { + it('should reflect scale', () => + { + const container = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + + graphics.drawRect(0, 0, 10, 10); + container.addChild(graphics); + container.scale.x = 2; + + expect(container.width).to.be.equals(20); + }); + + it('should adjust scale', () => + { + const container = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + + graphics.drawRect(0, 0, 10, 10); + container.addChild(graphics); + + container.width = 20; + + expect(container.width).to.be.equals(20); + expect(container.scale.x).to.be.equals(2); + }); + + it('should reset scale', () => + { + const container = new PIXI.Container(); + + container.scale.x = 2; + container.width = 5; + + expect(container.width).to.be.equals(0); + expect(container.scale.x).to.be.equals(1); + }); + }); + + describe('height', () => + { + it('should reflect scale', () => + { + const container = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + + graphics.drawRect(0, 0, 10, 10); + container.addChild(graphics); + container.scale.y = 2; + + expect(container.height).to.be.equals(20); + }); + + it('should adjust scale', () => + { + const container = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + + graphics.drawRect(0, 0, 10, 10); + container.addChild(graphics); + + container.height = 20; + + expect(container.height).to.be.equals(20); + expect(container.scale.y).to.be.equals(2); + }); + + it('should reset scale', () => + { + const container = new PIXI.Container(); + + container.scale.y = 2; + container.height = 5; + + expect(container.height).to.be.equals(0); + expect(container.scale.y).to.be.equals(1); + }); + }); + function assertWebGLNotRendered(container) { let rendered = false; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 455d59c..0ce75d2 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -18,6 +18,14 @@ * let sprite2 = new PIXI.Sprite(texture); * ``` * + * Textures made from SVGs, loaded or not, cannot be used before the file finishes processing. You can check for this by checking the sprite's _textureID property. + * ```js + * var texture = PIXI.Texture.fromImage('assets/image.svg'); + * var sprite1 = new PIXI.Sprite(texture); + * //sprite1._textureID should not be undefined if the texture has finished processing the SVG file + * ``` + * You can use a ticker or rAF to ensure your sprites load the finished textures after processing. See issue #3068. + * * @class * @extends EventEmitter * @memberof PIXI diff --git a/src/extras/getGlobalPosition.js b/src/extras/getGlobalPosition.js index b71682f..a23761f 100644 --- a/src/extras/getGlobalPosition.js +++ b/src/extras/getGlobalPosition.js @@ -1,20 +1,20 @@ import * as core from '../core'; /** - * Returns the global position of the displayObject + * Returns the global position of the displayObject. Does not depend on object scale, rotation and pivot. * * @memberof PIXI.DisplayObject# * @param {Point} point - the point to write the global value to. If null a new point will be returned + * @param {boolean} skipUpdate - setting to true will stop the transforms of the scene graph from + * being updated. This means the calculation returned MAY be out of date BUT will give you a + * nice performance boost * @return {Point} The updated point */ -core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point()) +core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point(), skipUpdate = false) { if (this.parent) { - this.displayObjectUpdateTransform(); - - point.x = this.worldTransform.tx; - point.y = this.worldTransform.ty; + this.parent.toGlobal(this.position, point, skipUpdate); } else { diff --git a/src/filters/blur/BlurFilter.js b/src/filters/blur/BlurFilter.js index 4919d10..5b3c3a9 100644 --- a/src/filters/blur/BlurFilter.js +++ b/src/filters/blur/BlurFilter.js @@ -68,7 +68,7 @@ set blur(value) { this.blurXFilter.blur = this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -113,7 +113,7 @@ set blurX(value) { this.blurXFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -136,6 +136,6 @@ set blurY(value) { this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } } diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 4719b6a..2ced6dc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,6 +1,4 @@ import * as core from '../core'; -import glCore from 'pixi-gl-core'; -import Shader from './webgl/MeshShader'; const tempPoint = new core.Point(); const tempPolygon = new core.Polygon(); @@ -59,11 +57,17 @@ this.indices = indices || new Uint16Array([0, 1, 3, 2]); /** - * Whether the Mesh is dirty or not + * Version of mesh uvs are dirty or not * * @member {number} */ this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ this.indexDirty = 0; /** @@ -122,62 +126,8 @@ */ _renderWebGL(renderer) { - // get rid of any thing that may be batching. - renderer.flush(); - - // renderer.plugins.mesh.render(this); - const gl = renderer.gl; - let glData = this._glDatas[renderer.CONTEXT_UID]; - - if (!glData) - { - glData = { - shader: new Shader(gl), - vertexBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.vertices, gl.STREAM_DRAW), - uvBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.uvs, gl.STREAM_DRAW), - indexBuffer: glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW), - // build the vao object that will render.. - vao: new glCore.VertexArrayObject(gl), - dirty: this.dirty, - indexDirty: this.indexDirty, - }; - - // build the vao object that will render.. - glData.vao = new glCore.VertexArrayObject(gl) - .addIndex(glData.indexBuffer) - .addAttribute(glData.vertexBuffer, glData.shader.attributes.aVertexPosition, gl.FLOAT, false, 2 * 4, 0) - .addAttribute(glData.uvBuffer, glData.shader.attributes.aTextureCoord, gl.FLOAT, false, 2 * 4, 0); - - this._glDatas[renderer.CONTEXT_UID] = glData; - } - - if (this.dirty !== glData.dirty) - { - glData.dirty = this.dirty; - glData.uvBuffer.upload(); - } - - if (this.indexDirty !== glData.indexDirty) - { - glData.indexDirty = this.indexDirty; - glData.indexBuffer.upload(); - } - - glData.vertexBuffer.upload(); - - renderer.bindShader(glData.shader); - renderer.bindTexture(this._texture, 0); - renderer.state.setBlendMode(this.blendMode); - - glData.shader.uniforms.translationMatrix = this.worldTransform.toArray(true); - glData.shader.uniforms.alpha = this.worldAlpha; - glData.shader.uniforms.tint = this.tintRgb; - - const drawMode = this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; - - glData.vao.bind() - .draw(drawMode, this.indices.length) - .unbind(); + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); } /** @@ -188,240 +138,7 @@ */ _renderCanvas(renderer) { - const context = renderer.context; - - const transform = this.worldTransform; - const res = renderer.resolution; - - if (renderer.roundPixels) - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - (transform.tx * res) | 0, - (transform.ty * res) | 0 - ); - } - else - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - transform.tx * res, - transform.ty * res - ); - } - - if (this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) - { - this._renderCanvasTriangleMesh(context); - } - else - { - this._renderCanvasTriangles(context); - } - } - - /** - * Draws the object in Triangle Mesh mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - The current drawing context - */ - _renderCanvasTriangleMesh(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const length = vertices.length / 2; - - // this.count++; - - for (let i = 0; i < length - 2; i++) - { - // draw some triangles! - const index = i * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index, (index + 2), (index + 4)); - } - } - - /** - * Draws the object in triangle mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - */ - _renderCanvasTriangles(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const indices = this.indices; - const length = indices.length; - // this.count++; - - for (let i = 0; i < length; i += 3) - { - // draw some triangles! - const index0 = indices[i] * 2; - const index1 = indices[i + 1] * 2; - const index2 = indices[i + 2] * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2); - } - } - - /** - * Draws one of the triangles that form this Mesh - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - * @param {Float32Array} vertices - a reference to the vertices of the Mesh - * @param {Float32Array} uvs - a reference to the uvs of the Mesh - * @param {number} index0 - the index of the first vertex - * @param {number} index1 - the index of the second vertex - * @param {number} index2 - the index of the third vertex - */ - _renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2) - { - const base = this._texture.baseTexture; - const textureSource = base.source; - const textureWidth = base.width; - const textureHeight = base.height; - - const u0 = uvs[index0] * base.width; - const u1 = uvs[index1] * base.width; - const u2 = uvs[index2] * base.width; - const v0 = uvs[index0 + 1] * base.height; - const v1 = uvs[index1 + 1] * base.height; - const v2 = uvs[index2 + 1] * base.height; - - let x0 = vertices[index0]; - let x1 = vertices[index1]; - let x2 = vertices[index2]; - let y0 = vertices[index0 + 1]; - let y1 = vertices[index1 + 1]; - let y2 = vertices[index2 + 1]; - - if (this.canvasPadding > 0) - { - const paddingX = this.canvasPadding / this.worldTransform.a; - const paddingY = this.canvasPadding / this.worldTransform.d; - const centerX = (x0 + x1 + x2) / 3; - const centerY = (y0 + y1 + y2) / 3; - - let normX = x0 - centerX; - let normY = y0 - centerY; - - let dist = Math.sqrt((normX * normX) + (normY * normY)); - - x0 = centerX + ((normX / dist) * (dist + paddingX)); - y0 = centerY + ((normY / dist) * (dist + paddingY)); - - // - - normX = x1 - centerX; - normY = y1 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x1 = centerX + ((normX / dist) * (dist + paddingX)); - y1 = centerY + ((normY / dist) * (dist + paddingY)); - - normX = x2 - centerX; - normY = y2 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x2 = centerX + ((normX / dist) * (dist + paddingX)); - y2 = centerY + ((normY / dist) * (dist + paddingY)); - } - - context.save(); - context.beginPath(); - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - - context.closePath(); - - context.clip(); - - // Compute matrix transform - const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); - const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); - const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); - const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); - const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); - const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); - const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); - - context.transform( - deltaA / delta, - deltaD / delta, - deltaB / delta, - deltaE / delta, - deltaC / delta, - deltaF / delta - ); - - context.drawImage( - textureSource, - 0, - 0, - textureWidth * base.resolution, - textureHeight * base.resolution, - 0, - 0, - textureWidth, - textureHeight - ); - - context.restore(); - } - - /** - * Renders a flat Mesh - * - * @private - * @param {PIXI.mesh.Mesh} mesh - The Mesh to render - */ - renderMeshFlat(mesh) - { - const context = this.context; - const vertices = mesh.vertices; - const length = vertices.length / 2; - - // this.count++; - - context.beginPath(); - - for (let i = 1; i < length - 2; ++i) - { - // draw some triangles! - const index = i * 2; - - const x0 = vertices[index]; - const y0 = vertices[index + 1]; - - const x1 = vertices[index + 2]; - const y1 = vertices[index + 3]; - - const x2 = vertices[index + 4]; - const y2 = vertices[index + 5]; - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - } - - context.fillStyle = '#FF0000'; - context.fill(); - context.closePath(); + renderer.plugins.mesh.render(this); } /** diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js new file mode 100644 index 0000000..a1ad74c --- /dev/null +++ b/src/mesh/canvas/CanvasMeshRenderer.js @@ -0,0 +1,276 @@ +import * as core from '../../core'; +import { default as Mesh } from '../Mesh'; + +/** + * Renderer dedicated to meshes. + * + * @class + * @private + * @memberof PIXI + */ +export default class MeshSpriteRenderer +{ + /** + * @param {PIXI.CanvasRenderer} renderer - The renderer this downport works for + */ + constructor(renderer) + { + this.renderer = renderer; + } + + /** + * Renders the Mesh + * + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + render(mesh) + { + const renderer = this.renderer; + const context = renderer.context; + + const transform = mesh.worldTransform; + const res = renderer.resolution; + + if (renderer.roundPixels) + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + (transform.tx * res) | 0, + (transform.ty * res) | 0 + ); + } + else + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + transform.tx * res, + transform.ty * res + ); + } + + if (mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) + { + this._renderTriangleMesh(mesh); + } + else + { + this._renderTriangles(mesh); + } + } + + /** + * Draws the object in Triangle Mesh mode + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + _renderTriangleMesh(mesh) + { + // draw triangles!! + const length = mesh.vertices.length / 2; + + for (let i = 0; i < length - 2; i++) + { + // draw some triangles! + const index = i * 2; + + this._renderDrawTriangle(mesh, index, (index + 2), (index + 4)); + } + } + + /** + * Draws the object in triangle mode using canvas + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + */ + _renderTriangles(mesh) + { + // draw triangles!! + const indices = mesh.indices; + const length = indices.length; + + for (let i = 0; i < length; i += 3) + { + // draw some triangles! + const index0 = indices[i] * 2; + const index1 = indices[i + 1] * 2; + const index2 = indices[i + 2] * 2; + + this._renderDrawTriangle(mesh, index0, index1, index2); + } + } + + /** + * Draws one of the triangles that from the Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + * @param {number} index0 - the index of the first vertex + * @param {number} index1 - the index of the second vertex + * @param {number} index2 - the index of the third vertex + */ + _renderDrawTriangle(mesh, index0, index1, index2) + { + const context = this.renderer.context; + const uvs = mesh.uvs; + const vertices = mesh.vertices; + const texture = mesh._texture; + + if (!texture.valid) + { + return; + } + + const base = texture.baseTexture; + const textureSource = base.source; + const textureWidth = base.width; + const textureHeight = base.height; + + const u0 = uvs[index0] * base.width; + const u1 = uvs[index1] * base.width; + const u2 = uvs[index2] * base.width; + const v0 = uvs[index0 + 1] * base.height; + const v1 = uvs[index1 + 1] * base.height; + const v2 = uvs[index2 + 1] * base.height; + + let x0 = vertices[index0]; + let x1 = vertices[index1]; + let x2 = vertices[index2]; + let y0 = vertices[index0 + 1]; + let y1 = vertices[index1 + 1]; + let y2 = vertices[index2 + 1]; + + if (mesh.canvasPadding > 0) + { + const paddingX = mesh.canvasPadding / mesh.worldTransform.a; + const paddingY = mesh.canvasPadding / mesh.worldTransform.d; + const centerX = (x0 + x1 + x2) / 3; + const centerY = (y0 + y1 + y2) / 3; + + let normX = x0 - centerX; + let normY = y0 - centerY; + + let dist = Math.sqrt((normX * normX) + (normY * normY)); + + x0 = centerX + ((normX / dist) * (dist + paddingX)); + y0 = centerY + ((normY / dist) * (dist + paddingY)); + + // + + normX = x1 - centerX; + normY = y1 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x1 = centerX + ((normX / dist) * (dist + paddingX)); + y1 = centerY + ((normY / dist) * (dist + paddingY)); + + normX = x2 - centerX; + normY = y2 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x2 = centerX + ((normX / dist) * (dist + paddingX)); + y2 = centerY + ((normY / dist) * (dist + paddingY)); + } + + context.save(); + context.beginPath(); + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + + context.closePath(); + + context.clip(); + + // Compute matrix transform + const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); + const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); + const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); + const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); + const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); + const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); + const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); + + context.transform( + deltaA / delta, + deltaD / delta, + deltaB / delta, + deltaE / delta, + deltaC / delta, + deltaF / delta + ); + + context.drawImage( + textureSource, + 0, + 0, + textureWidth * base.resolution, + textureHeight * base.resolution, + 0, + 0, + textureWidth, + textureHeight + ); + + context.restore(); + } + + /** + * Renders a flat Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - The Mesh to render + */ + renderMeshFlat(mesh) + { + const context = this.renderer.context; + const vertices = mesh.vertices; + const length = vertices.length / 2; + + // this.count++; + + context.beginPath(); + + for (let i = 1; i < length - 2; ++i) + { + // draw some triangles! + const index = i * 2; + + const x0 = vertices[index]; + const y0 = vertices[index + 1]; + + const x1 = vertices[index + 2]; + const y1 = vertices[index + 3]; + + const x2 = vertices[index + 4]; + const y2 = vertices[index + 5]; + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + } + + context.fillStyle = '#FF0000'; + context.fill(); + context.closePath(); + } + + /** + * destroy the the renderer. + * + */ + destroy() + { + this.renderer = null; + } +} + +core.CanvasRenderer.registerPlugin('mesh', MeshSpriteRenderer); diff --git a/src/mesh/index.js b/src/mesh/index.js index 473d22a..4d1cad6 100644 --- a/src/mesh/index.js +++ b/src/mesh/index.js @@ -2,7 +2,8 @@ * @namespace PIXI.mesh */ export { default as Mesh } from './Mesh'; +export { default as MeshRenderer } from './webgl/MeshRenderer'; +export { default as CanvasMeshRenderer } from './canvas/CanvasMeshRenderer'; export { default as Plane } from './Plane'; export { default as NineSlicePlane } from './NineSlicePlane'; export { default as Rope } from './Rope'; -export { default as MeshShader } from './webgl/MeshShader'; diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js new file mode 100644 index 0000000..73d78e6 --- /dev/null +++ b/src/mesh/webgl/MeshRenderer.js @@ -0,0 +1,108 @@ +import * as core from '../../core'; +import glCore from 'pixi-gl-core'; +import { default as Mesh } from '../Mesh'; + +const glslify = require('glslify'); // eslint-disable-line no-undef + +/** + * WebGL renderer plugin for tiling sprites + */ +export class MeshRenderer extends core.ObjectRenderer { + + /** + * constructor for renderer + * + * @param {WebGLRenderer} renderer The renderer this tiling awesomeness works for. + */ + constructor(renderer) + { + super(renderer); + + this.shader = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + onContextChange() + { + const gl = this.renderer.gl; + + this.shader = new core.Shader(gl, + glslify('./mesh.vert'), + glslify('./mesh.frag')); + } + + /** + * renders mesh + * + * @param {PIXI.mesh.Mesh} mesh mesh instance + */ + render(mesh) + { + const renderer = this.renderer; + const gl = renderer.gl; + const texture = mesh._texture; + + if (!texture.valid) + { + return; + } + + let glData = mesh._glDatas[renderer.CONTEXT_UID]; + + if (!glData) + { + glData = { + shader: this.shader, + vertexBuffer: glCore.GLBuffer.createVertexBuffer(gl, mesh.vertices, gl.STREAM_DRAW), + uvBuffer: glCore.GLBuffer.createVertexBuffer(gl, mesh.uvs, gl.STREAM_DRAW), + indexBuffer: glCore.GLBuffer.createIndexBuffer(gl, mesh.indices, gl.STATIC_DRAW), + // build the vao object that will render.. + vao: new glCore.VertexArrayObject(gl), + dirty: mesh.dirty, + indexDirty: mesh.indexDirty, + }; + + // build the vao object that will render.. + glData.vao = new glCore.VertexArrayObject(gl) + .addIndex(glData.indexBuffer) + .addAttribute(glData.vertexBuffer, glData.shader.attributes.aVertexPosition, gl.FLOAT, false, 2 * 4, 0) + .addAttribute(glData.uvBuffer, glData.shader.attributes.aTextureCoord, gl.FLOAT, false, 2 * 4, 0); + + mesh._glDatas[renderer.CONTEXT_UID] = glData; + } + + if (mesh.dirty !== glData.dirty) + { + glData.dirty = mesh.dirty; + glData.uvBuffer.upload(); + } + + if (mesh.indexDirty !== glData.indexDirty) + { + glData.indexDirty = mesh.indexDirty; + glData.indexBuffer.upload(); + } + + glData.vertexBuffer.upload(); + + renderer.bindShader(glData.shader); + renderer.bindTexture(texture, 0); + renderer.state.setBlendMode(mesh.blendMode); + + glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); + glData.shader.uniforms.alpha = mesh.worldAlpha; + glData.shader.uniforms.tint = mesh.tintRgb; + + const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; + + glData.vao.bind() + .draw(drawMode, mesh.indices.length) + .unbind(); + } +} + +core.WebGLRenderer.registerPlugin('mesh', MeshRenderer); diff --git a/src/mesh/webgl/MeshShader.js b/src/mesh/webgl/MeshShader.js deleted file mode 100644 index 1bbdc0e..0000000 --- a/src/mesh/webgl/MeshShader.js +++ /dev/null @@ -1,46 +0,0 @@ -import Shader from '../../core/Shader'; - -/** - * @class - * @extends PIXI.Shader - * @memberof PIXI.mesh - */ -export default class MeshShader extends Shader -{ - /** - * @param {WebGLRenderingContext} gl - The WebGLRenderingContext. - */ - constructor(gl) - { - super( - gl, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'varying vec2 vTextureCoord;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', // eslint-disable-line max-len - ' vTextureCoord = aTextureCoord;', - '}', - ].join('\n'), - [ - 'varying vec2 vTextureCoord;', - 'uniform float alpha;', - 'uniform vec3 tint;', - - 'uniform sampler2D uSampler;', - - 'void main(void){', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha);', - // ' gl_FragColor = vec4(1.0);', - '}', - ].join('\n') - ); - } -} diff --git a/src/mesh/webgl/mesh.frag b/src/mesh/webgl/mesh.frag new file mode 100644 index 0000000..9e0b634 --- /dev/null +++ b/src/mesh/webgl/mesh.frag @@ -0,0 +1,10 @@ +varying vec2 vTextureCoord; +uniform float alpha; +uniform vec3 tint; + +uniform sampler2D uSampler; + +void main(void) +{ + gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha); +} diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert new file mode 100644 index 0000000..a337aef --- /dev/null +++ b/src/mesh/webgl/mesh.vert @@ -0,0 +1,14 @@ +attribute vec2 aVertexPosition; +attribute vec2 aTextureCoord; + +uniform mat3 translationMatrix; +uniform mat3 projectionMatrix; + +varying vec2 vTextureCoord; + +void main(void) +{ + gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); + + vTextureCoord = aTextureCoord; +} diff --git a/test/core/Container.js b/test/core/Container.js index 5ee605b..be3bf1a 100644 --- a/test/core/Container.js +++ b/test/core/Container.js @@ -301,6 +301,12 @@ { container.swapChildren(child1, child2); }); + + // second call required to complete returned index coverage + assertCallToOnChildrenChanged(container, 0, () => + { + container.swapChildren(child1, child2); + }); }); it('should not call onChildrenChange if supplied children are equal', () => @@ -385,6 +391,175 @@ }); }); + describe('removeChildren', () => + { + it('should remove all children when no arguments supplied', () => + { + const container = new PIXI.Container(); + let removed = []; + + container.addChild(new PIXI.DisplayObject(), new PIXI.DisplayObject(), new PIXI.DisplayObject()); + + expect(container.children.length).to.be.equals(3); + + removed = container.removeChildren(); + + expect(container.children.length).to.be.equals(0); + expect(removed.length).to.be.equals(3); + }); + + it('should return empty array if no children', () => + { + const container = new PIXI.Container(); + const removed = container.removeChildren(); + + expect(removed.length).to.be.equals(0); + }); + + it('should handle a range greater than length', () => + { + const container = new PIXI.Container(); + let removed = []; + + container.addChild(new PIXI.DisplayObject()); + + removed = container.removeChildren(0, 2); + expect(removed.length).to.be.equals(1); + }); + + it('should throw outside acceptable range', () => + { + const container = new PIXI.Container(); + + container.addChild(new PIXI.DisplayObject()); + + expect(() => container.removeChildren(2)) + .to.throw('removeChildren: numeric values are outside the acceptable range.'); + expect(() => container.removeChildren(-1)) + .to.throw('removeChildren: numeric values are outside the acceptable range.'); + expect(() => container.removeChildren(-1, 1)) + .to.throw('removeChildren: numeric values are outside the acceptable range.'); + }); + }); + + describe('destroy', () => + { + it('should not destroy children by default', () => + { + const container = new PIXI.Container(); + const child = new PIXI.DisplayObject(); + + container.addChild(child); + container.destroy(); + + expect(container.children.length).to.be.equals(0); + expect(child.transform).to.not.be.null; + }); + + it('should allow children destroy', () => + { + let container = new PIXI.Container(); + let child = new PIXI.DisplayObject(); + + container.addChild(child); + container.destroy({ children: true }); + + expect(container.children.length).to.be.equals(0); + expect(container.transform).to.be.null; + expect(child.transform).to.be.null; + + container = new PIXI.Container(); + child = new PIXI.DisplayObject(); + + container.addChild(child); + container.destroy(true); + + expect(container.children.length).to.be.equals(0); + expect(container.transform).to.be.null; + expect(child.transform).to.be.null; + }); + }); + + describe('width', () => + { + it('should reflect scale', () => + { + const container = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + + graphics.drawRect(0, 0, 10, 10); + container.addChild(graphics); + container.scale.x = 2; + + expect(container.width).to.be.equals(20); + }); + + it('should adjust scale', () => + { + const container = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + + graphics.drawRect(0, 0, 10, 10); + container.addChild(graphics); + + container.width = 20; + + expect(container.width).to.be.equals(20); + expect(container.scale.x).to.be.equals(2); + }); + + it('should reset scale', () => + { + const container = new PIXI.Container(); + + container.scale.x = 2; + container.width = 5; + + expect(container.width).to.be.equals(0); + expect(container.scale.x).to.be.equals(1); + }); + }); + + describe('height', () => + { + it('should reflect scale', () => + { + const container = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + + graphics.drawRect(0, 0, 10, 10); + container.addChild(graphics); + container.scale.y = 2; + + expect(container.height).to.be.equals(20); + }); + + it('should adjust scale', () => + { + const container = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + + graphics.drawRect(0, 0, 10, 10); + container.addChild(graphics); + + container.height = 20; + + expect(container.height).to.be.equals(20); + expect(container.scale.y).to.be.equals(2); + }); + + it('should reset scale', () => + { + const container = new PIXI.Container(); + + container.scale.y = 2; + container.height = 5; + + expect(container.height).to.be.equals(0); + expect(container.scale.y).to.be.equals(1); + }); + }); + function assertWebGLNotRendered(container) { let rendered = false; diff --git a/test/core/Sprite.js b/test/core/Sprite.js index 38d77ca..3eb2f56 100755 --- a/test/core/Sprite.js +++ b/test/core/Sprite.js @@ -4,7 +4,7 @@ { describe('width', function () { - it('should not be negative for nagative scale.x', function () + it('should not be negative for negative scale.x', function () { var sprite = new PIXI.Sprite(); @@ -34,7 +34,7 @@ describe('height', function () { - it('should not be negative for nagative scale.y', function () + it('should not be negative for negative scale.y', function () { var sprite = new PIXI.Sprite(); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 455d59c..0ce75d2 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -18,6 +18,14 @@ * let sprite2 = new PIXI.Sprite(texture); * ``` * + * Textures made from SVGs, loaded or not, cannot be used before the file finishes processing. You can check for this by checking the sprite's _textureID property. + * ```js + * var texture = PIXI.Texture.fromImage('assets/image.svg'); + * var sprite1 = new PIXI.Sprite(texture); + * //sprite1._textureID should not be undefined if the texture has finished processing the SVG file + * ``` + * You can use a ticker or rAF to ensure your sprites load the finished textures after processing. See issue #3068. + * * @class * @extends EventEmitter * @memberof PIXI diff --git a/src/extras/getGlobalPosition.js b/src/extras/getGlobalPosition.js index b71682f..a23761f 100644 --- a/src/extras/getGlobalPosition.js +++ b/src/extras/getGlobalPosition.js @@ -1,20 +1,20 @@ import * as core from '../core'; /** - * Returns the global position of the displayObject + * Returns the global position of the displayObject. Does not depend on object scale, rotation and pivot. * * @memberof PIXI.DisplayObject# * @param {Point} point - the point to write the global value to. If null a new point will be returned + * @param {boolean} skipUpdate - setting to true will stop the transforms of the scene graph from + * being updated. This means the calculation returned MAY be out of date BUT will give you a + * nice performance boost * @return {Point} The updated point */ -core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point()) +core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point(), skipUpdate = false) { if (this.parent) { - this.displayObjectUpdateTransform(); - - point.x = this.worldTransform.tx; - point.y = this.worldTransform.ty; + this.parent.toGlobal(this.position, point, skipUpdate); } else { diff --git a/src/filters/blur/BlurFilter.js b/src/filters/blur/BlurFilter.js index 4919d10..5b3c3a9 100644 --- a/src/filters/blur/BlurFilter.js +++ b/src/filters/blur/BlurFilter.js @@ -68,7 +68,7 @@ set blur(value) { this.blurXFilter.blur = this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -113,7 +113,7 @@ set blurX(value) { this.blurXFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -136,6 +136,6 @@ set blurY(value) { this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } } diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 4719b6a..2ced6dc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,6 +1,4 @@ import * as core from '../core'; -import glCore from 'pixi-gl-core'; -import Shader from './webgl/MeshShader'; const tempPoint = new core.Point(); const tempPolygon = new core.Polygon(); @@ -59,11 +57,17 @@ this.indices = indices || new Uint16Array([0, 1, 3, 2]); /** - * Whether the Mesh is dirty or not + * Version of mesh uvs are dirty or not * * @member {number} */ this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ this.indexDirty = 0; /** @@ -122,62 +126,8 @@ */ _renderWebGL(renderer) { - // get rid of any thing that may be batching. - renderer.flush(); - - // renderer.plugins.mesh.render(this); - const gl = renderer.gl; - let glData = this._glDatas[renderer.CONTEXT_UID]; - - if (!glData) - { - glData = { - shader: new Shader(gl), - vertexBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.vertices, gl.STREAM_DRAW), - uvBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.uvs, gl.STREAM_DRAW), - indexBuffer: glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW), - // build the vao object that will render.. - vao: new glCore.VertexArrayObject(gl), - dirty: this.dirty, - indexDirty: this.indexDirty, - }; - - // build the vao object that will render.. - glData.vao = new glCore.VertexArrayObject(gl) - .addIndex(glData.indexBuffer) - .addAttribute(glData.vertexBuffer, glData.shader.attributes.aVertexPosition, gl.FLOAT, false, 2 * 4, 0) - .addAttribute(glData.uvBuffer, glData.shader.attributes.aTextureCoord, gl.FLOAT, false, 2 * 4, 0); - - this._glDatas[renderer.CONTEXT_UID] = glData; - } - - if (this.dirty !== glData.dirty) - { - glData.dirty = this.dirty; - glData.uvBuffer.upload(); - } - - if (this.indexDirty !== glData.indexDirty) - { - glData.indexDirty = this.indexDirty; - glData.indexBuffer.upload(); - } - - glData.vertexBuffer.upload(); - - renderer.bindShader(glData.shader); - renderer.bindTexture(this._texture, 0); - renderer.state.setBlendMode(this.blendMode); - - glData.shader.uniforms.translationMatrix = this.worldTransform.toArray(true); - glData.shader.uniforms.alpha = this.worldAlpha; - glData.shader.uniforms.tint = this.tintRgb; - - const drawMode = this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; - - glData.vao.bind() - .draw(drawMode, this.indices.length) - .unbind(); + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); } /** @@ -188,240 +138,7 @@ */ _renderCanvas(renderer) { - const context = renderer.context; - - const transform = this.worldTransform; - const res = renderer.resolution; - - if (renderer.roundPixels) - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - (transform.tx * res) | 0, - (transform.ty * res) | 0 - ); - } - else - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - transform.tx * res, - transform.ty * res - ); - } - - if (this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) - { - this._renderCanvasTriangleMesh(context); - } - else - { - this._renderCanvasTriangles(context); - } - } - - /** - * Draws the object in Triangle Mesh mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - The current drawing context - */ - _renderCanvasTriangleMesh(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const length = vertices.length / 2; - - // this.count++; - - for (let i = 0; i < length - 2; i++) - { - // draw some triangles! - const index = i * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index, (index + 2), (index + 4)); - } - } - - /** - * Draws the object in triangle mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - */ - _renderCanvasTriangles(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const indices = this.indices; - const length = indices.length; - // this.count++; - - for (let i = 0; i < length; i += 3) - { - // draw some triangles! - const index0 = indices[i] * 2; - const index1 = indices[i + 1] * 2; - const index2 = indices[i + 2] * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2); - } - } - - /** - * Draws one of the triangles that form this Mesh - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - * @param {Float32Array} vertices - a reference to the vertices of the Mesh - * @param {Float32Array} uvs - a reference to the uvs of the Mesh - * @param {number} index0 - the index of the first vertex - * @param {number} index1 - the index of the second vertex - * @param {number} index2 - the index of the third vertex - */ - _renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2) - { - const base = this._texture.baseTexture; - const textureSource = base.source; - const textureWidth = base.width; - const textureHeight = base.height; - - const u0 = uvs[index0] * base.width; - const u1 = uvs[index1] * base.width; - const u2 = uvs[index2] * base.width; - const v0 = uvs[index0 + 1] * base.height; - const v1 = uvs[index1 + 1] * base.height; - const v2 = uvs[index2 + 1] * base.height; - - let x0 = vertices[index0]; - let x1 = vertices[index1]; - let x2 = vertices[index2]; - let y0 = vertices[index0 + 1]; - let y1 = vertices[index1 + 1]; - let y2 = vertices[index2 + 1]; - - if (this.canvasPadding > 0) - { - const paddingX = this.canvasPadding / this.worldTransform.a; - const paddingY = this.canvasPadding / this.worldTransform.d; - const centerX = (x0 + x1 + x2) / 3; - const centerY = (y0 + y1 + y2) / 3; - - let normX = x0 - centerX; - let normY = y0 - centerY; - - let dist = Math.sqrt((normX * normX) + (normY * normY)); - - x0 = centerX + ((normX / dist) * (dist + paddingX)); - y0 = centerY + ((normY / dist) * (dist + paddingY)); - - // - - normX = x1 - centerX; - normY = y1 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x1 = centerX + ((normX / dist) * (dist + paddingX)); - y1 = centerY + ((normY / dist) * (dist + paddingY)); - - normX = x2 - centerX; - normY = y2 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x2 = centerX + ((normX / dist) * (dist + paddingX)); - y2 = centerY + ((normY / dist) * (dist + paddingY)); - } - - context.save(); - context.beginPath(); - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - - context.closePath(); - - context.clip(); - - // Compute matrix transform - const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); - const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); - const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); - const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); - const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); - const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); - const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); - - context.transform( - deltaA / delta, - deltaD / delta, - deltaB / delta, - deltaE / delta, - deltaC / delta, - deltaF / delta - ); - - context.drawImage( - textureSource, - 0, - 0, - textureWidth * base.resolution, - textureHeight * base.resolution, - 0, - 0, - textureWidth, - textureHeight - ); - - context.restore(); - } - - /** - * Renders a flat Mesh - * - * @private - * @param {PIXI.mesh.Mesh} mesh - The Mesh to render - */ - renderMeshFlat(mesh) - { - const context = this.context; - const vertices = mesh.vertices; - const length = vertices.length / 2; - - // this.count++; - - context.beginPath(); - - for (let i = 1; i < length - 2; ++i) - { - // draw some triangles! - const index = i * 2; - - const x0 = vertices[index]; - const y0 = vertices[index + 1]; - - const x1 = vertices[index + 2]; - const y1 = vertices[index + 3]; - - const x2 = vertices[index + 4]; - const y2 = vertices[index + 5]; - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - } - - context.fillStyle = '#FF0000'; - context.fill(); - context.closePath(); + renderer.plugins.mesh.render(this); } /** diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js new file mode 100644 index 0000000..a1ad74c --- /dev/null +++ b/src/mesh/canvas/CanvasMeshRenderer.js @@ -0,0 +1,276 @@ +import * as core from '../../core'; +import { default as Mesh } from '../Mesh'; + +/** + * Renderer dedicated to meshes. + * + * @class + * @private + * @memberof PIXI + */ +export default class MeshSpriteRenderer +{ + /** + * @param {PIXI.CanvasRenderer} renderer - The renderer this downport works for + */ + constructor(renderer) + { + this.renderer = renderer; + } + + /** + * Renders the Mesh + * + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + render(mesh) + { + const renderer = this.renderer; + const context = renderer.context; + + const transform = mesh.worldTransform; + const res = renderer.resolution; + + if (renderer.roundPixels) + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + (transform.tx * res) | 0, + (transform.ty * res) | 0 + ); + } + else + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + transform.tx * res, + transform.ty * res + ); + } + + if (mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) + { + this._renderTriangleMesh(mesh); + } + else + { + this._renderTriangles(mesh); + } + } + + /** + * Draws the object in Triangle Mesh mode + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + _renderTriangleMesh(mesh) + { + // draw triangles!! + const length = mesh.vertices.length / 2; + + for (let i = 0; i < length - 2; i++) + { + // draw some triangles! + const index = i * 2; + + this._renderDrawTriangle(mesh, index, (index + 2), (index + 4)); + } + } + + /** + * Draws the object in triangle mode using canvas + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + */ + _renderTriangles(mesh) + { + // draw triangles!! + const indices = mesh.indices; + const length = indices.length; + + for (let i = 0; i < length; i += 3) + { + // draw some triangles! + const index0 = indices[i] * 2; + const index1 = indices[i + 1] * 2; + const index2 = indices[i + 2] * 2; + + this._renderDrawTriangle(mesh, index0, index1, index2); + } + } + + /** + * Draws one of the triangles that from the Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + * @param {number} index0 - the index of the first vertex + * @param {number} index1 - the index of the second vertex + * @param {number} index2 - the index of the third vertex + */ + _renderDrawTriangle(mesh, index0, index1, index2) + { + const context = this.renderer.context; + const uvs = mesh.uvs; + const vertices = mesh.vertices; + const texture = mesh._texture; + + if (!texture.valid) + { + return; + } + + const base = texture.baseTexture; + const textureSource = base.source; + const textureWidth = base.width; + const textureHeight = base.height; + + const u0 = uvs[index0] * base.width; + const u1 = uvs[index1] * base.width; + const u2 = uvs[index2] * base.width; + const v0 = uvs[index0 + 1] * base.height; + const v1 = uvs[index1 + 1] * base.height; + const v2 = uvs[index2 + 1] * base.height; + + let x0 = vertices[index0]; + let x1 = vertices[index1]; + let x2 = vertices[index2]; + let y0 = vertices[index0 + 1]; + let y1 = vertices[index1 + 1]; + let y2 = vertices[index2 + 1]; + + if (mesh.canvasPadding > 0) + { + const paddingX = mesh.canvasPadding / mesh.worldTransform.a; + const paddingY = mesh.canvasPadding / mesh.worldTransform.d; + const centerX = (x0 + x1 + x2) / 3; + const centerY = (y0 + y1 + y2) / 3; + + let normX = x0 - centerX; + let normY = y0 - centerY; + + let dist = Math.sqrt((normX * normX) + (normY * normY)); + + x0 = centerX + ((normX / dist) * (dist + paddingX)); + y0 = centerY + ((normY / dist) * (dist + paddingY)); + + // + + normX = x1 - centerX; + normY = y1 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x1 = centerX + ((normX / dist) * (dist + paddingX)); + y1 = centerY + ((normY / dist) * (dist + paddingY)); + + normX = x2 - centerX; + normY = y2 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x2 = centerX + ((normX / dist) * (dist + paddingX)); + y2 = centerY + ((normY / dist) * (dist + paddingY)); + } + + context.save(); + context.beginPath(); + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + + context.closePath(); + + context.clip(); + + // Compute matrix transform + const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); + const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); + const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); + const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); + const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); + const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); + const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); + + context.transform( + deltaA / delta, + deltaD / delta, + deltaB / delta, + deltaE / delta, + deltaC / delta, + deltaF / delta + ); + + context.drawImage( + textureSource, + 0, + 0, + textureWidth * base.resolution, + textureHeight * base.resolution, + 0, + 0, + textureWidth, + textureHeight + ); + + context.restore(); + } + + /** + * Renders a flat Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - The Mesh to render + */ + renderMeshFlat(mesh) + { + const context = this.renderer.context; + const vertices = mesh.vertices; + const length = vertices.length / 2; + + // this.count++; + + context.beginPath(); + + for (let i = 1; i < length - 2; ++i) + { + // draw some triangles! + const index = i * 2; + + const x0 = vertices[index]; + const y0 = vertices[index + 1]; + + const x1 = vertices[index + 2]; + const y1 = vertices[index + 3]; + + const x2 = vertices[index + 4]; + const y2 = vertices[index + 5]; + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + } + + context.fillStyle = '#FF0000'; + context.fill(); + context.closePath(); + } + + /** + * destroy the the renderer. + * + */ + destroy() + { + this.renderer = null; + } +} + +core.CanvasRenderer.registerPlugin('mesh', MeshSpriteRenderer); diff --git a/src/mesh/index.js b/src/mesh/index.js index 473d22a..4d1cad6 100644 --- a/src/mesh/index.js +++ b/src/mesh/index.js @@ -2,7 +2,8 @@ * @namespace PIXI.mesh */ export { default as Mesh } from './Mesh'; +export { default as MeshRenderer } from './webgl/MeshRenderer'; +export { default as CanvasMeshRenderer } from './canvas/CanvasMeshRenderer'; export { default as Plane } from './Plane'; export { default as NineSlicePlane } from './NineSlicePlane'; export { default as Rope } from './Rope'; -export { default as MeshShader } from './webgl/MeshShader'; diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js new file mode 100644 index 0000000..73d78e6 --- /dev/null +++ b/src/mesh/webgl/MeshRenderer.js @@ -0,0 +1,108 @@ +import * as core from '../../core'; +import glCore from 'pixi-gl-core'; +import { default as Mesh } from '../Mesh'; + +const glslify = require('glslify'); // eslint-disable-line no-undef + +/** + * WebGL renderer plugin for tiling sprites + */ +export class MeshRenderer extends core.ObjectRenderer { + + /** + * constructor for renderer + * + * @param {WebGLRenderer} renderer The renderer this tiling awesomeness works for. + */ + constructor(renderer) + { + super(renderer); + + this.shader = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + onContextChange() + { + const gl = this.renderer.gl; + + this.shader = new core.Shader(gl, + glslify('./mesh.vert'), + glslify('./mesh.frag')); + } + + /** + * renders mesh + * + * @param {PIXI.mesh.Mesh} mesh mesh instance + */ + render(mesh) + { + const renderer = this.renderer; + const gl = renderer.gl; + const texture = mesh._texture; + + if (!texture.valid) + { + return; + } + + let glData = mesh._glDatas[renderer.CONTEXT_UID]; + + if (!glData) + { + glData = { + shader: this.shader, + vertexBuffer: glCore.GLBuffer.createVertexBuffer(gl, mesh.vertices, gl.STREAM_DRAW), + uvBuffer: glCore.GLBuffer.createVertexBuffer(gl, mesh.uvs, gl.STREAM_DRAW), + indexBuffer: glCore.GLBuffer.createIndexBuffer(gl, mesh.indices, gl.STATIC_DRAW), + // build the vao object that will render.. + vao: new glCore.VertexArrayObject(gl), + dirty: mesh.dirty, + indexDirty: mesh.indexDirty, + }; + + // build the vao object that will render.. + glData.vao = new glCore.VertexArrayObject(gl) + .addIndex(glData.indexBuffer) + .addAttribute(glData.vertexBuffer, glData.shader.attributes.aVertexPosition, gl.FLOAT, false, 2 * 4, 0) + .addAttribute(glData.uvBuffer, glData.shader.attributes.aTextureCoord, gl.FLOAT, false, 2 * 4, 0); + + mesh._glDatas[renderer.CONTEXT_UID] = glData; + } + + if (mesh.dirty !== glData.dirty) + { + glData.dirty = mesh.dirty; + glData.uvBuffer.upload(); + } + + if (mesh.indexDirty !== glData.indexDirty) + { + glData.indexDirty = mesh.indexDirty; + glData.indexBuffer.upload(); + } + + glData.vertexBuffer.upload(); + + renderer.bindShader(glData.shader); + renderer.bindTexture(texture, 0); + renderer.state.setBlendMode(mesh.blendMode); + + glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); + glData.shader.uniforms.alpha = mesh.worldAlpha; + glData.shader.uniforms.tint = mesh.tintRgb; + + const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; + + glData.vao.bind() + .draw(drawMode, mesh.indices.length) + .unbind(); + } +} + +core.WebGLRenderer.registerPlugin('mesh', MeshRenderer); diff --git a/src/mesh/webgl/MeshShader.js b/src/mesh/webgl/MeshShader.js deleted file mode 100644 index 1bbdc0e..0000000 --- a/src/mesh/webgl/MeshShader.js +++ /dev/null @@ -1,46 +0,0 @@ -import Shader from '../../core/Shader'; - -/** - * @class - * @extends PIXI.Shader - * @memberof PIXI.mesh - */ -export default class MeshShader extends Shader -{ - /** - * @param {WebGLRenderingContext} gl - The WebGLRenderingContext. - */ - constructor(gl) - { - super( - gl, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'varying vec2 vTextureCoord;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', // eslint-disable-line max-len - ' vTextureCoord = aTextureCoord;', - '}', - ].join('\n'), - [ - 'varying vec2 vTextureCoord;', - 'uniform float alpha;', - 'uniform vec3 tint;', - - 'uniform sampler2D uSampler;', - - 'void main(void){', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha);', - // ' gl_FragColor = vec4(1.0);', - '}', - ].join('\n') - ); - } -} diff --git a/src/mesh/webgl/mesh.frag b/src/mesh/webgl/mesh.frag new file mode 100644 index 0000000..9e0b634 --- /dev/null +++ b/src/mesh/webgl/mesh.frag @@ -0,0 +1,10 @@ +varying vec2 vTextureCoord; +uniform float alpha; +uniform vec3 tint; + +uniform sampler2D uSampler; + +void main(void) +{ + gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha); +} diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert new file mode 100644 index 0000000..a337aef --- /dev/null +++ b/src/mesh/webgl/mesh.vert @@ -0,0 +1,14 @@ +attribute vec2 aVertexPosition; +attribute vec2 aTextureCoord; + +uniform mat3 translationMatrix; +uniform mat3 projectionMatrix; + +varying vec2 vTextureCoord; + +void main(void) +{ + gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); + + vTextureCoord = aTextureCoord; +} diff --git a/test/core/Container.js b/test/core/Container.js index 5ee605b..be3bf1a 100644 --- a/test/core/Container.js +++ b/test/core/Container.js @@ -301,6 +301,12 @@ { container.swapChildren(child1, child2); }); + + // second call required to complete returned index coverage + assertCallToOnChildrenChanged(container, 0, () => + { + container.swapChildren(child1, child2); + }); }); it('should not call onChildrenChange if supplied children are equal', () => @@ -385,6 +391,175 @@ }); }); + describe('removeChildren', () => + { + it('should remove all children when no arguments supplied', () => + { + const container = new PIXI.Container(); + let removed = []; + + container.addChild(new PIXI.DisplayObject(), new PIXI.DisplayObject(), new PIXI.DisplayObject()); + + expect(container.children.length).to.be.equals(3); + + removed = container.removeChildren(); + + expect(container.children.length).to.be.equals(0); + expect(removed.length).to.be.equals(3); + }); + + it('should return empty array if no children', () => + { + const container = new PIXI.Container(); + const removed = container.removeChildren(); + + expect(removed.length).to.be.equals(0); + }); + + it('should handle a range greater than length', () => + { + const container = new PIXI.Container(); + let removed = []; + + container.addChild(new PIXI.DisplayObject()); + + removed = container.removeChildren(0, 2); + expect(removed.length).to.be.equals(1); + }); + + it('should throw outside acceptable range', () => + { + const container = new PIXI.Container(); + + container.addChild(new PIXI.DisplayObject()); + + expect(() => container.removeChildren(2)) + .to.throw('removeChildren: numeric values are outside the acceptable range.'); + expect(() => container.removeChildren(-1)) + .to.throw('removeChildren: numeric values are outside the acceptable range.'); + expect(() => container.removeChildren(-1, 1)) + .to.throw('removeChildren: numeric values are outside the acceptable range.'); + }); + }); + + describe('destroy', () => + { + it('should not destroy children by default', () => + { + const container = new PIXI.Container(); + const child = new PIXI.DisplayObject(); + + container.addChild(child); + container.destroy(); + + expect(container.children.length).to.be.equals(0); + expect(child.transform).to.not.be.null; + }); + + it('should allow children destroy', () => + { + let container = new PIXI.Container(); + let child = new PIXI.DisplayObject(); + + container.addChild(child); + container.destroy({ children: true }); + + expect(container.children.length).to.be.equals(0); + expect(container.transform).to.be.null; + expect(child.transform).to.be.null; + + container = new PIXI.Container(); + child = new PIXI.DisplayObject(); + + container.addChild(child); + container.destroy(true); + + expect(container.children.length).to.be.equals(0); + expect(container.transform).to.be.null; + expect(child.transform).to.be.null; + }); + }); + + describe('width', () => + { + it('should reflect scale', () => + { + const container = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + + graphics.drawRect(0, 0, 10, 10); + container.addChild(graphics); + container.scale.x = 2; + + expect(container.width).to.be.equals(20); + }); + + it('should adjust scale', () => + { + const container = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + + graphics.drawRect(0, 0, 10, 10); + container.addChild(graphics); + + container.width = 20; + + expect(container.width).to.be.equals(20); + expect(container.scale.x).to.be.equals(2); + }); + + it('should reset scale', () => + { + const container = new PIXI.Container(); + + container.scale.x = 2; + container.width = 5; + + expect(container.width).to.be.equals(0); + expect(container.scale.x).to.be.equals(1); + }); + }); + + describe('height', () => + { + it('should reflect scale', () => + { + const container = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + + graphics.drawRect(0, 0, 10, 10); + container.addChild(graphics); + container.scale.y = 2; + + expect(container.height).to.be.equals(20); + }); + + it('should adjust scale', () => + { + const container = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + + graphics.drawRect(0, 0, 10, 10); + container.addChild(graphics); + + container.height = 20; + + expect(container.height).to.be.equals(20); + expect(container.scale.y).to.be.equals(2); + }); + + it('should reset scale', () => + { + const container = new PIXI.Container(); + + container.scale.y = 2; + container.height = 5; + + expect(container.height).to.be.equals(0); + expect(container.scale.y).to.be.equals(1); + }); + }); + function assertWebGLNotRendered(container) { let rendered = false; diff --git a/test/core/Sprite.js b/test/core/Sprite.js index 38d77ca..3eb2f56 100755 --- a/test/core/Sprite.js +++ b/test/core/Sprite.js @@ -4,7 +4,7 @@ { describe('width', function () { - it('should not be negative for nagative scale.x', function () + it('should not be negative for negative scale.x', function () { var sprite = new PIXI.Sprite(); @@ -34,7 +34,7 @@ describe('height', function () { - it('should not be negative for nagative scale.y', function () + it('should not be negative for negative scale.y', function () { var sprite = new PIXI.Sprite(); diff --git a/test/core/getGlobalPosition.js b/test/core/getGlobalPosition.js new file mode 100644 index 0000000..cb0cd29 --- /dev/null +++ b/test/core/getGlobalPosition.js @@ -0,0 +1,34 @@ +'use strict'; + +describe('getGlobalPosition', function () +{ + it('should return correct global coordinates of a displayObject, without depending on its pivot', function () + { + var parent = new PIXI.Container(); + + var container = new PIXI.Container(); + + parent.addChild(container); + + parent.position.set(100, 100); + parent.rotation = Math.PI; + parent.scale.set(2, 2); + container.position.set(10, -30); + container.pivot.set(1000, 1000); + + var globalPoint; + + globalPoint = container.getGlobalPosition(globalPoint, false); + + expect(globalPoint.x).to.equal(80); + expect(globalPoint.y).to.equal(160); + + // check but skipUpdate + + parent.position.set(200, 200); + globalPoint = container.getGlobalPosition(globalPoint, true); + + expect(globalPoint.x).to.equal(80); + expect(globalPoint.y).to.equal(160); + }); +}); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index 455d59c..0ce75d2 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -18,6 +18,14 @@ * let sprite2 = new PIXI.Sprite(texture); * ``` * + * Textures made from SVGs, loaded or not, cannot be used before the file finishes processing. You can check for this by checking the sprite's _textureID property. + * ```js + * var texture = PIXI.Texture.fromImage('assets/image.svg'); + * var sprite1 = new PIXI.Sprite(texture); + * //sprite1._textureID should not be undefined if the texture has finished processing the SVG file + * ``` + * You can use a ticker or rAF to ensure your sprites load the finished textures after processing. See issue #3068. + * * @class * @extends EventEmitter * @memberof PIXI diff --git a/src/extras/getGlobalPosition.js b/src/extras/getGlobalPosition.js index b71682f..a23761f 100644 --- a/src/extras/getGlobalPosition.js +++ b/src/extras/getGlobalPosition.js @@ -1,20 +1,20 @@ import * as core from '../core'; /** - * Returns the global position of the displayObject + * Returns the global position of the displayObject. Does not depend on object scale, rotation and pivot. * * @memberof PIXI.DisplayObject# * @param {Point} point - the point to write the global value to. If null a new point will be returned + * @param {boolean} skipUpdate - setting to true will stop the transforms of the scene graph from + * being updated. This means the calculation returned MAY be out of date BUT will give you a + * nice performance boost * @return {Point} The updated point */ -core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point()) +core.DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point = new core.Point(), skipUpdate = false) { if (this.parent) { - this.displayObjectUpdateTransform(); - - point.x = this.worldTransform.tx; - point.y = this.worldTransform.ty; + this.parent.toGlobal(this.position, point, skipUpdate); } else { diff --git a/src/filters/blur/BlurFilter.js b/src/filters/blur/BlurFilter.js index 4919d10..5b3c3a9 100644 --- a/src/filters/blur/BlurFilter.js +++ b/src/filters/blur/BlurFilter.js @@ -68,7 +68,7 @@ set blur(value) { this.blurXFilter.blur = this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -113,7 +113,7 @@ set blurX(value) { this.blurXFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } /** @@ -136,6 +136,6 @@ set blurY(value) { this.blurYFilter.blur = value; - this.padding = Math.max(Math.abs(this.blurYFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; + this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2; } } diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 4719b6a..2ced6dc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,6 +1,4 @@ import * as core from '../core'; -import glCore from 'pixi-gl-core'; -import Shader from './webgl/MeshShader'; const tempPoint = new core.Point(); const tempPolygon = new core.Polygon(); @@ -59,11 +57,17 @@ this.indices = indices || new Uint16Array([0, 1, 3, 2]); /** - * Whether the Mesh is dirty or not + * Version of mesh uvs are dirty or not * * @member {number} */ this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ this.indexDirty = 0; /** @@ -122,62 +126,8 @@ */ _renderWebGL(renderer) { - // get rid of any thing that may be batching. - renderer.flush(); - - // renderer.plugins.mesh.render(this); - const gl = renderer.gl; - let glData = this._glDatas[renderer.CONTEXT_UID]; - - if (!glData) - { - glData = { - shader: new Shader(gl), - vertexBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.vertices, gl.STREAM_DRAW), - uvBuffer: glCore.GLBuffer.createVertexBuffer(gl, this.uvs, gl.STREAM_DRAW), - indexBuffer: glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW), - // build the vao object that will render.. - vao: new glCore.VertexArrayObject(gl), - dirty: this.dirty, - indexDirty: this.indexDirty, - }; - - // build the vao object that will render.. - glData.vao = new glCore.VertexArrayObject(gl) - .addIndex(glData.indexBuffer) - .addAttribute(glData.vertexBuffer, glData.shader.attributes.aVertexPosition, gl.FLOAT, false, 2 * 4, 0) - .addAttribute(glData.uvBuffer, glData.shader.attributes.aTextureCoord, gl.FLOAT, false, 2 * 4, 0); - - this._glDatas[renderer.CONTEXT_UID] = glData; - } - - if (this.dirty !== glData.dirty) - { - glData.dirty = this.dirty; - glData.uvBuffer.upload(); - } - - if (this.indexDirty !== glData.indexDirty) - { - glData.indexDirty = this.indexDirty; - glData.indexBuffer.upload(); - } - - glData.vertexBuffer.upload(); - - renderer.bindShader(glData.shader); - renderer.bindTexture(this._texture, 0); - renderer.state.setBlendMode(this.blendMode); - - glData.shader.uniforms.translationMatrix = this.worldTransform.toArray(true); - glData.shader.uniforms.alpha = this.worldAlpha; - glData.shader.uniforms.tint = this.tintRgb; - - const drawMode = this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; - - glData.vao.bind() - .draw(drawMode, this.indices.length) - .unbind(); + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); } /** @@ -188,240 +138,7 @@ */ _renderCanvas(renderer) { - const context = renderer.context; - - const transform = this.worldTransform; - const res = renderer.resolution; - - if (renderer.roundPixels) - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - (transform.tx * res) | 0, - (transform.ty * res) | 0 - ); - } - else - { - context.setTransform( - transform.a * res, - transform.b * res, - transform.c * res, - transform.d * res, - transform.tx * res, - transform.ty * res - ); - } - - if (this.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) - { - this._renderCanvasTriangleMesh(context); - } - else - { - this._renderCanvasTriangles(context); - } - } - - /** - * Draws the object in Triangle Mesh mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - The current drawing context - */ - _renderCanvasTriangleMesh(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const length = vertices.length / 2; - - // this.count++; - - for (let i = 0; i < length - 2; i++) - { - // draw some triangles! - const index = i * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index, (index + 2), (index + 4)); - } - } - - /** - * Draws the object in triangle mode using canvas - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - */ - _renderCanvasTriangles(context) - { - // draw triangles!! - const vertices = this.vertices; - const uvs = this.uvs; - const indices = this.indices; - const length = indices.length; - // this.count++; - - for (let i = 0; i < length; i += 3) - { - // draw some triangles! - const index0 = indices[i] * 2; - const index1 = indices[i + 1] * 2; - const index2 = indices[i + 2] * 2; - - this._renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2); - } - } - - /** - * Draws one of the triangles that form this Mesh - * - * @private - * @param {CanvasRenderingContext2D} context - the current drawing context - * @param {Float32Array} vertices - a reference to the vertices of the Mesh - * @param {Float32Array} uvs - a reference to the uvs of the Mesh - * @param {number} index0 - the index of the first vertex - * @param {number} index1 - the index of the second vertex - * @param {number} index2 - the index of the third vertex - */ - _renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2) - { - const base = this._texture.baseTexture; - const textureSource = base.source; - const textureWidth = base.width; - const textureHeight = base.height; - - const u0 = uvs[index0] * base.width; - const u1 = uvs[index1] * base.width; - const u2 = uvs[index2] * base.width; - const v0 = uvs[index0 + 1] * base.height; - const v1 = uvs[index1 + 1] * base.height; - const v2 = uvs[index2 + 1] * base.height; - - let x0 = vertices[index0]; - let x1 = vertices[index1]; - let x2 = vertices[index2]; - let y0 = vertices[index0 + 1]; - let y1 = vertices[index1 + 1]; - let y2 = vertices[index2 + 1]; - - if (this.canvasPadding > 0) - { - const paddingX = this.canvasPadding / this.worldTransform.a; - const paddingY = this.canvasPadding / this.worldTransform.d; - const centerX = (x0 + x1 + x2) / 3; - const centerY = (y0 + y1 + y2) / 3; - - let normX = x0 - centerX; - let normY = y0 - centerY; - - let dist = Math.sqrt((normX * normX) + (normY * normY)); - - x0 = centerX + ((normX / dist) * (dist + paddingX)); - y0 = centerY + ((normY / dist) * (dist + paddingY)); - - // - - normX = x1 - centerX; - normY = y1 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x1 = centerX + ((normX / dist) * (dist + paddingX)); - y1 = centerY + ((normY / dist) * (dist + paddingY)); - - normX = x2 - centerX; - normY = y2 - centerY; - - dist = Math.sqrt((normX * normX) + (normY * normY)); - x2 = centerX + ((normX / dist) * (dist + paddingX)); - y2 = centerY + ((normY / dist) * (dist + paddingY)); - } - - context.save(); - context.beginPath(); - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - - context.closePath(); - - context.clip(); - - // Compute matrix transform - const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); - const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); - const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); - const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); - const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); - const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); - const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); - - context.transform( - deltaA / delta, - deltaD / delta, - deltaB / delta, - deltaE / delta, - deltaC / delta, - deltaF / delta - ); - - context.drawImage( - textureSource, - 0, - 0, - textureWidth * base.resolution, - textureHeight * base.resolution, - 0, - 0, - textureWidth, - textureHeight - ); - - context.restore(); - } - - /** - * Renders a flat Mesh - * - * @private - * @param {PIXI.mesh.Mesh} mesh - The Mesh to render - */ - renderMeshFlat(mesh) - { - const context = this.context; - const vertices = mesh.vertices; - const length = vertices.length / 2; - - // this.count++; - - context.beginPath(); - - for (let i = 1; i < length - 2; ++i) - { - // draw some triangles! - const index = i * 2; - - const x0 = vertices[index]; - const y0 = vertices[index + 1]; - - const x1 = vertices[index + 2]; - const y1 = vertices[index + 3]; - - const x2 = vertices[index + 4]; - const y2 = vertices[index + 5]; - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - } - - context.fillStyle = '#FF0000'; - context.fill(); - context.closePath(); + renderer.plugins.mesh.render(this); } /** diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js new file mode 100644 index 0000000..a1ad74c --- /dev/null +++ b/src/mesh/canvas/CanvasMeshRenderer.js @@ -0,0 +1,276 @@ +import * as core from '../../core'; +import { default as Mesh } from '../Mesh'; + +/** + * Renderer dedicated to meshes. + * + * @class + * @private + * @memberof PIXI + */ +export default class MeshSpriteRenderer +{ + /** + * @param {PIXI.CanvasRenderer} renderer - The renderer this downport works for + */ + constructor(renderer) + { + this.renderer = renderer; + } + + /** + * Renders the Mesh + * + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + render(mesh) + { + const renderer = this.renderer; + const context = renderer.context; + + const transform = mesh.worldTransform; + const res = renderer.resolution; + + if (renderer.roundPixels) + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + (transform.tx * res) | 0, + (transform.ty * res) | 0 + ); + } + else + { + context.setTransform( + transform.a * res, + transform.b * res, + transform.c * res, + transform.d * res, + transform.tx * res, + transform.ty * res + ); + } + + if (mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH) + { + this._renderTriangleMesh(mesh); + } + else + { + this._renderTriangles(mesh); + } + } + + /** + * Draws the object in Triangle Mesh mode + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the Mesh to render + */ + _renderTriangleMesh(mesh) + { + // draw triangles!! + const length = mesh.vertices.length / 2; + + for (let i = 0; i < length - 2; i++) + { + // draw some triangles! + const index = i * 2; + + this._renderDrawTriangle(mesh, index, (index + 2), (index + 4)); + } + } + + /** + * Draws the object in triangle mode using canvas + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + */ + _renderTriangles(mesh) + { + // draw triangles!! + const indices = mesh.indices; + const length = indices.length; + + for (let i = 0; i < length; i += 3) + { + // draw some triangles! + const index0 = indices[i] * 2; + const index1 = indices[i + 1] * 2; + const index2 = indices[i + 2] * 2; + + this._renderDrawTriangle(mesh, index0, index1, index2); + } + } + + /** + * Draws one of the triangles that from the Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - the current mesh + * @param {number} index0 - the index of the first vertex + * @param {number} index1 - the index of the second vertex + * @param {number} index2 - the index of the third vertex + */ + _renderDrawTriangle(mesh, index0, index1, index2) + { + const context = this.renderer.context; + const uvs = mesh.uvs; + const vertices = mesh.vertices; + const texture = mesh._texture; + + if (!texture.valid) + { + return; + } + + const base = texture.baseTexture; + const textureSource = base.source; + const textureWidth = base.width; + const textureHeight = base.height; + + const u0 = uvs[index0] * base.width; + const u1 = uvs[index1] * base.width; + const u2 = uvs[index2] * base.width; + const v0 = uvs[index0 + 1] * base.height; + const v1 = uvs[index1 + 1] * base.height; + const v2 = uvs[index2 + 1] * base.height; + + let x0 = vertices[index0]; + let x1 = vertices[index1]; + let x2 = vertices[index2]; + let y0 = vertices[index0 + 1]; + let y1 = vertices[index1 + 1]; + let y2 = vertices[index2 + 1]; + + if (mesh.canvasPadding > 0) + { + const paddingX = mesh.canvasPadding / mesh.worldTransform.a; + const paddingY = mesh.canvasPadding / mesh.worldTransform.d; + const centerX = (x0 + x1 + x2) / 3; + const centerY = (y0 + y1 + y2) / 3; + + let normX = x0 - centerX; + let normY = y0 - centerY; + + let dist = Math.sqrt((normX * normX) + (normY * normY)); + + x0 = centerX + ((normX / dist) * (dist + paddingX)); + y0 = centerY + ((normY / dist) * (dist + paddingY)); + + // + + normX = x1 - centerX; + normY = y1 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x1 = centerX + ((normX / dist) * (dist + paddingX)); + y1 = centerY + ((normY / dist) * (dist + paddingY)); + + normX = x2 - centerX; + normY = y2 - centerY; + + dist = Math.sqrt((normX * normX) + (normY * normY)); + x2 = centerX + ((normX / dist) * (dist + paddingX)); + y2 = centerY + ((normY / dist) * (dist + paddingY)); + } + + context.save(); + context.beginPath(); + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + + context.closePath(); + + context.clip(); + + // Compute matrix transform + const delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); + const deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); + const deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); + const deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); + const deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); + const deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); + const deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); + + context.transform( + deltaA / delta, + deltaD / delta, + deltaB / delta, + deltaE / delta, + deltaC / delta, + deltaF / delta + ); + + context.drawImage( + textureSource, + 0, + 0, + textureWidth * base.resolution, + textureHeight * base.resolution, + 0, + 0, + textureWidth, + textureHeight + ); + + context.restore(); + } + + /** + * Renders a flat Mesh + * + * @private + * @param {PIXI.mesh.Mesh} mesh - The Mesh to render + */ + renderMeshFlat(mesh) + { + const context = this.renderer.context; + const vertices = mesh.vertices; + const length = vertices.length / 2; + + // this.count++; + + context.beginPath(); + + for (let i = 1; i < length - 2; ++i) + { + // draw some triangles! + const index = i * 2; + + const x0 = vertices[index]; + const y0 = vertices[index + 1]; + + const x1 = vertices[index + 2]; + const y1 = vertices[index + 3]; + + const x2 = vertices[index + 4]; + const y2 = vertices[index + 5]; + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + } + + context.fillStyle = '#FF0000'; + context.fill(); + context.closePath(); + } + + /** + * destroy the the renderer. + * + */ + destroy() + { + this.renderer = null; + } +} + +core.CanvasRenderer.registerPlugin('mesh', MeshSpriteRenderer); diff --git a/src/mesh/index.js b/src/mesh/index.js index 473d22a..4d1cad6 100644 --- a/src/mesh/index.js +++ b/src/mesh/index.js @@ -2,7 +2,8 @@ * @namespace PIXI.mesh */ export { default as Mesh } from './Mesh'; +export { default as MeshRenderer } from './webgl/MeshRenderer'; +export { default as CanvasMeshRenderer } from './canvas/CanvasMeshRenderer'; export { default as Plane } from './Plane'; export { default as NineSlicePlane } from './NineSlicePlane'; export { default as Rope } from './Rope'; -export { default as MeshShader } from './webgl/MeshShader'; diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js new file mode 100644 index 0000000..73d78e6 --- /dev/null +++ b/src/mesh/webgl/MeshRenderer.js @@ -0,0 +1,108 @@ +import * as core from '../../core'; +import glCore from 'pixi-gl-core'; +import { default as Mesh } from '../Mesh'; + +const glslify = require('glslify'); // eslint-disable-line no-undef + +/** + * WebGL renderer plugin for tiling sprites + */ +export class MeshRenderer extends core.ObjectRenderer { + + /** + * constructor for renderer + * + * @param {WebGLRenderer} renderer The renderer this tiling awesomeness works for. + */ + constructor(renderer) + { + super(renderer); + + this.shader = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + onContextChange() + { + const gl = this.renderer.gl; + + this.shader = new core.Shader(gl, + glslify('./mesh.vert'), + glslify('./mesh.frag')); + } + + /** + * renders mesh + * + * @param {PIXI.mesh.Mesh} mesh mesh instance + */ + render(mesh) + { + const renderer = this.renderer; + const gl = renderer.gl; + const texture = mesh._texture; + + if (!texture.valid) + { + return; + } + + let glData = mesh._glDatas[renderer.CONTEXT_UID]; + + if (!glData) + { + glData = { + shader: this.shader, + vertexBuffer: glCore.GLBuffer.createVertexBuffer(gl, mesh.vertices, gl.STREAM_DRAW), + uvBuffer: glCore.GLBuffer.createVertexBuffer(gl, mesh.uvs, gl.STREAM_DRAW), + indexBuffer: glCore.GLBuffer.createIndexBuffer(gl, mesh.indices, gl.STATIC_DRAW), + // build the vao object that will render.. + vao: new glCore.VertexArrayObject(gl), + dirty: mesh.dirty, + indexDirty: mesh.indexDirty, + }; + + // build the vao object that will render.. + glData.vao = new glCore.VertexArrayObject(gl) + .addIndex(glData.indexBuffer) + .addAttribute(glData.vertexBuffer, glData.shader.attributes.aVertexPosition, gl.FLOAT, false, 2 * 4, 0) + .addAttribute(glData.uvBuffer, glData.shader.attributes.aTextureCoord, gl.FLOAT, false, 2 * 4, 0); + + mesh._glDatas[renderer.CONTEXT_UID] = glData; + } + + if (mesh.dirty !== glData.dirty) + { + glData.dirty = mesh.dirty; + glData.uvBuffer.upload(); + } + + if (mesh.indexDirty !== glData.indexDirty) + { + glData.indexDirty = mesh.indexDirty; + glData.indexBuffer.upload(); + } + + glData.vertexBuffer.upload(); + + renderer.bindShader(glData.shader); + renderer.bindTexture(texture, 0); + renderer.state.setBlendMode(mesh.blendMode); + + glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); + glData.shader.uniforms.alpha = mesh.worldAlpha; + glData.shader.uniforms.tint = mesh.tintRgb; + + const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; + + glData.vao.bind() + .draw(drawMode, mesh.indices.length) + .unbind(); + } +} + +core.WebGLRenderer.registerPlugin('mesh', MeshRenderer); diff --git a/src/mesh/webgl/MeshShader.js b/src/mesh/webgl/MeshShader.js deleted file mode 100644 index 1bbdc0e..0000000 --- a/src/mesh/webgl/MeshShader.js +++ /dev/null @@ -1,46 +0,0 @@ -import Shader from '../../core/Shader'; - -/** - * @class - * @extends PIXI.Shader - * @memberof PIXI.mesh - */ -export default class MeshShader extends Shader -{ - /** - * @param {WebGLRenderingContext} gl - The WebGLRenderingContext. - */ - constructor(gl) - { - super( - gl, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'varying vec2 vTextureCoord;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', // eslint-disable-line max-len - ' vTextureCoord = aTextureCoord;', - '}', - ].join('\n'), - [ - 'varying vec2 vTextureCoord;', - 'uniform float alpha;', - 'uniform vec3 tint;', - - 'uniform sampler2D uSampler;', - - 'void main(void){', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha);', - // ' gl_FragColor = vec4(1.0);', - '}', - ].join('\n') - ); - } -} diff --git a/src/mesh/webgl/mesh.frag b/src/mesh/webgl/mesh.frag new file mode 100644 index 0000000..9e0b634 --- /dev/null +++ b/src/mesh/webgl/mesh.frag @@ -0,0 +1,10 @@ +varying vec2 vTextureCoord; +uniform float alpha; +uniform vec3 tint; + +uniform sampler2D uSampler; + +void main(void) +{ + gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha); +} diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert new file mode 100644 index 0000000..a337aef --- /dev/null +++ b/src/mesh/webgl/mesh.vert @@ -0,0 +1,14 @@ +attribute vec2 aVertexPosition; +attribute vec2 aTextureCoord; + +uniform mat3 translationMatrix; +uniform mat3 projectionMatrix; + +varying vec2 vTextureCoord; + +void main(void) +{ + gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); + + vTextureCoord = aTextureCoord; +} diff --git a/test/core/Container.js b/test/core/Container.js index 5ee605b..be3bf1a 100644 --- a/test/core/Container.js +++ b/test/core/Container.js @@ -301,6 +301,12 @@ { container.swapChildren(child1, child2); }); + + // second call required to complete returned index coverage + assertCallToOnChildrenChanged(container, 0, () => + { + container.swapChildren(child1, child2); + }); }); it('should not call onChildrenChange if supplied children are equal', () => @@ -385,6 +391,175 @@ }); }); + describe('removeChildren', () => + { + it('should remove all children when no arguments supplied', () => + { + const container = new PIXI.Container(); + let removed = []; + + container.addChild(new PIXI.DisplayObject(), new PIXI.DisplayObject(), new PIXI.DisplayObject()); + + expect(container.children.length).to.be.equals(3); + + removed = container.removeChildren(); + + expect(container.children.length).to.be.equals(0); + expect(removed.length).to.be.equals(3); + }); + + it('should return empty array if no children', () => + { + const container = new PIXI.Container(); + const removed = container.removeChildren(); + + expect(removed.length).to.be.equals(0); + }); + + it('should handle a range greater than length', () => + { + const container = new PIXI.Container(); + let removed = []; + + container.addChild(new PIXI.DisplayObject()); + + removed = container.removeChildren(0, 2); + expect(removed.length).to.be.equals(1); + }); + + it('should throw outside acceptable range', () => + { + const container = new PIXI.Container(); + + container.addChild(new PIXI.DisplayObject()); + + expect(() => container.removeChildren(2)) + .to.throw('removeChildren: numeric values are outside the acceptable range.'); + expect(() => container.removeChildren(-1)) + .to.throw('removeChildren: numeric values are outside the acceptable range.'); + expect(() => container.removeChildren(-1, 1)) + .to.throw('removeChildren: numeric values are outside the acceptable range.'); + }); + }); + + describe('destroy', () => + { + it('should not destroy children by default', () => + { + const container = new PIXI.Container(); + const child = new PIXI.DisplayObject(); + + container.addChild(child); + container.destroy(); + + expect(container.children.length).to.be.equals(0); + expect(child.transform).to.not.be.null; + }); + + it('should allow children destroy', () => + { + let container = new PIXI.Container(); + let child = new PIXI.DisplayObject(); + + container.addChild(child); + container.destroy({ children: true }); + + expect(container.children.length).to.be.equals(0); + expect(container.transform).to.be.null; + expect(child.transform).to.be.null; + + container = new PIXI.Container(); + child = new PIXI.DisplayObject(); + + container.addChild(child); + container.destroy(true); + + expect(container.children.length).to.be.equals(0); + expect(container.transform).to.be.null; + expect(child.transform).to.be.null; + }); + }); + + describe('width', () => + { + it('should reflect scale', () => + { + const container = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + + graphics.drawRect(0, 0, 10, 10); + container.addChild(graphics); + container.scale.x = 2; + + expect(container.width).to.be.equals(20); + }); + + it('should adjust scale', () => + { + const container = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + + graphics.drawRect(0, 0, 10, 10); + container.addChild(graphics); + + container.width = 20; + + expect(container.width).to.be.equals(20); + expect(container.scale.x).to.be.equals(2); + }); + + it('should reset scale', () => + { + const container = new PIXI.Container(); + + container.scale.x = 2; + container.width = 5; + + expect(container.width).to.be.equals(0); + expect(container.scale.x).to.be.equals(1); + }); + }); + + describe('height', () => + { + it('should reflect scale', () => + { + const container = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + + graphics.drawRect(0, 0, 10, 10); + container.addChild(graphics); + container.scale.y = 2; + + expect(container.height).to.be.equals(20); + }); + + it('should adjust scale', () => + { + const container = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + + graphics.drawRect(0, 0, 10, 10); + container.addChild(graphics); + + container.height = 20; + + expect(container.height).to.be.equals(20); + expect(container.scale.y).to.be.equals(2); + }); + + it('should reset scale', () => + { + const container = new PIXI.Container(); + + container.scale.y = 2; + container.height = 5; + + expect(container.height).to.be.equals(0); + expect(container.scale.y).to.be.equals(1); + }); + }); + function assertWebGLNotRendered(container) { let rendered = false; diff --git a/test/core/Sprite.js b/test/core/Sprite.js index 38d77ca..3eb2f56 100755 --- a/test/core/Sprite.js +++ b/test/core/Sprite.js @@ -4,7 +4,7 @@ { describe('width', function () { - it('should not be negative for nagative scale.x', function () + it('should not be negative for negative scale.x', function () { var sprite = new PIXI.Sprite(); @@ -34,7 +34,7 @@ describe('height', function () { - it('should not be negative for nagative scale.y', function () + it('should not be negative for negative scale.y', function () { var sprite = new PIXI.Sprite(); diff --git a/test/core/getGlobalPosition.js b/test/core/getGlobalPosition.js new file mode 100644 index 0000000..cb0cd29 --- /dev/null +++ b/test/core/getGlobalPosition.js @@ -0,0 +1,34 @@ +'use strict'; + +describe('getGlobalPosition', function () +{ + it('should return correct global coordinates of a displayObject, without depending on its pivot', function () + { + var parent = new PIXI.Container(); + + var container = new PIXI.Container(); + + parent.addChild(container); + + parent.position.set(100, 100); + parent.rotation = Math.PI; + parent.scale.set(2, 2); + container.position.set(10, -30); + container.pivot.set(1000, 1000); + + var globalPoint; + + globalPoint = container.getGlobalPosition(globalPoint, false); + + expect(globalPoint.x).to.equal(80); + expect(globalPoint.y).to.equal(160); + + // check but skipUpdate + + parent.position.set(200, 200); + globalPoint = container.getGlobalPosition(globalPoint, true); + + expect(globalPoint.x).to.equal(80); + expect(globalPoint.y).to.equal(160); + }); +}); diff --git a/test/core/index.js b/test/core/index.js index 899c633..0393e14 100755 --- a/test/core/index.js +++ b/test/core/index.js @@ -8,6 +8,7 @@ require('./Text'); require('./toGlobal'); require('./toLocal'); +require('./getGlobalPosition'); require('./util'); require('./Point'); require('./ObservablePoint');