diff --git a/src/core/index.js b/src/core/index.js index 7ea739a..6f6b9dc 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -39,6 +39,7 @@ export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; +export { default as Shader } from './shader/Shader'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; diff --git a/src/core/index.js b/src/core/index.js index 7ea739a..6f6b9dc 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -39,6 +39,7 @@ export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; +export { default as Shader } from './shader/Shader'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 369d7d3..93cb171 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -1,5 +1,5 @@ -import { GLTexture } from 'pixi-gl-core'; -import { WRAP_MODES, SCALE_MODES } from '../../const'; +import { GLShader } from 'pixi-gl-core'; +import { WRAP_MODES, SCALE_MODES, PRECISION} from '../../const'; import RenderTarget from './utils/RenderTarget'; import { removeItems } from '../../utils'; @@ -35,14 +35,22 @@ { let glShader = shader.glShaders[renderer.CONTEXT_UID] || this.generateShader(shader); + this.renderer._bindGLShader(glShader); this.syncUniforms(glShader, shader); - this.renderer._bindGLShader(glShader); } generateShader(shader) { - const glShader = new GLShader(this.gl, filter.vertexSrc, filter.fragmentSrc, PRECISION.DEFAULT); + + const attribMap = {}; + for (const i in shader.attributeData) + { + attribMap[i] = shader.attributeData[i].location; + } + + console.log(attribMap) + const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); shader.glShaders[renderer.CONTEXT_UID] = glShader; return glShader diff --git a/src/core/index.js b/src/core/index.js index 7ea739a..6f6b9dc 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -39,6 +39,7 @@ export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; +export { default as Shader } from './shader/Shader'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 369d7d3..93cb171 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -1,5 +1,5 @@ -import { GLTexture } from 'pixi-gl-core'; -import { WRAP_MODES, SCALE_MODES } from '../../const'; +import { GLShader } from 'pixi-gl-core'; +import { WRAP_MODES, SCALE_MODES, PRECISION} from '../../const'; import RenderTarget from './utils/RenderTarget'; import { removeItems } from '../../utils'; @@ -35,14 +35,22 @@ { let glShader = shader.glShaders[renderer.CONTEXT_UID] || this.generateShader(shader); + this.renderer._bindGLShader(glShader); this.syncUniforms(glShader, shader); - this.renderer._bindGLShader(glShader); } generateShader(shader) { - const glShader = new GLShader(this.gl, filter.vertexSrc, filter.fragmentSrc, PRECISION.DEFAULT); + + const attribMap = {}; + for (const i in shader.attributeData) + { + attribMap[i] = shader.attributeData[i].location; + } + + console.log(attribMap) + const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); shader.glShaders[renderer.CONTEXT_UID] = glShader; return glShader diff --git a/src/core/shader/Shader.js b/src/core/shader/Shader.js index 9666db0..fb0a85a 100644 --- a/src/core/shader/Shader.js +++ b/src/core/shader/Shader.js @@ -1,4 +1,5 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; // let math = require('../../../math'); /** @@ -33,6 +34,9 @@ // currently this does not extract structs only default types this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc, 'projectionMatrix|uSampler'); + this.attributeData = extractAttributesFromSrc(this.vertexSrc); + + this.uniforms = {}; for (const i in this.uniformData) diff --git a/src/core/index.js b/src/core/index.js index 7ea739a..6f6b9dc 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -39,6 +39,7 @@ export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; +export { default as Shader } from './shader/Shader'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 369d7d3..93cb171 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -1,5 +1,5 @@ -import { GLTexture } from 'pixi-gl-core'; -import { WRAP_MODES, SCALE_MODES } from '../../const'; +import { GLShader } from 'pixi-gl-core'; +import { WRAP_MODES, SCALE_MODES, PRECISION} from '../../const'; import RenderTarget from './utils/RenderTarget'; import { removeItems } from '../../utils'; @@ -35,14 +35,22 @@ { let glShader = shader.glShaders[renderer.CONTEXT_UID] || this.generateShader(shader); + this.renderer._bindGLShader(glShader); this.syncUniforms(glShader, shader); - this.renderer._bindGLShader(glShader); } generateShader(shader) { - const glShader = new GLShader(this.gl, filter.vertexSrc, filter.fragmentSrc, PRECISION.DEFAULT); + + const attribMap = {}; + for (const i in shader.attributeData) + { + attribMap[i] = shader.attributeData[i].location; + } + + console.log(attribMap) + const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); shader.glShaders[renderer.CONTEXT_UID] = glShader; return glShader diff --git a/src/core/shader/Shader.js b/src/core/shader/Shader.js index 9666db0..fb0a85a 100644 --- a/src/core/shader/Shader.js +++ b/src/core/shader/Shader.js @@ -1,4 +1,5 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; // let math = require('../../../math'); /** @@ -33,6 +34,9 @@ // currently this does not extract structs only default types this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc, 'projectionMatrix|uSampler'); + this.attributeData = extractAttributesFromSrc(this.vertexSrc); + + this.uniforms = {}; for (const i in this.uniformData) diff --git a/src/core/shader/extractAttributesFromSrc.js b/src/core/shader/extractAttributesFromSrc.js new file mode 100644 index 0000000..b190ef4 --- /dev/null +++ b/src/core/shader/extractAttributesFromSrc.js @@ -0,0 +1,75 @@ +import glCore from 'pixi-gl-core'; + +const defaultValue = glCore.shader.defaultValue; + +function extractAttributesFromSrc(vertexSrc, mask) +{ + const vertAttributes = extractAttributesFromString(vertexSrc, mask); + + return vertAttributes; +} + +function extractAttributesFromString(string) +{ + const maskRegex = new RegExp('^(projectionMatrix|uSampler|filterArea)$'); + + const attributesArray = []; + let nameSplit; + const a = 20; + + // clean the lines a little - remove extra spaces / teabs etc + // then split along ';' + const lines = string.replace(/\s+/g, ' ') + .split(/\s*;\s*/); + + // loop through.. + for (let i = 0; i < lines.length; i++) + { + const line = lines[i].trim(); + + if (line.indexOf('attribute') > -1) + { + const splitLine = line.split(' '); + const type = splitLine[1]; + + let name = splitLine[2]; + let size = 1; + + if (name.indexOf('[') > -1) + { + // array! + nameSplit = name.split(/\[|\]/); + name = nameSplit[0]; + size *= Number(nameSplit[1]); + } + + if (!name.match(maskRegex)) + { + attributesArray.push({ + value: defaultValue(type, size), + name, + location: 0, + type, + }); + } + } + } + + attributesArray.sort(function (a, b) { + return (a.name > b.name) ? 1 : -1; + }); + + const attributes = {}; + + // now lets sort them alphabetically.. + for (let i = 0; i < attributesArray.length; i++) + { + const attrib = attributesArray[i]; + attrib.location = i; + attributes[attrib.name] = attrib; + } + + return attributes; +} + +export default extractAttributesFromSrc; diff --git a/src/core/index.js b/src/core/index.js index 7ea739a..6f6b9dc 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -39,6 +39,7 @@ export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; +export { default as Shader } from './shader/Shader'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 369d7d3..93cb171 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -1,5 +1,5 @@ -import { GLTexture } from 'pixi-gl-core'; -import { WRAP_MODES, SCALE_MODES } from '../../const'; +import { GLShader } from 'pixi-gl-core'; +import { WRAP_MODES, SCALE_MODES, PRECISION} from '../../const'; import RenderTarget from './utils/RenderTarget'; import { removeItems } from '../../utils'; @@ -35,14 +35,22 @@ { let glShader = shader.glShaders[renderer.CONTEXT_UID] || this.generateShader(shader); + this.renderer._bindGLShader(glShader); this.syncUniforms(glShader, shader); - this.renderer._bindGLShader(glShader); } generateShader(shader) { - const glShader = new GLShader(this.gl, filter.vertexSrc, filter.fragmentSrc, PRECISION.DEFAULT); + + const attribMap = {}; + for (const i in shader.attributeData) + { + attribMap[i] = shader.attributeData[i].location; + } + + console.log(attribMap) + const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); shader.glShaders[renderer.CONTEXT_UID] = glShader; return glShader diff --git a/src/core/shader/Shader.js b/src/core/shader/Shader.js index 9666db0..fb0a85a 100644 --- a/src/core/shader/Shader.js +++ b/src/core/shader/Shader.js @@ -1,4 +1,5 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; // let math = require('../../../math'); /** @@ -33,6 +34,9 @@ // currently this does not extract structs only default types this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc, 'projectionMatrix|uSampler'); + this.attributeData = extractAttributesFromSrc(this.vertexSrc); + + this.uniforms = {}; for (const i in this.uniformData) diff --git a/src/core/shader/extractAttributesFromSrc.js b/src/core/shader/extractAttributesFromSrc.js new file mode 100644 index 0000000..b190ef4 --- /dev/null +++ b/src/core/shader/extractAttributesFromSrc.js @@ -0,0 +1,75 @@ +import glCore from 'pixi-gl-core'; + +const defaultValue = glCore.shader.defaultValue; + +function extractAttributesFromSrc(vertexSrc, mask) +{ + const vertAttributes = extractAttributesFromString(vertexSrc, mask); + + return vertAttributes; +} + +function extractAttributesFromString(string) +{ + const maskRegex = new RegExp('^(projectionMatrix|uSampler|filterArea)$'); + + const attributesArray = []; + let nameSplit; + const a = 20; + + // clean the lines a little - remove extra spaces / teabs etc + // then split along ';' + const lines = string.replace(/\s+/g, ' ') + .split(/\s*;\s*/); + + // loop through.. + for (let i = 0; i < lines.length; i++) + { + const line = lines[i].trim(); + + if (line.indexOf('attribute') > -1) + { + const splitLine = line.split(' '); + const type = splitLine[1]; + + let name = splitLine[2]; + let size = 1; + + if (name.indexOf('[') > -1) + { + // array! + nameSplit = name.split(/\[|\]/); + name = nameSplit[0]; + size *= Number(nameSplit[1]); + } + + if (!name.match(maskRegex)) + { + attributesArray.push({ + value: defaultValue(type, size), + name, + location: 0, + type, + }); + } + } + } + + attributesArray.sort(function (a, b) { + return (a.name > b.name) ? 1 : -1; + }); + + const attributes = {}; + + // now lets sort them alphabetically.. + for (let i = 0; i < attributesArray.length; i++) + { + const attrib = attributesArray[i]; + attrib.location = i; + attributes[attrib.name] = attrib; + } + + return attributes; +} + +export default extractAttributesFromSrc; diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 21e5a61..83418e0 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,78 +1,29 @@ import * as core from '../core'; -const tempPoint = new core.Point(); -const tempPolygon = new core.Polygon(); - /** * Base mesh class * @class * @extends PIXI.Container * @memberof PIXI.mesh + * @param texture {PIXI.Texture} The texture to use + * @param [vertices] {Float32Array} if you want to specify the vertices + * @param [uvs] {Float32Array} if you want to specify the uvs + * @param [indices] {Uint16Array} if you want to specify the indices + * @param [drawMode] {number} the drawMode, can be any of the Mesh.DRAW_MODES consts */ export default class Mesh extends core.Container { - /** - * @param {PIXI.Texture} texture - The texture to use - * @param {Float32Array} [vertices] - if you want to specify the vertices - * @param {Float32Array} [uvs] - if you want to specify the uvs - * @param {Uint16Array} [indices] - if you want to specify the indices - * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts - */ - constructor(texture, vertices, uvs, indices, drawMode) + + constructor(geometry, shader, drawMode) { super(); - /** - * The texture of the Mesh - * - * @member {PIXI.Texture} - * @private - */ - this._texture = null; + this.geometry = geometry; + + this.shader = shader; /** - * The Uvs of the Mesh - * - * @member {Float32Array} - */ - this.uvs = uvs || new Float32Array([0, 0, - 1, 0, - 1, 1, - 0, 1]); - - /** - * An array of vertices - * - * @member {Float32Array} - */ - this.vertices = vertices || new Float32Array([0, 0, - 100, 0, - 100, 100, - 0, 100]); - - /* - * @member {Uint16Array} An array containing the indices of the vertices - */ - // TODO auto generate this based on draw mode! - this.indices = indices || new Uint16Array([0, 1, 3, 2]); - - /** - * Version of mesh uvs are dirty or not - * - * @member {number} - */ - this.dirty = 0; - - /** - * Version of mesh indices - * - * @member {number} - */ - this.indexDirty = 0; - - /** - * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove - * any blend mode. + * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove any blend mode. * * @member {number} * @default PIXI.BLEND_MODES.NORMAL @@ -81,12 +32,10 @@ this.blendMode = core.BLEND_MODES.NORMAL; /** - * Triangles in canvas mode are automatically antialiased, use this value to force triangles - * to overlap a bit with each other. + * Triangles in canvas mode are automatically antialiased, use this value to force triangles to overlap a bit with each other. * * @member {number} */ - this.canvasPadding = 0; /** * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.Mesh.DRAW_MODES} consts @@ -95,40 +44,13 @@ * @see PIXI.mesh.Mesh.DRAW_MODES */ this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; - - // run texture setter; - this.texture = texture; - - /** - * The default shader that is used if a mesh doesn't have a more specific one. - * - * @member {PIXI.Shader} - */ - this.shader = null; - - /** - * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any - * tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - */ - this.tintRgb = new Float32Array([1, 1, 1]); - - /** - * A map of renderer IDs to webgl render data - * - * @private - * @member {object} - */ - this._glDatas = {}; } /** * Renders the object using the WebGL renderer * + * @param renderer {PIXI.WebGLRenderer} a reference to the WebGL renderer * @private - * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer */ _renderWebGL(renderer) { @@ -137,151 +59,43 @@ } /** - * Renders the object using the Canvas renderer + * Calculates the bounds of the mesh. The bounds calculation takes the worldTransform into account. * - * @private - * @param {PIXI.CanvasRenderer} renderer - The canvas renderer. - */ - _renderCanvas(renderer) - { - renderer.plugins.mesh.render(this); - } - - /** - * When the texture is updated, this event will fire to update the scale and frame - * - * @private - */ - _onTextureUpdate() - { - /* empty */ - } - - /** - * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. - * + * @param [matrix=this.worldTransform] {PIXI.Matrix} the transformation matrix of the sprite + * @return {PIXI.Rectangle} the framing rectangle */ _calculateBounds() { - // TODO - we can cache local bounds and use them if they are dirty (like graphics) - this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length); + // The position property could be set manually? + if (this.geometry.attributes.aVertexPosition) + { + const vertices = this.geometry.attributes.aVertexPosition.buffer.data; + + // TODO - we can cache local bounds and use them if they are dirty (like graphics) + this._bounds.addVertices(this.transform, vertices, 0, vertices.length); + } } - /** + /** * Tests if a point is inside this mesh. Works only for TRIANGLE_MESH * - * @param {PIXI.Point} point - the point to test + * @param point {PIXI.Point} the point to test * @return {boolean} the result of the test */ - containsPoint(point) - { - if (!this.getBounds().contains(point.x, point.y)) - { - return false; - } - this.worldTransform.applyInverse(point, tempPoint); - - const vertices = this.vertices; - const points = tempPolygon.points; - const indices = this.indices; - const len = this.indices.length; - const step = this.drawMode === Mesh.DRAW_MODES.TRIANGLES ? 3 : 1; - - for (let i = 0; i + 2 < len; i += step) - { - const ind0 = indices[i] * 2; - const ind1 = indices[i + 1] * 2; - const ind2 = indices[i + 2] * 2; - - points[0] = vertices[ind0]; - points[1] = vertices[ind0 + 1]; - points[2] = vertices[ind1]; - points[3] = vertices[ind1 + 1]; - points[4] = vertices[ind2]; - points[5] = vertices[ind2 + 1]; - - if (tempPolygon.contains(tempPoint.x, tempPoint.y)) - { - return true; - } - } - - return false; - } - - /** - * The texture that the mesh uses. - * - * @member {PIXI.Texture} - * @memberof PIXI.mesh.Mesh# - */ - get texture() - { - return this._texture; - } - - /** - * Sets the texture the mesh uses. - * - * @param {Texture} value - The value to set. - */ - set texture(value) - { - if (this._texture === value) - { - return; - } - - this._texture = value; - - if (value) - { - // wait for the texture to load - if (value.baseTexture.hasLoaded) - { - this._onTextureUpdate(); - } - else - { - value.once('update', this._onTextureUpdate, this); - } - } - } - - /** - * The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - * @default 0xFFFFFF - */ - get tint() - { - return core.utils.rgb2hex(this.tintRgb); - } - - /** - * Sets the tint the mesh uses. - * - * @param {number} value - The value to set. - */ - set tint(value) - { - this.tintRgb = core.utils.hex2rgb(value, this.tintRgb); - } } - /** * Different drawing buffer modes supported * * @static * @constant - * @type {object} - * @property {number} TRIANGLE_MESH - * @property {number} TRIANGLES + * @property {object} DRAW_MODES + * @property {number} DRAW_MODES.TRIANGLE_MESH + * @property {number} DRAW_MODES.TRIANGLES */ Mesh.DRAW_MODES = { - TRIANGLE_MESH: 0, - TRIANGLES: 1, + TRIANGLE_MESH: 1, + TRIANGLES: 2, + POINTS: 3, }; + diff --git a/src/core/index.js b/src/core/index.js index 7ea739a..6f6b9dc 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -39,6 +39,7 @@ export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; +export { default as Shader } from './shader/Shader'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 369d7d3..93cb171 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -1,5 +1,5 @@ -import { GLTexture } from 'pixi-gl-core'; -import { WRAP_MODES, SCALE_MODES } from '../../const'; +import { GLShader } from 'pixi-gl-core'; +import { WRAP_MODES, SCALE_MODES, PRECISION} from '../../const'; import RenderTarget from './utils/RenderTarget'; import { removeItems } from '../../utils'; @@ -35,14 +35,22 @@ { let glShader = shader.glShaders[renderer.CONTEXT_UID] || this.generateShader(shader); + this.renderer._bindGLShader(glShader); this.syncUniforms(glShader, shader); - this.renderer._bindGLShader(glShader); } generateShader(shader) { - const glShader = new GLShader(this.gl, filter.vertexSrc, filter.fragmentSrc, PRECISION.DEFAULT); + + const attribMap = {}; + for (const i in shader.attributeData) + { + attribMap[i] = shader.attributeData[i].location; + } + + console.log(attribMap) + const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); shader.glShaders[renderer.CONTEXT_UID] = glShader; return glShader diff --git a/src/core/shader/Shader.js b/src/core/shader/Shader.js index 9666db0..fb0a85a 100644 --- a/src/core/shader/Shader.js +++ b/src/core/shader/Shader.js @@ -1,4 +1,5 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; // let math = require('../../../math'); /** @@ -33,6 +34,9 @@ // currently this does not extract structs only default types this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc, 'projectionMatrix|uSampler'); + this.attributeData = extractAttributesFromSrc(this.vertexSrc); + + this.uniforms = {}; for (const i in this.uniformData) diff --git a/src/core/shader/extractAttributesFromSrc.js b/src/core/shader/extractAttributesFromSrc.js new file mode 100644 index 0000000..b190ef4 --- /dev/null +++ b/src/core/shader/extractAttributesFromSrc.js @@ -0,0 +1,75 @@ +import glCore from 'pixi-gl-core'; + +const defaultValue = glCore.shader.defaultValue; + +function extractAttributesFromSrc(vertexSrc, mask) +{ + const vertAttributes = extractAttributesFromString(vertexSrc, mask); + + return vertAttributes; +} + +function extractAttributesFromString(string) +{ + const maskRegex = new RegExp('^(projectionMatrix|uSampler|filterArea)$'); + + const attributesArray = []; + let nameSplit; + const a = 20; + + // clean the lines a little - remove extra spaces / teabs etc + // then split along ';' + const lines = string.replace(/\s+/g, ' ') + .split(/\s*;\s*/); + + // loop through.. + for (let i = 0; i < lines.length; i++) + { + const line = lines[i].trim(); + + if (line.indexOf('attribute') > -1) + { + const splitLine = line.split(' '); + const type = splitLine[1]; + + let name = splitLine[2]; + let size = 1; + + if (name.indexOf('[') > -1) + { + // array! + nameSplit = name.split(/\[|\]/); + name = nameSplit[0]; + size *= Number(nameSplit[1]); + } + + if (!name.match(maskRegex)) + { + attributesArray.push({ + value: defaultValue(type, size), + name, + location: 0, + type, + }); + } + } + } + + attributesArray.sort(function (a, b) { + return (a.name > b.name) ? 1 : -1; + }); + + const attributes = {}; + + // now lets sort them alphabetically.. + for (let i = 0; i < attributesArray.length; i++) + { + const attrib = attributesArray[i]; + attrib.location = i; + attributes[attrib.name] = attrib; + } + + return attributes; +} + +export default extractAttributesFromSrc; diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 21e5a61..83418e0 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,78 +1,29 @@ import * as core from '../core'; -const tempPoint = new core.Point(); -const tempPolygon = new core.Polygon(); - /** * Base mesh class * @class * @extends PIXI.Container * @memberof PIXI.mesh + * @param texture {PIXI.Texture} The texture to use + * @param [vertices] {Float32Array} if you want to specify the vertices + * @param [uvs] {Float32Array} if you want to specify the uvs + * @param [indices] {Uint16Array} if you want to specify the indices + * @param [drawMode] {number} the drawMode, can be any of the Mesh.DRAW_MODES consts */ export default class Mesh extends core.Container { - /** - * @param {PIXI.Texture} texture - The texture to use - * @param {Float32Array} [vertices] - if you want to specify the vertices - * @param {Float32Array} [uvs] - if you want to specify the uvs - * @param {Uint16Array} [indices] - if you want to specify the indices - * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts - */ - constructor(texture, vertices, uvs, indices, drawMode) + + constructor(geometry, shader, drawMode) { super(); - /** - * The texture of the Mesh - * - * @member {PIXI.Texture} - * @private - */ - this._texture = null; + this.geometry = geometry; + + this.shader = shader; /** - * The Uvs of the Mesh - * - * @member {Float32Array} - */ - this.uvs = uvs || new Float32Array([0, 0, - 1, 0, - 1, 1, - 0, 1]); - - /** - * An array of vertices - * - * @member {Float32Array} - */ - this.vertices = vertices || new Float32Array([0, 0, - 100, 0, - 100, 100, - 0, 100]); - - /* - * @member {Uint16Array} An array containing the indices of the vertices - */ - // TODO auto generate this based on draw mode! - this.indices = indices || new Uint16Array([0, 1, 3, 2]); - - /** - * Version of mesh uvs are dirty or not - * - * @member {number} - */ - this.dirty = 0; - - /** - * Version of mesh indices - * - * @member {number} - */ - this.indexDirty = 0; - - /** - * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove - * any blend mode. + * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove any blend mode. * * @member {number} * @default PIXI.BLEND_MODES.NORMAL @@ -81,12 +32,10 @@ this.blendMode = core.BLEND_MODES.NORMAL; /** - * Triangles in canvas mode are automatically antialiased, use this value to force triangles - * to overlap a bit with each other. + * Triangles in canvas mode are automatically antialiased, use this value to force triangles to overlap a bit with each other. * * @member {number} */ - this.canvasPadding = 0; /** * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.Mesh.DRAW_MODES} consts @@ -95,40 +44,13 @@ * @see PIXI.mesh.Mesh.DRAW_MODES */ this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; - - // run texture setter; - this.texture = texture; - - /** - * The default shader that is used if a mesh doesn't have a more specific one. - * - * @member {PIXI.Shader} - */ - this.shader = null; - - /** - * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any - * tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - */ - this.tintRgb = new Float32Array([1, 1, 1]); - - /** - * A map of renderer IDs to webgl render data - * - * @private - * @member {object} - */ - this._glDatas = {}; } /** * Renders the object using the WebGL renderer * + * @param renderer {PIXI.WebGLRenderer} a reference to the WebGL renderer * @private - * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer */ _renderWebGL(renderer) { @@ -137,151 +59,43 @@ } /** - * Renders the object using the Canvas renderer + * Calculates the bounds of the mesh. The bounds calculation takes the worldTransform into account. * - * @private - * @param {PIXI.CanvasRenderer} renderer - The canvas renderer. - */ - _renderCanvas(renderer) - { - renderer.plugins.mesh.render(this); - } - - /** - * When the texture is updated, this event will fire to update the scale and frame - * - * @private - */ - _onTextureUpdate() - { - /* empty */ - } - - /** - * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. - * + * @param [matrix=this.worldTransform] {PIXI.Matrix} the transformation matrix of the sprite + * @return {PIXI.Rectangle} the framing rectangle */ _calculateBounds() { - // TODO - we can cache local bounds and use them if they are dirty (like graphics) - this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length); + // The position property could be set manually? + if (this.geometry.attributes.aVertexPosition) + { + const vertices = this.geometry.attributes.aVertexPosition.buffer.data; + + // TODO - we can cache local bounds and use them if they are dirty (like graphics) + this._bounds.addVertices(this.transform, vertices, 0, vertices.length); + } } - /** + /** * Tests if a point is inside this mesh. Works only for TRIANGLE_MESH * - * @param {PIXI.Point} point - the point to test + * @param point {PIXI.Point} the point to test * @return {boolean} the result of the test */ - containsPoint(point) - { - if (!this.getBounds().contains(point.x, point.y)) - { - return false; - } - this.worldTransform.applyInverse(point, tempPoint); - - const vertices = this.vertices; - const points = tempPolygon.points; - const indices = this.indices; - const len = this.indices.length; - const step = this.drawMode === Mesh.DRAW_MODES.TRIANGLES ? 3 : 1; - - for (let i = 0; i + 2 < len; i += step) - { - const ind0 = indices[i] * 2; - const ind1 = indices[i + 1] * 2; - const ind2 = indices[i + 2] * 2; - - points[0] = vertices[ind0]; - points[1] = vertices[ind0 + 1]; - points[2] = vertices[ind1]; - points[3] = vertices[ind1 + 1]; - points[4] = vertices[ind2]; - points[5] = vertices[ind2 + 1]; - - if (tempPolygon.contains(tempPoint.x, tempPoint.y)) - { - return true; - } - } - - return false; - } - - /** - * The texture that the mesh uses. - * - * @member {PIXI.Texture} - * @memberof PIXI.mesh.Mesh# - */ - get texture() - { - return this._texture; - } - - /** - * Sets the texture the mesh uses. - * - * @param {Texture} value - The value to set. - */ - set texture(value) - { - if (this._texture === value) - { - return; - } - - this._texture = value; - - if (value) - { - // wait for the texture to load - if (value.baseTexture.hasLoaded) - { - this._onTextureUpdate(); - } - else - { - value.once('update', this._onTextureUpdate, this); - } - } - } - - /** - * The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - * @default 0xFFFFFF - */ - get tint() - { - return core.utils.rgb2hex(this.tintRgb); - } - - /** - * Sets the tint the mesh uses. - * - * @param {number} value - The value to set. - */ - set tint(value) - { - this.tintRgb = core.utils.hex2rgb(value, this.tintRgb); - } } - /** * Different drawing buffer modes supported * * @static * @constant - * @type {object} - * @property {number} TRIANGLE_MESH - * @property {number} TRIANGLES + * @property {object} DRAW_MODES + * @property {number} DRAW_MODES.TRIANGLE_MESH + * @property {number} DRAW_MODES.TRIANGLES */ Mesh.DRAW_MODES = { - TRIANGLE_MESH: 0, - TRIANGLES: 1, + TRIANGLE_MESH: 1, + TRIANGLES: 2, + POINTS: 3, }; + diff --git a/src/mesh/Mesh_.js b/src/mesh/Mesh_.js new file mode 100644 index 0000000..21e5a61 --- /dev/null +++ b/src/mesh/Mesh_.js @@ -0,0 +1,287 @@ +import * as core from '../core'; + +const tempPoint = new core.Point(); +const tempPolygon = new core.Polygon(); + +/** + * Base mesh class + * @class + * @extends PIXI.Container + * @memberof PIXI.mesh + */ +export default class Mesh extends core.Container +{ + /** + * @param {PIXI.Texture} texture - The texture to use + * @param {Float32Array} [vertices] - if you want to specify the vertices + * @param {Float32Array} [uvs] - if you want to specify the uvs + * @param {Uint16Array} [indices] - if you want to specify the indices + * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts + */ + constructor(texture, vertices, uvs, indices, drawMode) + { + super(); + + /** + * The texture of the Mesh + * + * @member {PIXI.Texture} + * @private + */ + this._texture = null; + + /** + * The Uvs of the Mesh + * + * @member {Float32Array} + */ + this.uvs = uvs || new Float32Array([0, 0, + 1, 0, + 1, 1, + 0, 1]); + + /** + * An array of vertices + * + * @member {Float32Array} + */ + this.vertices = vertices || new Float32Array([0, 0, + 100, 0, + 100, 100, + 0, 100]); + + /* + * @member {Uint16Array} An array containing the indices of the vertices + */ + // TODO auto generate this based on draw mode! + this.indices = indices || new Uint16Array([0, 1, 3, 2]); + + /** + * Version of mesh uvs are dirty or not + * + * @member {number} + */ + this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ + this.indexDirty = 0; + + /** + * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove + * any blend mode. + * + * @member {number} + * @default PIXI.BLEND_MODES.NORMAL + * @see PIXI.BLEND_MODES + */ + this.blendMode = core.BLEND_MODES.NORMAL; + + /** + * Triangles in canvas mode are automatically antialiased, use this value to force triangles + * to overlap a bit with each other. + * + * @member {number} + */ + this.canvasPadding = 0; + + /** + * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.Mesh.DRAW_MODES} consts + * + * @member {number} + * @see PIXI.mesh.Mesh.DRAW_MODES + */ + this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; + + // run texture setter; + this.texture = texture; + + /** + * The default shader that is used if a mesh doesn't have a more specific one. + * + * @member {PIXI.Shader} + */ + this.shader = null; + + /** + * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any + * tint effect. + * + * @member {number} + * @memberof PIXI.mesh.Mesh# + */ + this.tintRgb = new Float32Array([1, 1, 1]); + + /** + * A map of renderer IDs to webgl render data + * + * @private + * @member {object} + */ + this._glDatas = {}; + } + + /** + * Renders the object using the WebGL renderer + * + * @private + * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer + */ + _renderWebGL(renderer) + { + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); + } + + /** + * Renders the object using the Canvas renderer + * + * @private + * @param {PIXI.CanvasRenderer} renderer - The canvas renderer. + */ + _renderCanvas(renderer) + { + renderer.plugins.mesh.render(this); + } + + /** + * When the texture is updated, this event will fire to update the scale and frame + * + * @private + */ + _onTextureUpdate() + { + /* empty */ + } + + /** + * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. + * + */ + _calculateBounds() + { + // TODO - we can cache local bounds and use them if they are dirty (like graphics) + this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length); + } + + /** + * Tests if a point is inside this mesh. Works only for TRIANGLE_MESH + * + * @param {PIXI.Point} point - the point to test + * @return {boolean} the result of the test + */ + containsPoint(point) + { + if (!this.getBounds().contains(point.x, point.y)) + { + return false; + } + + this.worldTransform.applyInverse(point, tempPoint); + + const vertices = this.vertices; + const points = tempPolygon.points; + const indices = this.indices; + const len = this.indices.length; + const step = this.drawMode === Mesh.DRAW_MODES.TRIANGLES ? 3 : 1; + + for (let i = 0; i + 2 < len; i += step) + { + const ind0 = indices[i] * 2; + const ind1 = indices[i + 1] * 2; + const ind2 = indices[i + 2] * 2; + + points[0] = vertices[ind0]; + points[1] = vertices[ind0 + 1]; + points[2] = vertices[ind1]; + points[3] = vertices[ind1 + 1]; + points[4] = vertices[ind2]; + points[5] = vertices[ind2 + 1]; + + if (tempPolygon.contains(tempPoint.x, tempPoint.y)) + { + return true; + } + } + + return false; + } + + /** + * The texture that the mesh uses. + * + * @member {PIXI.Texture} + * @memberof PIXI.mesh.Mesh# + */ + get texture() + { + return this._texture; + } + + /** + * Sets the texture the mesh uses. + * + * @param {Texture} value - The value to set. + */ + set texture(value) + { + if (this._texture === value) + { + return; + } + + this._texture = value; + + if (value) + { + // wait for the texture to load + if (value.baseTexture.hasLoaded) + { + this._onTextureUpdate(); + } + else + { + value.once('update', this._onTextureUpdate, this); + } + } + } + + /** + * The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect. + * + * @member {number} + * @memberof PIXI.mesh.Mesh# + * @default 0xFFFFFF + */ + get tint() + { + return core.utils.rgb2hex(this.tintRgb); + } + + /** + * Sets the tint the mesh uses. + * + * @param {number} value - The value to set. + */ + set tint(value) + { + this.tintRgb = core.utils.hex2rgb(value, this.tintRgb); + } +} + +/** + * Different drawing buffer modes supported + * + * @static + * @constant + * @type {object} + * @property {number} TRIANGLE_MESH + * @property {number} TRIANGLES + */ +Mesh.DRAW_MODES = { + TRIANGLE_MESH: 0, + TRIANGLES: 1, +}; diff --git a/src/core/index.js b/src/core/index.js index 7ea739a..6f6b9dc 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -39,6 +39,7 @@ export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; +export { default as Shader } from './shader/Shader'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 369d7d3..93cb171 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -1,5 +1,5 @@ -import { GLTexture } from 'pixi-gl-core'; -import { WRAP_MODES, SCALE_MODES } from '../../const'; +import { GLShader } from 'pixi-gl-core'; +import { WRAP_MODES, SCALE_MODES, PRECISION} from '../../const'; import RenderTarget from './utils/RenderTarget'; import { removeItems } from '../../utils'; @@ -35,14 +35,22 @@ { let glShader = shader.glShaders[renderer.CONTEXT_UID] || this.generateShader(shader); + this.renderer._bindGLShader(glShader); this.syncUniforms(glShader, shader); - this.renderer._bindGLShader(glShader); } generateShader(shader) { - const glShader = new GLShader(this.gl, filter.vertexSrc, filter.fragmentSrc, PRECISION.DEFAULT); + + const attribMap = {}; + for (const i in shader.attributeData) + { + attribMap[i] = shader.attributeData[i].location; + } + + console.log(attribMap) + const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); shader.glShaders[renderer.CONTEXT_UID] = glShader; return glShader diff --git a/src/core/shader/Shader.js b/src/core/shader/Shader.js index 9666db0..fb0a85a 100644 --- a/src/core/shader/Shader.js +++ b/src/core/shader/Shader.js @@ -1,4 +1,5 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; // let math = require('../../../math'); /** @@ -33,6 +34,9 @@ // currently this does not extract structs only default types this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc, 'projectionMatrix|uSampler'); + this.attributeData = extractAttributesFromSrc(this.vertexSrc); + + this.uniforms = {}; for (const i in this.uniformData) diff --git a/src/core/shader/extractAttributesFromSrc.js b/src/core/shader/extractAttributesFromSrc.js new file mode 100644 index 0000000..b190ef4 --- /dev/null +++ b/src/core/shader/extractAttributesFromSrc.js @@ -0,0 +1,75 @@ +import glCore from 'pixi-gl-core'; + +const defaultValue = glCore.shader.defaultValue; + +function extractAttributesFromSrc(vertexSrc, mask) +{ + const vertAttributes = extractAttributesFromString(vertexSrc, mask); + + return vertAttributes; +} + +function extractAttributesFromString(string) +{ + const maskRegex = new RegExp('^(projectionMatrix|uSampler|filterArea)$'); + + const attributesArray = []; + let nameSplit; + const a = 20; + + // clean the lines a little - remove extra spaces / teabs etc + // then split along ';' + const lines = string.replace(/\s+/g, ' ') + .split(/\s*;\s*/); + + // loop through.. + for (let i = 0; i < lines.length; i++) + { + const line = lines[i].trim(); + + if (line.indexOf('attribute') > -1) + { + const splitLine = line.split(' '); + const type = splitLine[1]; + + let name = splitLine[2]; + let size = 1; + + if (name.indexOf('[') > -1) + { + // array! + nameSplit = name.split(/\[|\]/); + name = nameSplit[0]; + size *= Number(nameSplit[1]); + } + + if (!name.match(maskRegex)) + { + attributesArray.push({ + value: defaultValue(type, size), + name, + location: 0, + type, + }); + } + } + } + + attributesArray.sort(function (a, b) { + return (a.name > b.name) ? 1 : -1; + }); + + const attributes = {}; + + // now lets sort them alphabetically.. + for (let i = 0; i < attributesArray.length; i++) + { + const attrib = attributesArray[i]; + attrib.location = i; + attributes[attrib.name] = attrib; + } + + return attributes; +} + +export default extractAttributesFromSrc; diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 21e5a61..83418e0 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,78 +1,29 @@ import * as core from '../core'; -const tempPoint = new core.Point(); -const tempPolygon = new core.Polygon(); - /** * Base mesh class * @class * @extends PIXI.Container * @memberof PIXI.mesh + * @param texture {PIXI.Texture} The texture to use + * @param [vertices] {Float32Array} if you want to specify the vertices + * @param [uvs] {Float32Array} if you want to specify the uvs + * @param [indices] {Uint16Array} if you want to specify the indices + * @param [drawMode] {number} the drawMode, can be any of the Mesh.DRAW_MODES consts */ export default class Mesh extends core.Container { - /** - * @param {PIXI.Texture} texture - The texture to use - * @param {Float32Array} [vertices] - if you want to specify the vertices - * @param {Float32Array} [uvs] - if you want to specify the uvs - * @param {Uint16Array} [indices] - if you want to specify the indices - * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts - */ - constructor(texture, vertices, uvs, indices, drawMode) + + constructor(geometry, shader, drawMode) { super(); - /** - * The texture of the Mesh - * - * @member {PIXI.Texture} - * @private - */ - this._texture = null; + this.geometry = geometry; + + this.shader = shader; /** - * The Uvs of the Mesh - * - * @member {Float32Array} - */ - this.uvs = uvs || new Float32Array([0, 0, - 1, 0, - 1, 1, - 0, 1]); - - /** - * An array of vertices - * - * @member {Float32Array} - */ - this.vertices = vertices || new Float32Array([0, 0, - 100, 0, - 100, 100, - 0, 100]); - - /* - * @member {Uint16Array} An array containing the indices of the vertices - */ - // TODO auto generate this based on draw mode! - this.indices = indices || new Uint16Array([0, 1, 3, 2]); - - /** - * Version of mesh uvs are dirty or not - * - * @member {number} - */ - this.dirty = 0; - - /** - * Version of mesh indices - * - * @member {number} - */ - this.indexDirty = 0; - - /** - * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove - * any blend mode. + * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove any blend mode. * * @member {number} * @default PIXI.BLEND_MODES.NORMAL @@ -81,12 +32,10 @@ this.blendMode = core.BLEND_MODES.NORMAL; /** - * Triangles in canvas mode are automatically antialiased, use this value to force triangles - * to overlap a bit with each other. + * Triangles in canvas mode are automatically antialiased, use this value to force triangles to overlap a bit with each other. * * @member {number} */ - this.canvasPadding = 0; /** * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.Mesh.DRAW_MODES} consts @@ -95,40 +44,13 @@ * @see PIXI.mesh.Mesh.DRAW_MODES */ this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; - - // run texture setter; - this.texture = texture; - - /** - * The default shader that is used if a mesh doesn't have a more specific one. - * - * @member {PIXI.Shader} - */ - this.shader = null; - - /** - * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any - * tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - */ - this.tintRgb = new Float32Array([1, 1, 1]); - - /** - * A map of renderer IDs to webgl render data - * - * @private - * @member {object} - */ - this._glDatas = {}; } /** * Renders the object using the WebGL renderer * + * @param renderer {PIXI.WebGLRenderer} a reference to the WebGL renderer * @private - * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer */ _renderWebGL(renderer) { @@ -137,151 +59,43 @@ } /** - * Renders the object using the Canvas renderer + * Calculates the bounds of the mesh. The bounds calculation takes the worldTransform into account. * - * @private - * @param {PIXI.CanvasRenderer} renderer - The canvas renderer. - */ - _renderCanvas(renderer) - { - renderer.plugins.mesh.render(this); - } - - /** - * When the texture is updated, this event will fire to update the scale and frame - * - * @private - */ - _onTextureUpdate() - { - /* empty */ - } - - /** - * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. - * + * @param [matrix=this.worldTransform] {PIXI.Matrix} the transformation matrix of the sprite + * @return {PIXI.Rectangle} the framing rectangle */ _calculateBounds() { - // TODO - we can cache local bounds and use them if they are dirty (like graphics) - this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length); + // The position property could be set manually? + if (this.geometry.attributes.aVertexPosition) + { + const vertices = this.geometry.attributes.aVertexPosition.buffer.data; + + // TODO - we can cache local bounds and use them if they are dirty (like graphics) + this._bounds.addVertices(this.transform, vertices, 0, vertices.length); + } } - /** + /** * Tests if a point is inside this mesh. Works only for TRIANGLE_MESH * - * @param {PIXI.Point} point - the point to test + * @param point {PIXI.Point} the point to test * @return {boolean} the result of the test */ - containsPoint(point) - { - if (!this.getBounds().contains(point.x, point.y)) - { - return false; - } - this.worldTransform.applyInverse(point, tempPoint); - - const vertices = this.vertices; - const points = tempPolygon.points; - const indices = this.indices; - const len = this.indices.length; - const step = this.drawMode === Mesh.DRAW_MODES.TRIANGLES ? 3 : 1; - - for (let i = 0; i + 2 < len; i += step) - { - const ind0 = indices[i] * 2; - const ind1 = indices[i + 1] * 2; - const ind2 = indices[i + 2] * 2; - - points[0] = vertices[ind0]; - points[1] = vertices[ind0 + 1]; - points[2] = vertices[ind1]; - points[3] = vertices[ind1 + 1]; - points[4] = vertices[ind2]; - points[5] = vertices[ind2 + 1]; - - if (tempPolygon.contains(tempPoint.x, tempPoint.y)) - { - return true; - } - } - - return false; - } - - /** - * The texture that the mesh uses. - * - * @member {PIXI.Texture} - * @memberof PIXI.mesh.Mesh# - */ - get texture() - { - return this._texture; - } - - /** - * Sets the texture the mesh uses. - * - * @param {Texture} value - The value to set. - */ - set texture(value) - { - if (this._texture === value) - { - return; - } - - this._texture = value; - - if (value) - { - // wait for the texture to load - if (value.baseTexture.hasLoaded) - { - this._onTextureUpdate(); - } - else - { - value.once('update', this._onTextureUpdate, this); - } - } - } - - /** - * The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - * @default 0xFFFFFF - */ - get tint() - { - return core.utils.rgb2hex(this.tintRgb); - } - - /** - * Sets the tint the mesh uses. - * - * @param {number} value - The value to set. - */ - set tint(value) - { - this.tintRgb = core.utils.hex2rgb(value, this.tintRgb); - } } - /** * Different drawing buffer modes supported * * @static * @constant - * @type {object} - * @property {number} TRIANGLE_MESH - * @property {number} TRIANGLES + * @property {object} DRAW_MODES + * @property {number} DRAW_MODES.TRIANGLE_MESH + * @property {number} DRAW_MODES.TRIANGLES */ Mesh.DRAW_MODES = { - TRIANGLE_MESH: 0, - TRIANGLES: 1, + TRIANGLE_MESH: 1, + TRIANGLES: 2, + POINTS: 3, }; + diff --git a/src/mesh/Mesh_.js b/src/mesh/Mesh_.js new file mode 100644 index 0000000..21e5a61 --- /dev/null +++ b/src/mesh/Mesh_.js @@ -0,0 +1,287 @@ +import * as core from '../core'; + +const tempPoint = new core.Point(); +const tempPolygon = new core.Polygon(); + +/** + * Base mesh class + * @class + * @extends PIXI.Container + * @memberof PIXI.mesh + */ +export default class Mesh extends core.Container +{ + /** + * @param {PIXI.Texture} texture - The texture to use + * @param {Float32Array} [vertices] - if you want to specify the vertices + * @param {Float32Array} [uvs] - if you want to specify the uvs + * @param {Uint16Array} [indices] - if you want to specify the indices + * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts + */ + constructor(texture, vertices, uvs, indices, drawMode) + { + super(); + + /** + * The texture of the Mesh + * + * @member {PIXI.Texture} + * @private + */ + this._texture = null; + + /** + * The Uvs of the Mesh + * + * @member {Float32Array} + */ + this.uvs = uvs || new Float32Array([0, 0, + 1, 0, + 1, 1, + 0, 1]); + + /** + * An array of vertices + * + * @member {Float32Array} + */ + this.vertices = vertices || new Float32Array([0, 0, + 100, 0, + 100, 100, + 0, 100]); + + /* + * @member {Uint16Array} An array containing the indices of the vertices + */ + // TODO auto generate this based on draw mode! + this.indices = indices || new Uint16Array([0, 1, 3, 2]); + + /** + * Version of mesh uvs are dirty or not + * + * @member {number} + */ + this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ + this.indexDirty = 0; + + /** + * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove + * any blend mode. + * + * @member {number} + * @default PIXI.BLEND_MODES.NORMAL + * @see PIXI.BLEND_MODES + */ + this.blendMode = core.BLEND_MODES.NORMAL; + + /** + * Triangles in canvas mode are automatically antialiased, use this value to force triangles + * to overlap a bit with each other. + * + * @member {number} + */ + this.canvasPadding = 0; + + /** + * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.Mesh.DRAW_MODES} consts + * + * @member {number} + * @see PIXI.mesh.Mesh.DRAW_MODES + */ + this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; + + // run texture setter; + this.texture = texture; + + /** + * The default shader that is used if a mesh doesn't have a more specific one. + * + * @member {PIXI.Shader} + */ + this.shader = null; + + /** + * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any + * tint effect. + * + * @member {number} + * @memberof PIXI.mesh.Mesh# + */ + this.tintRgb = new Float32Array([1, 1, 1]); + + /** + * A map of renderer IDs to webgl render data + * + * @private + * @member {object} + */ + this._glDatas = {}; + } + + /** + * Renders the object using the WebGL renderer + * + * @private + * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer + */ + _renderWebGL(renderer) + { + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); + } + + /** + * Renders the object using the Canvas renderer + * + * @private + * @param {PIXI.CanvasRenderer} renderer - The canvas renderer. + */ + _renderCanvas(renderer) + { + renderer.plugins.mesh.render(this); + } + + /** + * When the texture is updated, this event will fire to update the scale and frame + * + * @private + */ + _onTextureUpdate() + { + /* empty */ + } + + /** + * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. + * + */ + _calculateBounds() + { + // TODO - we can cache local bounds and use them if they are dirty (like graphics) + this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length); + } + + /** + * Tests if a point is inside this mesh. Works only for TRIANGLE_MESH + * + * @param {PIXI.Point} point - the point to test + * @return {boolean} the result of the test + */ + containsPoint(point) + { + if (!this.getBounds().contains(point.x, point.y)) + { + return false; + } + + this.worldTransform.applyInverse(point, tempPoint); + + const vertices = this.vertices; + const points = tempPolygon.points; + const indices = this.indices; + const len = this.indices.length; + const step = this.drawMode === Mesh.DRAW_MODES.TRIANGLES ? 3 : 1; + + for (let i = 0; i + 2 < len; i += step) + { + const ind0 = indices[i] * 2; + const ind1 = indices[i + 1] * 2; + const ind2 = indices[i + 2] * 2; + + points[0] = vertices[ind0]; + points[1] = vertices[ind0 + 1]; + points[2] = vertices[ind1]; + points[3] = vertices[ind1 + 1]; + points[4] = vertices[ind2]; + points[5] = vertices[ind2 + 1]; + + if (tempPolygon.contains(tempPoint.x, tempPoint.y)) + { + return true; + } + } + + return false; + } + + /** + * The texture that the mesh uses. + * + * @member {PIXI.Texture} + * @memberof PIXI.mesh.Mesh# + */ + get texture() + { + return this._texture; + } + + /** + * Sets the texture the mesh uses. + * + * @param {Texture} value - The value to set. + */ + set texture(value) + { + if (this._texture === value) + { + return; + } + + this._texture = value; + + if (value) + { + // wait for the texture to load + if (value.baseTexture.hasLoaded) + { + this._onTextureUpdate(); + } + else + { + value.once('update', this._onTextureUpdate, this); + } + } + } + + /** + * The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect. + * + * @member {number} + * @memberof PIXI.mesh.Mesh# + * @default 0xFFFFFF + */ + get tint() + { + return core.utils.rgb2hex(this.tintRgb); + } + + /** + * Sets the tint the mesh uses. + * + * @param {number} value - The value to set. + */ + set tint(value) + { + this.tintRgb = core.utils.hex2rgb(value, this.tintRgb); + } +} + +/** + * Different drawing buffer modes supported + * + * @static + * @constant + * @type {object} + * @property {number} TRIANGLE_MESH + * @property {number} TRIANGLES + */ +Mesh.DRAW_MODES = { + TRIANGLE_MESH: 0, + TRIANGLES: 1, +}; diff --git a/src/mesh/geometry/Attribute.js b/src/mesh/geometry/Attribute.js new file mode 100644 index 0000000..7a0a7a5 --- /dev/null +++ b/src/mesh/geometry/Attribute.js @@ -0,0 +1,26 @@ +class Attribute +{ + + constructor(buffer, size = 2, stride = 0, start = 0, normalised = false) + { + this.buffer = buffer; + this.normalized = normalised; + this.size = size; + this.stride = stride; + this.start = start; + this.type = null; + } + + destroy() + { + this.buffer = null; + } + +} + +Attribute.from = function (buffer, stride, start, normalised) +{ + return new Attribute(buffer, stride, start, normalised); +}; + +module.exports = Attribute; diff --git a/src/core/index.js b/src/core/index.js index 7ea739a..6f6b9dc 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -39,6 +39,7 @@ export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; +export { default as Shader } from './shader/Shader'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 369d7d3..93cb171 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -1,5 +1,5 @@ -import { GLTexture } from 'pixi-gl-core'; -import { WRAP_MODES, SCALE_MODES } from '../../const'; +import { GLShader } from 'pixi-gl-core'; +import { WRAP_MODES, SCALE_MODES, PRECISION} from '../../const'; import RenderTarget from './utils/RenderTarget'; import { removeItems } from '../../utils'; @@ -35,14 +35,22 @@ { let glShader = shader.glShaders[renderer.CONTEXT_UID] || this.generateShader(shader); + this.renderer._bindGLShader(glShader); this.syncUniforms(glShader, shader); - this.renderer._bindGLShader(glShader); } generateShader(shader) { - const glShader = new GLShader(this.gl, filter.vertexSrc, filter.fragmentSrc, PRECISION.DEFAULT); + + const attribMap = {}; + for (const i in shader.attributeData) + { + attribMap[i] = shader.attributeData[i].location; + } + + console.log(attribMap) + const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); shader.glShaders[renderer.CONTEXT_UID] = glShader; return glShader diff --git a/src/core/shader/Shader.js b/src/core/shader/Shader.js index 9666db0..fb0a85a 100644 --- a/src/core/shader/Shader.js +++ b/src/core/shader/Shader.js @@ -1,4 +1,5 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; // let math = require('../../../math'); /** @@ -33,6 +34,9 @@ // currently this does not extract structs only default types this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc, 'projectionMatrix|uSampler'); + this.attributeData = extractAttributesFromSrc(this.vertexSrc); + + this.uniforms = {}; for (const i in this.uniformData) diff --git a/src/core/shader/extractAttributesFromSrc.js b/src/core/shader/extractAttributesFromSrc.js new file mode 100644 index 0000000..b190ef4 --- /dev/null +++ b/src/core/shader/extractAttributesFromSrc.js @@ -0,0 +1,75 @@ +import glCore from 'pixi-gl-core'; + +const defaultValue = glCore.shader.defaultValue; + +function extractAttributesFromSrc(vertexSrc, mask) +{ + const vertAttributes = extractAttributesFromString(vertexSrc, mask); + + return vertAttributes; +} + +function extractAttributesFromString(string) +{ + const maskRegex = new RegExp('^(projectionMatrix|uSampler|filterArea)$'); + + const attributesArray = []; + let nameSplit; + const a = 20; + + // clean the lines a little - remove extra spaces / teabs etc + // then split along ';' + const lines = string.replace(/\s+/g, ' ') + .split(/\s*;\s*/); + + // loop through.. + for (let i = 0; i < lines.length; i++) + { + const line = lines[i].trim(); + + if (line.indexOf('attribute') > -1) + { + const splitLine = line.split(' '); + const type = splitLine[1]; + + let name = splitLine[2]; + let size = 1; + + if (name.indexOf('[') > -1) + { + // array! + nameSplit = name.split(/\[|\]/); + name = nameSplit[0]; + size *= Number(nameSplit[1]); + } + + if (!name.match(maskRegex)) + { + attributesArray.push({ + value: defaultValue(type, size), + name, + location: 0, + type, + }); + } + } + } + + attributesArray.sort(function (a, b) { + return (a.name > b.name) ? 1 : -1; + }); + + const attributes = {}; + + // now lets sort them alphabetically.. + for (let i = 0; i < attributesArray.length; i++) + { + const attrib = attributesArray[i]; + attrib.location = i; + attributes[attrib.name] = attrib; + } + + return attributes; +} + +export default extractAttributesFromSrc; diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 21e5a61..83418e0 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,78 +1,29 @@ import * as core from '../core'; -const tempPoint = new core.Point(); -const tempPolygon = new core.Polygon(); - /** * Base mesh class * @class * @extends PIXI.Container * @memberof PIXI.mesh + * @param texture {PIXI.Texture} The texture to use + * @param [vertices] {Float32Array} if you want to specify the vertices + * @param [uvs] {Float32Array} if you want to specify the uvs + * @param [indices] {Uint16Array} if you want to specify the indices + * @param [drawMode] {number} the drawMode, can be any of the Mesh.DRAW_MODES consts */ export default class Mesh extends core.Container { - /** - * @param {PIXI.Texture} texture - The texture to use - * @param {Float32Array} [vertices] - if you want to specify the vertices - * @param {Float32Array} [uvs] - if you want to specify the uvs - * @param {Uint16Array} [indices] - if you want to specify the indices - * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts - */ - constructor(texture, vertices, uvs, indices, drawMode) + + constructor(geometry, shader, drawMode) { super(); - /** - * The texture of the Mesh - * - * @member {PIXI.Texture} - * @private - */ - this._texture = null; + this.geometry = geometry; + + this.shader = shader; /** - * The Uvs of the Mesh - * - * @member {Float32Array} - */ - this.uvs = uvs || new Float32Array([0, 0, - 1, 0, - 1, 1, - 0, 1]); - - /** - * An array of vertices - * - * @member {Float32Array} - */ - this.vertices = vertices || new Float32Array([0, 0, - 100, 0, - 100, 100, - 0, 100]); - - /* - * @member {Uint16Array} An array containing the indices of the vertices - */ - // TODO auto generate this based on draw mode! - this.indices = indices || new Uint16Array([0, 1, 3, 2]); - - /** - * Version of mesh uvs are dirty or not - * - * @member {number} - */ - this.dirty = 0; - - /** - * Version of mesh indices - * - * @member {number} - */ - this.indexDirty = 0; - - /** - * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove - * any blend mode. + * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove any blend mode. * * @member {number} * @default PIXI.BLEND_MODES.NORMAL @@ -81,12 +32,10 @@ this.blendMode = core.BLEND_MODES.NORMAL; /** - * Triangles in canvas mode are automatically antialiased, use this value to force triangles - * to overlap a bit with each other. + * Triangles in canvas mode are automatically antialiased, use this value to force triangles to overlap a bit with each other. * * @member {number} */ - this.canvasPadding = 0; /** * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.Mesh.DRAW_MODES} consts @@ -95,40 +44,13 @@ * @see PIXI.mesh.Mesh.DRAW_MODES */ this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; - - // run texture setter; - this.texture = texture; - - /** - * The default shader that is used if a mesh doesn't have a more specific one. - * - * @member {PIXI.Shader} - */ - this.shader = null; - - /** - * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any - * tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - */ - this.tintRgb = new Float32Array([1, 1, 1]); - - /** - * A map of renderer IDs to webgl render data - * - * @private - * @member {object} - */ - this._glDatas = {}; } /** * Renders the object using the WebGL renderer * + * @param renderer {PIXI.WebGLRenderer} a reference to the WebGL renderer * @private - * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer */ _renderWebGL(renderer) { @@ -137,151 +59,43 @@ } /** - * Renders the object using the Canvas renderer + * Calculates the bounds of the mesh. The bounds calculation takes the worldTransform into account. * - * @private - * @param {PIXI.CanvasRenderer} renderer - The canvas renderer. - */ - _renderCanvas(renderer) - { - renderer.plugins.mesh.render(this); - } - - /** - * When the texture is updated, this event will fire to update the scale and frame - * - * @private - */ - _onTextureUpdate() - { - /* empty */ - } - - /** - * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. - * + * @param [matrix=this.worldTransform] {PIXI.Matrix} the transformation matrix of the sprite + * @return {PIXI.Rectangle} the framing rectangle */ _calculateBounds() { - // TODO - we can cache local bounds and use them if they are dirty (like graphics) - this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length); + // The position property could be set manually? + if (this.geometry.attributes.aVertexPosition) + { + const vertices = this.geometry.attributes.aVertexPosition.buffer.data; + + // TODO - we can cache local bounds and use them if they are dirty (like graphics) + this._bounds.addVertices(this.transform, vertices, 0, vertices.length); + } } - /** + /** * Tests if a point is inside this mesh. Works only for TRIANGLE_MESH * - * @param {PIXI.Point} point - the point to test + * @param point {PIXI.Point} the point to test * @return {boolean} the result of the test */ - containsPoint(point) - { - if (!this.getBounds().contains(point.x, point.y)) - { - return false; - } - this.worldTransform.applyInverse(point, tempPoint); - - const vertices = this.vertices; - const points = tempPolygon.points; - const indices = this.indices; - const len = this.indices.length; - const step = this.drawMode === Mesh.DRAW_MODES.TRIANGLES ? 3 : 1; - - for (let i = 0; i + 2 < len; i += step) - { - const ind0 = indices[i] * 2; - const ind1 = indices[i + 1] * 2; - const ind2 = indices[i + 2] * 2; - - points[0] = vertices[ind0]; - points[1] = vertices[ind0 + 1]; - points[2] = vertices[ind1]; - points[3] = vertices[ind1 + 1]; - points[4] = vertices[ind2]; - points[5] = vertices[ind2 + 1]; - - if (tempPolygon.contains(tempPoint.x, tempPoint.y)) - { - return true; - } - } - - return false; - } - - /** - * The texture that the mesh uses. - * - * @member {PIXI.Texture} - * @memberof PIXI.mesh.Mesh# - */ - get texture() - { - return this._texture; - } - - /** - * Sets the texture the mesh uses. - * - * @param {Texture} value - The value to set. - */ - set texture(value) - { - if (this._texture === value) - { - return; - } - - this._texture = value; - - if (value) - { - // wait for the texture to load - if (value.baseTexture.hasLoaded) - { - this._onTextureUpdate(); - } - else - { - value.once('update', this._onTextureUpdate, this); - } - } - } - - /** - * The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - * @default 0xFFFFFF - */ - get tint() - { - return core.utils.rgb2hex(this.tintRgb); - } - - /** - * Sets the tint the mesh uses. - * - * @param {number} value - The value to set. - */ - set tint(value) - { - this.tintRgb = core.utils.hex2rgb(value, this.tintRgb); - } } - /** * Different drawing buffer modes supported * * @static * @constant - * @type {object} - * @property {number} TRIANGLE_MESH - * @property {number} TRIANGLES + * @property {object} DRAW_MODES + * @property {number} DRAW_MODES.TRIANGLE_MESH + * @property {number} DRAW_MODES.TRIANGLES */ Mesh.DRAW_MODES = { - TRIANGLE_MESH: 0, - TRIANGLES: 1, + TRIANGLE_MESH: 1, + TRIANGLES: 2, + POINTS: 3, }; + diff --git a/src/mesh/Mesh_.js b/src/mesh/Mesh_.js new file mode 100644 index 0000000..21e5a61 --- /dev/null +++ b/src/mesh/Mesh_.js @@ -0,0 +1,287 @@ +import * as core from '../core'; + +const tempPoint = new core.Point(); +const tempPolygon = new core.Polygon(); + +/** + * Base mesh class + * @class + * @extends PIXI.Container + * @memberof PIXI.mesh + */ +export default class Mesh extends core.Container +{ + /** + * @param {PIXI.Texture} texture - The texture to use + * @param {Float32Array} [vertices] - if you want to specify the vertices + * @param {Float32Array} [uvs] - if you want to specify the uvs + * @param {Uint16Array} [indices] - if you want to specify the indices + * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts + */ + constructor(texture, vertices, uvs, indices, drawMode) + { + super(); + + /** + * The texture of the Mesh + * + * @member {PIXI.Texture} + * @private + */ + this._texture = null; + + /** + * The Uvs of the Mesh + * + * @member {Float32Array} + */ + this.uvs = uvs || new Float32Array([0, 0, + 1, 0, + 1, 1, + 0, 1]); + + /** + * An array of vertices + * + * @member {Float32Array} + */ + this.vertices = vertices || new Float32Array([0, 0, + 100, 0, + 100, 100, + 0, 100]); + + /* + * @member {Uint16Array} An array containing the indices of the vertices + */ + // TODO auto generate this based on draw mode! + this.indices = indices || new Uint16Array([0, 1, 3, 2]); + + /** + * Version of mesh uvs are dirty or not + * + * @member {number} + */ + this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ + this.indexDirty = 0; + + /** + * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove + * any blend mode. + * + * @member {number} + * @default PIXI.BLEND_MODES.NORMAL + * @see PIXI.BLEND_MODES + */ + this.blendMode = core.BLEND_MODES.NORMAL; + + /** + * Triangles in canvas mode are automatically antialiased, use this value to force triangles + * to overlap a bit with each other. + * + * @member {number} + */ + this.canvasPadding = 0; + + /** + * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.Mesh.DRAW_MODES} consts + * + * @member {number} + * @see PIXI.mesh.Mesh.DRAW_MODES + */ + this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; + + // run texture setter; + this.texture = texture; + + /** + * The default shader that is used if a mesh doesn't have a more specific one. + * + * @member {PIXI.Shader} + */ + this.shader = null; + + /** + * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any + * tint effect. + * + * @member {number} + * @memberof PIXI.mesh.Mesh# + */ + this.tintRgb = new Float32Array([1, 1, 1]); + + /** + * A map of renderer IDs to webgl render data + * + * @private + * @member {object} + */ + this._glDatas = {}; + } + + /** + * Renders the object using the WebGL renderer + * + * @private + * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer + */ + _renderWebGL(renderer) + { + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); + } + + /** + * Renders the object using the Canvas renderer + * + * @private + * @param {PIXI.CanvasRenderer} renderer - The canvas renderer. + */ + _renderCanvas(renderer) + { + renderer.plugins.mesh.render(this); + } + + /** + * When the texture is updated, this event will fire to update the scale and frame + * + * @private + */ + _onTextureUpdate() + { + /* empty */ + } + + /** + * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. + * + */ + _calculateBounds() + { + // TODO - we can cache local bounds and use them if they are dirty (like graphics) + this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length); + } + + /** + * Tests if a point is inside this mesh. Works only for TRIANGLE_MESH + * + * @param {PIXI.Point} point - the point to test + * @return {boolean} the result of the test + */ + containsPoint(point) + { + if (!this.getBounds().contains(point.x, point.y)) + { + return false; + } + + this.worldTransform.applyInverse(point, tempPoint); + + const vertices = this.vertices; + const points = tempPolygon.points; + const indices = this.indices; + const len = this.indices.length; + const step = this.drawMode === Mesh.DRAW_MODES.TRIANGLES ? 3 : 1; + + for (let i = 0; i + 2 < len; i += step) + { + const ind0 = indices[i] * 2; + const ind1 = indices[i + 1] * 2; + const ind2 = indices[i + 2] * 2; + + points[0] = vertices[ind0]; + points[1] = vertices[ind0 + 1]; + points[2] = vertices[ind1]; + points[3] = vertices[ind1 + 1]; + points[4] = vertices[ind2]; + points[5] = vertices[ind2 + 1]; + + if (tempPolygon.contains(tempPoint.x, tempPoint.y)) + { + return true; + } + } + + return false; + } + + /** + * The texture that the mesh uses. + * + * @member {PIXI.Texture} + * @memberof PIXI.mesh.Mesh# + */ + get texture() + { + return this._texture; + } + + /** + * Sets the texture the mesh uses. + * + * @param {Texture} value - The value to set. + */ + set texture(value) + { + if (this._texture === value) + { + return; + } + + this._texture = value; + + if (value) + { + // wait for the texture to load + if (value.baseTexture.hasLoaded) + { + this._onTextureUpdate(); + } + else + { + value.once('update', this._onTextureUpdate, this); + } + } + } + + /** + * The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect. + * + * @member {number} + * @memberof PIXI.mesh.Mesh# + * @default 0xFFFFFF + */ + get tint() + { + return core.utils.rgb2hex(this.tintRgb); + } + + /** + * Sets the tint the mesh uses. + * + * @param {number} value - The value to set. + */ + set tint(value) + { + this.tintRgb = core.utils.hex2rgb(value, this.tintRgb); + } +} + +/** + * Different drawing buffer modes supported + * + * @static + * @constant + * @type {object} + * @property {number} TRIANGLE_MESH + * @property {number} TRIANGLES + */ +Mesh.DRAW_MODES = { + TRIANGLE_MESH: 0, + TRIANGLES: 1, +}; diff --git a/src/mesh/geometry/Attribute.js b/src/mesh/geometry/Attribute.js new file mode 100644 index 0000000..7a0a7a5 --- /dev/null +++ b/src/mesh/geometry/Attribute.js @@ -0,0 +1,26 @@ +class Attribute +{ + + constructor(buffer, size = 2, stride = 0, start = 0, normalised = false) + { + this.buffer = buffer; + this.normalized = normalised; + this.size = size; + this.stride = stride; + this.start = start; + this.type = null; + } + + destroy() + { + this.buffer = null; + } + +} + +Attribute.from = function (buffer, stride, start, normalised) +{ + return new Attribute(buffer, stride, start, normalised); +}; + +module.exports = Attribute; diff --git a/src/mesh/geometry/Buffer.js b/src/mesh/geometry/Buffer.js new file mode 100644 index 0000000..7fc958d --- /dev/null +++ b/src/mesh/geometry/Buffer.js @@ -0,0 +1,73 @@ +let UID = 0; +/** + * Helper class to create a webGL buffer + * + * @class + * @memberof PIXI.glCore + * @param gl {WebGLRenderingContext} The current WebGL rendering context + * @param type {gl.ARRAY_BUFFER | gl.ELEMENT_ARRAY_BUFFER} @mat + * @param data {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} an array of data + * @param drawType {gl.STATIC_DRAW|gl.DYNAMIC_DRAW|gl.STREAM_DRAW} + */ +export default class Buffer +{ + constructor(data) + { + /** + * The type of the buffer + * + * @member {gl.ARRAY_BUFFER|gl.ELEMENT_ARRAY_BUFFER} + */ + // this.type = type || gl.ARRAY_BUFFER; + + /** + * The draw type of the buffer + * + * @member {gl.STATIC_DRAW|gl.DYNAMIC_DRAW|gl.STREAM_DRAW} + */ + // this.drawType = drawType || gl.STATIC_DRAW; + + /** + * The data in the buffer, as a typed array + * + * @member {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} + */ + this.data = data; + + this._glBuffers = []; + + this._updateID = 0; + + this.id = UID++; + } + + /** + * Uploads the buffer to the GPU + * @param data {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} an array of data to upload + * @param offset {Number} if only a subset of the data should be uploaded, this is the amount of data to subtract + */ + update() + { + this._updateID++; + } + + /** + * Destroys the buffer + * + */ + destroy() + { + for (let i = 0; i < this._glBuffers.length; i++) + { + this._glBuffers[i].destroy(); + } + + this.data = null; + } + + static from(data) + { + return new Buffer(data); + } +} + diff --git a/src/core/index.js b/src/core/index.js index 7ea739a..6f6b9dc 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -39,6 +39,7 @@ export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; +export { default as Shader } from './shader/Shader'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 369d7d3..93cb171 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -1,5 +1,5 @@ -import { GLTexture } from 'pixi-gl-core'; -import { WRAP_MODES, SCALE_MODES } from '../../const'; +import { GLShader } from 'pixi-gl-core'; +import { WRAP_MODES, SCALE_MODES, PRECISION} from '../../const'; import RenderTarget from './utils/RenderTarget'; import { removeItems } from '../../utils'; @@ -35,14 +35,22 @@ { let glShader = shader.glShaders[renderer.CONTEXT_UID] || this.generateShader(shader); + this.renderer._bindGLShader(glShader); this.syncUniforms(glShader, shader); - this.renderer._bindGLShader(glShader); } generateShader(shader) { - const glShader = new GLShader(this.gl, filter.vertexSrc, filter.fragmentSrc, PRECISION.DEFAULT); + + const attribMap = {}; + for (const i in shader.attributeData) + { + attribMap[i] = shader.attributeData[i].location; + } + + console.log(attribMap) + const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); shader.glShaders[renderer.CONTEXT_UID] = glShader; return glShader diff --git a/src/core/shader/Shader.js b/src/core/shader/Shader.js index 9666db0..fb0a85a 100644 --- a/src/core/shader/Shader.js +++ b/src/core/shader/Shader.js @@ -1,4 +1,5 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; // let math = require('../../../math'); /** @@ -33,6 +34,9 @@ // currently this does not extract structs only default types this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc, 'projectionMatrix|uSampler'); + this.attributeData = extractAttributesFromSrc(this.vertexSrc); + + this.uniforms = {}; for (const i in this.uniformData) diff --git a/src/core/shader/extractAttributesFromSrc.js b/src/core/shader/extractAttributesFromSrc.js new file mode 100644 index 0000000..b190ef4 --- /dev/null +++ b/src/core/shader/extractAttributesFromSrc.js @@ -0,0 +1,75 @@ +import glCore from 'pixi-gl-core'; + +const defaultValue = glCore.shader.defaultValue; + +function extractAttributesFromSrc(vertexSrc, mask) +{ + const vertAttributes = extractAttributesFromString(vertexSrc, mask); + + return vertAttributes; +} + +function extractAttributesFromString(string) +{ + const maskRegex = new RegExp('^(projectionMatrix|uSampler|filterArea)$'); + + const attributesArray = []; + let nameSplit; + const a = 20; + + // clean the lines a little - remove extra spaces / teabs etc + // then split along ';' + const lines = string.replace(/\s+/g, ' ') + .split(/\s*;\s*/); + + // loop through.. + for (let i = 0; i < lines.length; i++) + { + const line = lines[i].trim(); + + if (line.indexOf('attribute') > -1) + { + const splitLine = line.split(' '); + const type = splitLine[1]; + + let name = splitLine[2]; + let size = 1; + + if (name.indexOf('[') > -1) + { + // array! + nameSplit = name.split(/\[|\]/); + name = nameSplit[0]; + size *= Number(nameSplit[1]); + } + + if (!name.match(maskRegex)) + { + attributesArray.push({ + value: defaultValue(type, size), + name, + location: 0, + type, + }); + } + } + } + + attributesArray.sort(function (a, b) { + return (a.name > b.name) ? 1 : -1; + }); + + const attributes = {}; + + // now lets sort them alphabetically.. + for (let i = 0; i < attributesArray.length; i++) + { + const attrib = attributesArray[i]; + attrib.location = i; + attributes[attrib.name] = attrib; + } + + return attributes; +} + +export default extractAttributesFromSrc; diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 21e5a61..83418e0 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,78 +1,29 @@ import * as core from '../core'; -const tempPoint = new core.Point(); -const tempPolygon = new core.Polygon(); - /** * Base mesh class * @class * @extends PIXI.Container * @memberof PIXI.mesh + * @param texture {PIXI.Texture} The texture to use + * @param [vertices] {Float32Array} if you want to specify the vertices + * @param [uvs] {Float32Array} if you want to specify the uvs + * @param [indices] {Uint16Array} if you want to specify the indices + * @param [drawMode] {number} the drawMode, can be any of the Mesh.DRAW_MODES consts */ export default class Mesh extends core.Container { - /** - * @param {PIXI.Texture} texture - The texture to use - * @param {Float32Array} [vertices] - if you want to specify the vertices - * @param {Float32Array} [uvs] - if you want to specify the uvs - * @param {Uint16Array} [indices] - if you want to specify the indices - * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts - */ - constructor(texture, vertices, uvs, indices, drawMode) + + constructor(geometry, shader, drawMode) { super(); - /** - * The texture of the Mesh - * - * @member {PIXI.Texture} - * @private - */ - this._texture = null; + this.geometry = geometry; + + this.shader = shader; /** - * The Uvs of the Mesh - * - * @member {Float32Array} - */ - this.uvs = uvs || new Float32Array([0, 0, - 1, 0, - 1, 1, - 0, 1]); - - /** - * An array of vertices - * - * @member {Float32Array} - */ - this.vertices = vertices || new Float32Array([0, 0, - 100, 0, - 100, 100, - 0, 100]); - - /* - * @member {Uint16Array} An array containing the indices of the vertices - */ - // TODO auto generate this based on draw mode! - this.indices = indices || new Uint16Array([0, 1, 3, 2]); - - /** - * Version of mesh uvs are dirty or not - * - * @member {number} - */ - this.dirty = 0; - - /** - * Version of mesh indices - * - * @member {number} - */ - this.indexDirty = 0; - - /** - * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove - * any blend mode. + * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove any blend mode. * * @member {number} * @default PIXI.BLEND_MODES.NORMAL @@ -81,12 +32,10 @@ this.blendMode = core.BLEND_MODES.NORMAL; /** - * Triangles in canvas mode are automatically antialiased, use this value to force triangles - * to overlap a bit with each other. + * Triangles in canvas mode are automatically antialiased, use this value to force triangles to overlap a bit with each other. * * @member {number} */ - this.canvasPadding = 0; /** * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.Mesh.DRAW_MODES} consts @@ -95,40 +44,13 @@ * @see PIXI.mesh.Mesh.DRAW_MODES */ this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; - - // run texture setter; - this.texture = texture; - - /** - * The default shader that is used if a mesh doesn't have a more specific one. - * - * @member {PIXI.Shader} - */ - this.shader = null; - - /** - * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any - * tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - */ - this.tintRgb = new Float32Array([1, 1, 1]); - - /** - * A map of renderer IDs to webgl render data - * - * @private - * @member {object} - */ - this._glDatas = {}; } /** * Renders the object using the WebGL renderer * + * @param renderer {PIXI.WebGLRenderer} a reference to the WebGL renderer * @private - * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer */ _renderWebGL(renderer) { @@ -137,151 +59,43 @@ } /** - * Renders the object using the Canvas renderer + * Calculates the bounds of the mesh. The bounds calculation takes the worldTransform into account. * - * @private - * @param {PIXI.CanvasRenderer} renderer - The canvas renderer. - */ - _renderCanvas(renderer) - { - renderer.plugins.mesh.render(this); - } - - /** - * When the texture is updated, this event will fire to update the scale and frame - * - * @private - */ - _onTextureUpdate() - { - /* empty */ - } - - /** - * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. - * + * @param [matrix=this.worldTransform] {PIXI.Matrix} the transformation matrix of the sprite + * @return {PIXI.Rectangle} the framing rectangle */ _calculateBounds() { - // TODO - we can cache local bounds and use them if they are dirty (like graphics) - this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length); + // The position property could be set manually? + if (this.geometry.attributes.aVertexPosition) + { + const vertices = this.geometry.attributes.aVertexPosition.buffer.data; + + // TODO - we can cache local bounds and use them if they are dirty (like graphics) + this._bounds.addVertices(this.transform, vertices, 0, vertices.length); + } } - /** + /** * Tests if a point is inside this mesh. Works only for TRIANGLE_MESH * - * @param {PIXI.Point} point - the point to test + * @param point {PIXI.Point} the point to test * @return {boolean} the result of the test */ - containsPoint(point) - { - if (!this.getBounds().contains(point.x, point.y)) - { - return false; - } - this.worldTransform.applyInverse(point, tempPoint); - - const vertices = this.vertices; - const points = tempPolygon.points; - const indices = this.indices; - const len = this.indices.length; - const step = this.drawMode === Mesh.DRAW_MODES.TRIANGLES ? 3 : 1; - - for (let i = 0; i + 2 < len; i += step) - { - const ind0 = indices[i] * 2; - const ind1 = indices[i + 1] * 2; - const ind2 = indices[i + 2] * 2; - - points[0] = vertices[ind0]; - points[1] = vertices[ind0 + 1]; - points[2] = vertices[ind1]; - points[3] = vertices[ind1 + 1]; - points[4] = vertices[ind2]; - points[5] = vertices[ind2 + 1]; - - if (tempPolygon.contains(tempPoint.x, tempPoint.y)) - { - return true; - } - } - - return false; - } - - /** - * The texture that the mesh uses. - * - * @member {PIXI.Texture} - * @memberof PIXI.mesh.Mesh# - */ - get texture() - { - return this._texture; - } - - /** - * Sets the texture the mesh uses. - * - * @param {Texture} value - The value to set. - */ - set texture(value) - { - if (this._texture === value) - { - return; - } - - this._texture = value; - - if (value) - { - // wait for the texture to load - if (value.baseTexture.hasLoaded) - { - this._onTextureUpdate(); - } - else - { - value.once('update', this._onTextureUpdate, this); - } - } - } - - /** - * The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - * @default 0xFFFFFF - */ - get tint() - { - return core.utils.rgb2hex(this.tintRgb); - } - - /** - * Sets the tint the mesh uses. - * - * @param {number} value - The value to set. - */ - set tint(value) - { - this.tintRgb = core.utils.hex2rgb(value, this.tintRgb); - } } - /** * Different drawing buffer modes supported * * @static * @constant - * @type {object} - * @property {number} TRIANGLE_MESH - * @property {number} TRIANGLES + * @property {object} DRAW_MODES + * @property {number} DRAW_MODES.TRIANGLE_MESH + * @property {number} DRAW_MODES.TRIANGLES */ Mesh.DRAW_MODES = { - TRIANGLE_MESH: 0, - TRIANGLES: 1, + TRIANGLE_MESH: 1, + TRIANGLES: 2, + POINTS: 3, }; + diff --git a/src/mesh/Mesh_.js b/src/mesh/Mesh_.js new file mode 100644 index 0000000..21e5a61 --- /dev/null +++ b/src/mesh/Mesh_.js @@ -0,0 +1,287 @@ +import * as core from '../core'; + +const tempPoint = new core.Point(); +const tempPolygon = new core.Polygon(); + +/** + * Base mesh class + * @class + * @extends PIXI.Container + * @memberof PIXI.mesh + */ +export default class Mesh extends core.Container +{ + /** + * @param {PIXI.Texture} texture - The texture to use + * @param {Float32Array} [vertices] - if you want to specify the vertices + * @param {Float32Array} [uvs] - if you want to specify the uvs + * @param {Uint16Array} [indices] - if you want to specify the indices + * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts + */ + constructor(texture, vertices, uvs, indices, drawMode) + { + super(); + + /** + * The texture of the Mesh + * + * @member {PIXI.Texture} + * @private + */ + this._texture = null; + + /** + * The Uvs of the Mesh + * + * @member {Float32Array} + */ + this.uvs = uvs || new Float32Array([0, 0, + 1, 0, + 1, 1, + 0, 1]); + + /** + * An array of vertices + * + * @member {Float32Array} + */ + this.vertices = vertices || new Float32Array([0, 0, + 100, 0, + 100, 100, + 0, 100]); + + /* + * @member {Uint16Array} An array containing the indices of the vertices + */ + // TODO auto generate this based on draw mode! + this.indices = indices || new Uint16Array([0, 1, 3, 2]); + + /** + * Version of mesh uvs are dirty or not + * + * @member {number} + */ + this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ + this.indexDirty = 0; + + /** + * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove + * any blend mode. + * + * @member {number} + * @default PIXI.BLEND_MODES.NORMAL + * @see PIXI.BLEND_MODES + */ + this.blendMode = core.BLEND_MODES.NORMAL; + + /** + * Triangles in canvas mode are automatically antialiased, use this value to force triangles + * to overlap a bit with each other. + * + * @member {number} + */ + this.canvasPadding = 0; + + /** + * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.Mesh.DRAW_MODES} consts + * + * @member {number} + * @see PIXI.mesh.Mesh.DRAW_MODES + */ + this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; + + // run texture setter; + this.texture = texture; + + /** + * The default shader that is used if a mesh doesn't have a more specific one. + * + * @member {PIXI.Shader} + */ + this.shader = null; + + /** + * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any + * tint effect. + * + * @member {number} + * @memberof PIXI.mesh.Mesh# + */ + this.tintRgb = new Float32Array([1, 1, 1]); + + /** + * A map of renderer IDs to webgl render data + * + * @private + * @member {object} + */ + this._glDatas = {}; + } + + /** + * Renders the object using the WebGL renderer + * + * @private + * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer + */ + _renderWebGL(renderer) + { + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); + } + + /** + * Renders the object using the Canvas renderer + * + * @private + * @param {PIXI.CanvasRenderer} renderer - The canvas renderer. + */ + _renderCanvas(renderer) + { + renderer.plugins.mesh.render(this); + } + + /** + * When the texture is updated, this event will fire to update the scale and frame + * + * @private + */ + _onTextureUpdate() + { + /* empty */ + } + + /** + * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. + * + */ + _calculateBounds() + { + // TODO - we can cache local bounds and use them if they are dirty (like graphics) + this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length); + } + + /** + * Tests if a point is inside this mesh. Works only for TRIANGLE_MESH + * + * @param {PIXI.Point} point - the point to test + * @return {boolean} the result of the test + */ + containsPoint(point) + { + if (!this.getBounds().contains(point.x, point.y)) + { + return false; + } + + this.worldTransform.applyInverse(point, tempPoint); + + const vertices = this.vertices; + const points = tempPolygon.points; + const indices = this.indices; + const len = this.indices.length; + const step = this.drawMode === Mesh.DRAW_MODES.TRIANGLES ? 3 : 1; + + for (let i = 0; i + 2 < len; i += step) + { + const ind0 = indices[i] * 2; + const ind1 = indices[i + 1] * 2; + const ind2 = indices[i + 2] * 2; + + points[0] = vertices[ind0]; + points[1] = vertices[ind0 + 1]; + points[2] = vertices[ind1]; + points[3] = vertices[ind1 + 1]; + points[4] = vertices[ind2]; + points[5] = vertices[ind2 + 1]; + + if (tempPolygon.contains(tempPoint.x, tempPoint.y)) + { + return true; + } + } + + return false; + } + + /** + * The texture that the mesh uses. + * + * @member {PIXI.Texture} + * @memberof PIXI.mesh.Mesh# + */ + get texture() + { + return this._texture; + } + + /** + * Sets the texture the mesh uses. + * + * @param {Texture} value - The value to set. + */ + set texture(value) + { + if (this._texture === value) + { + return; + } + + this._texture = value; + + if (value) + { + // wait for the texture to load + if (value.baseTexture.hasLoaded) + { + this._onTextureUpdate(); + } + else + { + value.once('update', this._onTextureUpdate, this); + } + } + } + + /** + * The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect. + * + * @member {number} + * @memberof PIXI.mesh.Mesh# + * @default 0xFFFFFF + */ + get tint() + { + return core.utils.rgb2hex(this.tintRgb); + } + + /** + * Sets the tint the mesh uses. + * + * @param {number} value - The value to set. + */ + set tint(value) + { + this.tintRgb = core.utils.hex2rgb(value, this.tintRgb); + } +} + +/** + * Different drawing buffer modes supported + * + * @static + * @constant + * @type {object} + * @property {number} TRIANGLE_MESH + * @property {number} TRIANGLES + */ +Mesh.DRAW_MODES = { + TRIANGLE_MESH: 0, + TRIANGLES: 1, +}; diff --git a/src/mesh/geometry/Attribute.js b/src/mesh/geometry/Attribute.js new file mode 100644 index 0000000..7a0a7a5 --- /dev/null +++ b/src/mesh/geometry/Attribute.js @@ -0,0 +1,26 @@ +class Attribute +{ + + constructor(buffer, size = 2, stride = 0, start = 0, normalised = false) + { + this.buffer = buffer; + this.normalized = normalised; + this.size = size; + this.stride = stride; + this.start = start; + this.type = null; + } + + destroy() + { + this.buffer = null; + } + +} + +Attribute.from = function (buffer, stride, start, normalised) +{ + return new Attribute(buffer, stride, start, normalised); +}; + +module.exports = Attribute; diff --git a/src/mesh/geometry/Buffer.js b/src/mesh/geometry/Buffer.js new file mode 100644 index 0000000..7fc958d --- /dev/null +++ b/src/mesh/geometry/Buffer.js @@ -0,0 +1,73 @@ +let UID = 0; +/** + * Helper class to create a webGL buffer + * + * @class + * @memberof PIXI.glCore + * @param gl {WebGLRenderingContext} The current WebGL rendering context + * @param type {gl.ARRAY_BUFFER | gl.ELEMENT_ARRAY_BUFFER} @mat + * @param data {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} an array of data + * @param drawType {gl.STATIC_DRAW|gl.DYNAMIC_DRAW|gl.STREAM_DRAW} + */ +export default class Buffer +{ + constructor(data) + { + /** + * The type of the buffer + * + * @member {gl.ARRAY_BUFFER|gl.ELEMENT_ARRAY_BUFFER} + */ + // this.type = type || gl.ARRAY_BUFFER; + + /** + * The draw type of the buffer + * + * @member {gl.STATIC_DRAW|gl.DYNAMIC_DRAW|gl.STREAM_DRAW} + */ + // this.drawType = drawType || gl.STATIC_DRAW; + + /** + * The data in the buffer, as a typed array + * + * @member {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} + */ + this.data = data; + + this._glBuffers = []; + + this._updateID = 0; + + this.id = UID++; + } + + /** + * Uploads the buffer to the GPU + * @param data {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} an array of data to upload + * @param offset {Number} if only a subset of the data should be uploaded, this is the amount of data to subtract + */ + update() + { + this._updateID++; + } + + /** + * Destroys the buffer + * + */ + destroy() + { + for (let i = 0; i < this._glBuffers.length; i++) + { + this._glBuffers[i].destroy(); + } + + this.data = null; + } + + static from(data) + { + return new Buffer(data); + } +} + diff --git a/src/mesh/geometry/Geometry.js b/src/mesh/geometry/Geometry.js new file mode 100644 index 0000000..d734691 --- /dev/null +++ b/src/mesh/geometry/Geometry.js @@ -0,0 +1,55 @@ +import Attribute from './Attribute'; +import GeometryStyle from './GeometryStyle'; +import GeometryData from './GeometryData'; + +class Geometry +{ + + constructor(data, style) + { + this.style = style || new GeometryStyle(); + this.data = data || new GeometryData(); + + // / this.indexBuffer = null; + + this.glVertexArrayObjects = []; + } + + addAttribute(id, buffer, size = 2, stride = 0, start = 0, normalised = false) + { + this.style.addAttribute(id, new Attribute(buffer.id, size, stride, start, normalised)); + this.data.add(buffer.id, buffer); + + return this; + } + + addIndex(buffer) + { + this.data.addIndex(buffer); + + return this; + } + + destroy() + { + for (let i = 0; i < this.buffers.length; i++) + { + this.buffers[i].destroy(); + } + + this.buffers = null; + this.attributes = null; + + for (let i = 0; i < this.glVertexArrayObjects.length; i++) + { + this.glVertexArrayObjects[i].destroy(); + } + + this.glVertexArrayObjects = null; + + this.indexBuffer.destroy(); + this.indexBuffer = null; + } +} + +export default Geometry; diff --git a/src/core/index.js b/src/core/index.js index 7ea739a..6f6b9dc 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -39,6 +39,7 @@ export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; +export { default as Shader } from './shader/Shader'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 369d7d3..93cb171 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -1,5 +1,5 @@ -import { GLTexture } from 'pixi-gl-core'; -import { WRAP_MODES, SCALE_MODES } from '../../const'; +import { GLShader } from 'pixi-gl-core'; +import { WRAP_MODES, SCALE_MODES, PRECISION} from '../../const'; import RenderTarget from './utils/RenderTarget'; import { removeItems } from '../../utils'; @@ -35,14 +35,22 @@ { let glShader = shader.glShaders[renderer.CONTEXT_UID] || this.generateShader(shader); + this.renderer._bindGLShader(glShader); this.syncUniforms(glShader, shader); - this.renderer._bindGLShader(glShader); } generateShader(shader) { - const glShader = new GLShader(this.gl, filter.vertexSrc, filter.fragmentSrc, PRECISION.DEFAULT); + + const attribMap = {}; + for (const i in shader.attributeData) + { + attribMap[i] = shader.attributeData[i].location; + } + + console.log(attribMap) + const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); shader.glShaders[renderer.CONTEXT_UID] = glShader; return glShader diff --git a/src/core/shader/Shader.js b/src/core/shader/Shader.js index 9666db0..fb0a85a 100644 --- a/src/core/shader/Shader.js +++ b/src/core/shader/Shader.js @@ -1,4 +1,5 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; // let math = require('../../../math'); /** @@ -33,6 +34,9 @@ // currently this does not extract structs only default types this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc, 'projectionMatrix|uSampler'); + this.attributeData = extractAttributesFromSrc(this.vertexSrc); + + this.uniforms = {}; for (const i in this.uniformData) diff --git a/src/core/shader/extractAttributesFromSrc.js b/src/core/shader/extractAttributesFromSrc.js new file mode 100644 index 0000000..b190ef4 --- /dev/null +++ b/src/core/shader/extractAttributesFromSrc.js @@ -0,0 +1,75 @@ +import glCore from 'pixi-gl-core'; + +const defaultValue = glCore.shader.defaultValue; + +function extractAttributesFromSrc(vertexSrc, mask) +{ + const vertAttributes = extractAttributesFromString(vertexSrc, mask); + + return vertAttributes; +} + +function extractAttributesFromString(string) +{ + const maskRegex = new RegExp('^(projectionMatrix|uSampler|filterArea)$'); + + const attributesArray = []; + let nameSplit; + const a = 20; + + // clean the lines a little - remove extra spaces / teabs etc + // then split along ';' + const lines = string.replace(/\s+/g, ' ') + .split(/\s*;\s*/); + + // loop through.. + for (let i = 0; i < lines.length; i++) + { + const line = lines[i].trim(); + + if (line.indexOf('attribute') > -1) + { + const splitLine = line.split(' '); + const type = splitLine[1]; + + let name = splitLine[2]; + let size = 1; + + if (name.indexOf('[') > -1) + { + // array! + nameSplit = name.split(/\[|\]/); + name = nameSplit[0]; + size *= Number(nameSplit[1]); + } + + if (!name.match(maskRegex)) + { + attributesArray.push({ + value: defaultValue(type, size), + name, + location: 0, + type, + }); + } + } + } + + attributesArray.sort(function (a, b) { + return (a.name > b.name) ? 1 : -1; + }); + + const attributes = {}; + + // now lets sort them alphabetically.. + for (let i = 0; i < attributesArray.length; i++) + { + const attrib = attributesArray[i]; + attrib.location = i; + attributes[attrib.name] = attrib; + } + + return attributes; +} + +export default extractAttributesFromSrc; diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 21e5a61..83418e0 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,78 +1,29 @@ import * as core from '../core'; -const tempPoint = new core.Point(); -const tempPolygon = new core.Polygon(); - /** * Base mesh class * @class * @extends PIXI.Container * @memberof PIXI.mesh + * @param texture {PIXI.Texture} The texture to use + * @param [vertices] {Float32Array} if you want to specify the vertices + * @param [uvs] {Float32Array} if you want to specify the uvs + * @param [indices] {Uint16Array} if you want to specify the indices + * @param [drawMode] {number} the drawMode, can be any of the Mesh.DRAW_MODES consts */ export default class Mesh extends core.Container { - /** - * @param {PIXI.Texture} texture - The texture to use - * @param {Float32Array} [vertices] - if you want to specify the vertices - * @param {Float32Array} [uvs] - if you want to specify the uvs - * @param {Uint16Array} [indices] - if you want to specify the indices - * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts - */ - constructor(texture, vertices, uvs, indices, drawMode) + + constructor(geometry, shader, drawMode) { super(); - /** - * The texture of the Mesh - * - * @member {PIXI.Texture} - * @private - */ - this._texture = null; + this.geometry = geometry; + + this.shader = shader; /** - * The Uvs of the Mesh - * - * @member {Float32Array} - */ - this.uvs = uvs || new Float32Array([0, 0, - 1, 0, - 1, 1, - 0, 1]); - - /** - * An array of vertices - * - * @member {Float32Array} - */ - this.vertices = vertices || new Float32Array([0, 0, - 100, 0, - 100, 100, - 0, 100]); - - /* - * @member {Uint16Array} An array containing the indices of the vertices - */ - // TODO auto generate this based on draw mode! - this.indices = indices || new Uint16Array([0, 1, 3, 2]); - - /** - * Version of mesh uvs are dirty or not - * - * @member {number} - */ - this.dirty = 0; - - /** - * Version of mesh indices - * - * @member {number} - */ - this.indexDirty = 0; - - /** - * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove - * any blend mode. + * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove any blend mode. * * @member {number} * @default PIXI.BLEND_MODES.NORMAL @@ -81,12 +32,10 @@ this.blendMode = core.BLEND_MODES.NORMAL; /** - * Triangles in canvas mode are automatically antialiased, use this value to force triangles - * to overlap a bit with each other. + * Triangles in canvas mode are automatically antialiased, use this value to force triangles to overlap a bit with each other. * * @member {number} */ - this.canvasPadding = 0; /** * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.Mesh.DRAW_MODES} consts @@ -95,40 +44,13 @@ * @see PIXI.mesh.Mesh.DRAW_MODES */ this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; - - // run texture setter; - this.texture = texture; - - /** - * The default shader that is used if a mesh doesn't have a more specific one. - * - * @member {PIXI.Shader} - */ - this.shader = null; - - /** - * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any - * tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - */ - this.tintRgb = new Float32Array([1, 1, 1]); - - /** - * A map of renderer IDs to webgl render data - * - * @private - * @member {object} - */ - this._glDatas = {}; } /** * Renders the object using the WebGL renderer * + * @param renderer {PIXI.WebGLRenderer} a reference to the WebGL renderer * @private - * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer */ _renderWebGL(renderer) { @@ -137,151 +59,43 @@ } /** - * Renders the object using the Canvas renderer + * Calculates the bounds of the mesh. The bounds calculation takes the worldTransform into account. * - * @private - * @param {PIXI.CanvasRenderer} renderer - The canvas renderer. - */ - _renderCanvas(renderer) - { - renderer.plugins.mesh.render(this); - } - - /** - * When the texture is updated, this event will fire to update the scale and frame - * - * @private - */ - _onTextureUpdate() - { - /* empty */ - } - - /** - * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. - * + * @param [matrix=this.worldTransform] {PIXI.Matrix} the transformation matrix of the sprite + * @return {PIXI.Rectangle} the framing rectangle */ _calculateBounds() { - // TODO - we can cache local bounds and use them if they are dirty (like graphics) - this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length); + // The position property could be set manually? + if (this.geometry.attributes.aVertexPosition) + { + const vertices = this.geometry.attributes.aVertexPosition.buffer.data; + + // TODO - we can cache local bounds and use them if they are dirty (like graphics) + this._bounds.addVertices(this.transform, vertices, 0, vertices.length); + } } - /** + /** * Tests if a point is inside this mesh. Works only for TRIANGLE_MESH * - * @param {PIXI.Point} point - the point to test + * @param point {PIXI.Point} the point to test * @return {boolean} the result of the test */ - containsPoint(point) - { - if (!this.getBounds().contains(point.x, point.y)) - { - return false; - } - this.worldTransform.applyInverse(point, tempPoint); - - const vertices = this.vertices; - const points = tempPolygon.points; - const indices = this.indices; - const len = this.indices.length; - const step = this.drawMode === Mesh.DRAW_MODES.TRIANGLES ? 3 : 1; - - for (let i = 0; i + 2 < len; i += step) - { - const ind0 = indices[i] * 2; - const ind1 = indices[i + 1] * 2; - const ind2 = indices[i + 2] * 2; - - points[0] = vertices[ind0]; - points[1] = vertices[ind0 + 1]; - points[2] = vertices[ind1]; - points[3] = vertices[ind1 + 1]; - points[4] = vertices[ind2]; - points[5] = vertices[ind2 + 1]; - - if (tempPolygon.contains(tempPoint.x, tempPoint.y)) - { - return true; - } - } - - return false; - } - - /** - * The texture that the mesh uses. - * - * @member {PIXI.Texture} - * @memberof PIXI.mesh.Mesh# - */ - get texture() - { - return this._texture; - } - - /** - * Sets the texture the mesh uses. - * - * @param {Texture} value - The value to set. - */ - set texture(value) - { - if (this._texture === value) - { - return; - } - - this._texture = value; - - if (value) - { - // wait for the texture to load - if (value.baseTexture.hasLoaded) - { - this._onTextureUpdate(); - } - else - { - value.once('update', this._onTextureUpdate, this); - } - } - } - - /** - * The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - * @default 0xFFFFFF - */ - get tint() - { - return core.utils.rgb2hex(this.tintRgb); - } - - /** - * Sets the tint the mesh uses. - * - * @param {number} value - The value to set. - */ - set tint(value) - { - this.tintRgb = core.utils.hex2rgb(value, this.tintRgb); - } } - /** * Different drawing buffer modes supported * * @static * @constant - * @type {object} - * @property {number} TRIANGLE_MESH - * @property {number} TRIANGLES + * @property {object} DRAW_MODES + * @property {number} DRAW_MODES.TRIANGLE_MESH + * @property {number} DRAW_MODES.TRIANGLES */ Mesh.DRAW_MODES = { - TRIANGLE_MESH: 0, - TRIANGLES: 1, + TRIANGLE_MESH: 1, + TRIANGLES: 2, + POINTS: 3, }; + diff --git a/src/mesh/Mesh_.js b/src/mesh/Mesh_.js new file mode 100644 index 0000000..21e5a61 --- /dev/null +++ b/src/mesh/Mesh_.js @@ -0,0 +1,287 @@ +import * as core from '../core'; + +const tempPoint = new core.Point(); +const tempPolygon = new core.Polygon(); + +/** + * Base mesh class + * @class + * @extends PIXI.Container + * @memberof PIXI.mesh + */ +export default class Mesh extends core.Container +{ + /** + * @param {PIXI.Texture} texture - The texture to use + * @param {Float32Array} [vertices] - if you want to specify the vertices + * @param {Float32Array} [uvs] - if you want to specify the uvs + * @param {Uint16Array} [indices] - if you want to specify the indices + * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts + */ + constructor(texture, vertices, uvs, indices, drawMode) + { + super(); + + /** + * The texture of the Mesh + * + * @member {PIXI.Texture} + * @private + */ + this._texture = null; + + /** + * The Uvs of the Mesh + * + * @member {Float32Array} + */ + this.uvs = uvs || new Float32Array([0, 0, + 1, 0, + 1, 1, + 0, 1]); + + /** + * An array of vertices + * + * @member {Float32Array} + */ + this.vertices = vertices || new Float32Array([0, 0, + 100, 0, + 100, 100, + 0, 100]); + + /* + * @member {Uint16Array} An array containing the indices of the vertices + */ + // TODO auto generate this based on draw mode! + this.indices = indices || new Uint16Array([0, 1, 3, 2]); + + /** + * Version of mesh uvs are dirty or not + * + * @member {number} + */ + this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ + this.indexDirty = 0; + + /** + * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove + * any blend mode. + * + * @member {number} + * @default PIXI.BLEND_MODES.NORMAL + * @see PIXI.BLEND_MODES + */ + this.blendMode = core.BLEND_MODES.NORMAL; + + /** + * Triangles in canvas mode are automatically antialiased, use this value to force triangles + * to overlap a bit with each other. + * + * @member {number} + */ + this.canvasPadding = 0; + + /** + * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.Mesh.DRAW_MODES} consts + * + * @member {number} + * @see PIXI.mesh.Mesh.DRAW_MODES + */ + this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; + + // run texture setter; + this.texture = texture; + + /** + * The default shader that is used if a mesh doesn't have a more specific one. + * + * @member {PIXI.Shader} + */ + this.shader = null; + + /** + * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any + * tint effect. + * + * @member {number} + * @memberof PIXI.mesh.Mesh# + */ + this.tintRgb = new Float32Array([1, 1, 1]); + + /** + * A map of renderer IDs to webgl render data + * + * @private + * @member {object} + */ + this._glDatas = {}; + } + + /** + * Renders the object using the WebGL renderer + * + * @private + * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer + */ + _renderWebGL(renderer) + { + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); + } + + /** + * Renders the object using the Canvas renderer + * + * @private + * @param {PIXI.CanvasRenderer} renderer - The canvas renderer. + */ + _renderCanvas(renderer) + { + renderer.plugins.mesh.render(this); + } + + /** + * When the texture is updated, this event will fire to update the scale and frame + * + * @private + */ + _onTextureUpdate() + { + /* empty */ + } + + /** + * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. + * + */ + _calculateBounds() + { + // TODO - we can cache local bounds and use them if they are dirty (like graphics) + this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length); + } + + /** + * Tests if a point is inside this mesh. Works only for TRIANGLE_MESH + * + * @param {PIXI.Point} point - the point to test + * @return {boolean} the result of the test + */ + containsPoint(point) + { + if (!this.getBounds().contains(point.x, point.y)) + { + return false; + } + + this.worldTransform.applyInverse(point, tempPoint); + + const vertices = this.vertices; + const points = tempPolygon.points; + const indices = this.indices; + const len = this.indices.length; + const step = this.drawMode === Mesh.DRAW_MODES.TRIANGLES ? 3 : 1; + + for (let i = 0; i + 2 < len; i += step) + { + const ind0 = indices[i] * 2; + const ind1 = indices[i + 1] * 2; + const ind2 = indices[i + 2] * 2; + + points[0] = vertices[ind0]; + points[1] = vertices[ind0 + 1]; + points[2] = vertices[ind1]; + points[3] = vertices[ind1 + 1]; + points[4] = vertices[ind2]; + points[5] = vertices[ind2 + 1]; + + if (tempPolygon.contains(tempPoint.x, tempPoint.y)) + { + return true; + } + } + + return false; + } + + /** + * The texture that the mesh uses. + * + * @member {PIXI.Texture} + * @memberof PIXI.mesh.Mesh# + */ + get texture() + { + return this._texture; + } + + /** + * Sets the texture the mesh uses. + * + * @param {Texture} value - The value to set. + */ + set texture(value) + { + if (this._texture === value) + { + return; + } + + this._texture = value; + + if (value) + { + // wait for the texture to load + if (value.baseTexture.hasLoaded) + { + this._onTextureUpdate(); + } + else + { + value.once('update', this._onTextureUpdate, this); + } + } + } + + /** + * The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect. + * + * @member {number} + * @memberof PIXI.mesh.Mesh# + * @default 0xFFFFFF + */ + get tint() + { + return core.utils.rgb2hex(this.tintRgb); + } + + /** + * Sets the tint the mesh uses. + * + * @param {number} value - The value to set. + */ + set tint(value) + { + this.tintRgb = core.utils.hex2rgb(value, this.tintRgb); + } +} + +/** + * Different drawing buffer modes supported + * + * @static + * @constant + * @type {object} + * @property {number} TRIANGLE_MESH + * @property {number} TRIANGLES + */ +Mesh.DRAW_MODES = { + TRIANGLE_MESH: 0, + TRIANGLES: 1, +}; diff --git a/src/mesh/geometry/Attribute.js b/src/mesh/geometry/Attribute.js new file mode 100644 index 0000000..7a0a7a5 --- /dev/null +++ b/src/mesh/geometry/Attribute.js @@ -0,0 +1,26 @@ +class Attribute +{ + + constructor(buffer, size = 2, stride = 0, start = 0, normalised = false) + { + this.buffer = buffer; + this.normalized = normalised; + this.size = size; + this.stride = stride; + this.start = start; + this.type = null; + } + + destroy() + { + this.buffer = null; + } + +} + +Attribute.from = function (buffer, stride, start, normalised) +{ + return new Attribute(buffer, stride, start, normalised); +}; + +module.exports = Attribute; diff --git a/src/mesh/geometry/Buffer.js b/src/mesh/geometry/Buffer.js new file mode 100644 index 0000000..7fc958d --- /dev/null +++ b/src/mesh/geometry/Buffer.js @@ -0,0 +1,73 @@ +let UID = 0; +/** + * Helper class to create a webGL buffer + * + * @class + * @memberof PIXI.glCore + * @param gl {WebGLRenderingContext} The current WebGL rendering context + * @param type {gl.ARRAY_BUFFER | gl.ELEMENT_ARRAY_BUFFER} @mat + * @param data {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} an array of data + * @param drawType {gl.STATIC_DRAW|gl.DYNAMIC_DRAW|gl.STREAM_DRAW} + */ +export default class Buffer +{ + constructor(data) + { + /** + * The type of the buffer + * + * @member {gl.ARRAY_BUFFER|gl.ELEMENT_ARRAY_BUFFER} + */ + // this.type = type || gl.ARRAY_BUFFER; + + /** + * The draw type of the buffer + * + * @member {gl.STATIC_DRAW|gl.DYNAMIC_DRAW|gl.STREAM_DRAW} + */ + // this.drawType = drawType || gl.STATIC_DRAW; + + /** + * The data in the buffer, as a typed array + * + * @member {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} + */ + this.data = data; + + this._glBuffers = []; + + this._updateID = 0; + + this.id = UID++; + } + + /** + * Uploads the buffer to the GPU + * @param data {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} an array of data to upload + * @param offset {Number} if only a subset of the data should be uploaded, this is the amount of data to subtract + */ + update() + { + this._updateID++; + } + + /** + * Destroys the buffer + * + */ + destroy() + { + for (let i = 0; i < this._glBuffers.length; i++) + { + this._glBuffers[i].destroy(); + } + + this.data = null; + } + + static from(data) + { + return new Buffer(data); + } +} + diff --git a/src/mesh/geometry/Geometry.js b/src/mesh/geometry/Geometry.js new file mode 100644 index 0000000..d734691 --- /dev/null +++ b/src/mesh/geometry/Geometry.js @@ -0,0 +1,55 @@ +import Attribute from './Attribute'; +import GeometryStyle from './GeometryStyle'; +import GeometryData from './GeometryData'; + +class Geometry +{ + + constructor(data, style) + { + this.style = style || new GeometryStyle(); + this.data = data || new GeometryData(); + + // / this.indexBuffer = null; + + this.glVertexArrayObjects = []; + } + + addAttribute(id, buffer, size = 2, stride = 0, start = 0, normalised = false) + { + this.style.addAttribute(id, new Attribute(buffer.id, size, stride, start, normalised)); + this.data.add(buffer.id, buffer); + + return this; + } + + addIndex(buffer) + { + this.data.addIndex(buffer); + + return this; + } + + destroy() + { + for (let i = 0; i < this.buffers.length; i++) + { + this.buffers[i].destroy(); + } + + this.buffers = null; + this.attributes = null; + + for (let i = 0; i < this.glVertexArrayObjects.length; i++) + { + this.glVertexArrayObjects[i].destroy(); + } + + this.glVertexArrayObjects = null; + + this.indexBuffer.destroy(); + this.indexBuffer = null; + } +} + +export default Geometry; diff --git a/src/mesh/geometry/GeometryData.js b/src/mesh/geometry/GeometryData.js new file mode 100644 index 0000000..28dd0ca --- /dev/null +++ b/src/mesh/geometry/GeometryData.js @@ -0,0 +1,35 @@ +class GeometryData +{ + constructor() + { + this.buffers = []; + this.indexBuffer = null; + } + + add(id, buffer) + { + // only one! + if (this.buffers.indexOf(buffer) === -1) + { + this.buffers.push(buffer); + this[id] = buffer; + } + + return this; + } + + addIndex(buffer) + { + buffer.index = true; + this.indexBuffer = buffer; + + if (this.buffers.indexOf(buffer) === -1) + { + this.buffers.push(buffer); + } + + return this; + } +} + +export default GeometryData; diff --git a/src/core/index.js b/src/core/index.js index 7ea739a..6f6b9dc 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -39,6 +39,7 @@ export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; +export { default as Shader } from './shader/Shader'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 369d7d3..93cb171 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -1,5 +1,5 @@ -import { GLTexture } from 'pixi-gl-core'; -import { WRAP_MODES, SCALE_MODES } from '../../const'; +import { GLShader } from 'pixi-gl-core'; +import { WRAP_MODES, SCALE_MODES, PRECISION} from '../../const'; import RenderTarget from './utils/RenderTarget'; import { removeItems } from '../../utils'; @@ -35,14 +35,22 @@ { let glShader = shader.glShaders[renderer.CONTEXT_UID] || this.generateShader(shader); + this.renderer._bindGLShader(glShader); this.syncUniforms(glShader, shader); - this.renderer._bindGLShader(glShader); } generateShader(shader) { - const glShader = new GLShader(this.gl, filter.vertexSrc, filter.fragmentSrc, PRECISION.DEFAULT); + + const attribMap = {}; + for (const i in shader.attributeData) + { + attribMap[i] = shader.attributeData[i].location; + } + + console.log(attribMap) + const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); shader.glShaders[renderer.CONTEXT_UID] = glShader; return glShader diff --git a/src/core/shader/Shader.js b/src/core/shader/Shader.js index 9666db0..fb0a85a 100644 --- a/src/core/shader/Shader.js +++ b/src/core/shader/Shader.js @@ -1,4 +1,5 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; // let math = require('../../../math'); /** @@ -33,6 +34,9 @@ // currently this does not extract structs only default types this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc, 'projectionMatrix|uSampler'); + this.attributeData = extractAttributesFromSrc(this.vertexSrc); + + this.uniforms = {}; for (const i in this.uniformData) diff --git a/src/core/shader/extractAttributesFromSrc.js b/src/core/shader/extractAttributesFromSrc.js new file mode 100644 index 0000000..b190ef4 --- /dev/null +++ b/src/core/shader/extractAttributesFromSrc.js @@ -0,0 +1,75 @@ +import glCore from 'pixi-gl-core'; + +const defaultValue = glCore.shader.defaultValue; + +function extractAttributesFromSrc(vertexSrc, mask) +{ + const vertAttributes = extractAttributesFromString(vertexSrc, mask); + + return vertAttributes; +} + +function extractAttributesFromString(string) +{ + const maskRegex = new RegExp('^(projectionMatrix|uSampler|filterArea)$'); + + const attributesArray = []; + let nameSplit; + const a = 20; + + // clean the lines a little - remove extra spaces / teabs etc + // then split along ';' + const lines = string.replace(/\s+/g, ' ') + .split(/\s*;\s*/); + + // loop through.. + for (let i = 0; i < lines.length; i++) + { + const line = lines[i].trim(); + + if (line.indexOf('attribute') > -1) + { + const splitLine = line.split(' '); + const type = splitLine[1]; + + let name = splitLine[2]; + let size = 1; + + if (name.indexOf('[') > -1) + { + // array! + nameSplit = name.split(/\[|\]/); + name = nameSplit[0]; + size *= Number(nameSplit[1]); + } + + if (!name.match(maskRegex)) + { + attributesArray.push({ + value: defaultValue(type, size), + name, + location: 0, + type, + }); + } + } + } + + attributesArray.sort(function (a, b) { + return (a.name > b.name) ? 1 : -1; + }); + + const attributes = {}; + + // now lets sort them alphabetically.. + for (let i = 0; i < attributesArray.length; i++) + { + const attrib = attributesArray[i]; + attrib.location = i; + attributes[attrib.name] = attrib; + } + + return attributes; +} + +export default extractAttributesFromSrc; diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 21e5a61..83418e0 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,78 +1,29 @@ import * as core from '../core'; -const tempPoint = new core.Point(); -const tempPolygon = new core.Polygon(); - /** * Base mesh class * @class * @extends PIXI.Container * @memberof PIXI.mesh + * @param texture {PIXI.Texture} The texture to use + * @param [vertices] {Float32Array} if you want to specify the vertices + * @param [uvs] {Float32Array} if you want to specify the uvs + * @param [indices] {Uint16Array} if you want to specify the indices + * @param [drawMode] {number} the drawMode, can be any of the Mesh.DRAW_MODES consts */ export default class Mesh extends core.Container { - /** - * @param {PIXI.Texture} texture - The texture to use - * @param {Float32Array} [vertices] - if you want to specify the vertices - * @param {Float32Array} [uvs] - if you want to specify the uvs - * @param {Uint16Array} [indices] - if you want to specify the indices - * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts - */ - constructor(texture, vertices, uvs, indices, drawMode) + + constructor(geometry, shader, drawMode) { super(); - /** - * The texture of the Mesh - * - * @member {PIXI.Texture} - * @private - */ - this._texture = null; + this.geometry = geometry; + + this.shader = shader; /** - * The Uvs of the Mesh - * - * @member {Float32Array} - */ - this.uvs = uvs || new Float32Array([0, 0, - 1, 0, - 1, 1, - 0, 1]); - - /** - * An array of vertices - * - * @member {Float32Array} - */ - this.vertices = vertices || new Float32Array([0, 0, - 100, 0, - 100, 100, - 0, 100]); - - /* - * @member {Uint16Array} An array containing the indices of the vertices - */ - // TODO auto generate this based on draw mode! - this.indices = indices || new Uint16Array([0, 1, 3, 2]); - - /** - * Version of mesh uvs are dirty or not - * - * @member {number} - */ - this.dirty = 0; - - /** - * Version of mesh indices - * - * @member {number} - */ - this.indexDirty = 0; - - /** - * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove - * any blend mode. + * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove any blend mode. * * @member {number} * @default PIXI.BLEND_MODES.NORMAL @@ -81,12 +32,10 @@ this.blendMode = core.BLEND_MODES.NORMAL; /** - * Triangles in canvas mode are automatically antialiased, use this value to force triangles - * to overlap a bit with each other. + * Triangles in canvas mode are automatically antialiased, use this value to force triangles to overlap a bit with each other. * * @member {number} */ - this.canvasPadding = 0; /** * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.Mesh.DRAW_MODES} consts @@ -95,40 +44,13 @@ * @see PIXI.mesh.Mesh.DRAW_MODES */ this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; - - // run texture setter; - this.texture = texture; - - /** - * The default shader that is used if a mesh doesn't have a more specific one. - * - * @member {PIXI.Shader} - */ - this.shader = null; - - /** - * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any - * tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - */ - this.tintRgb = new Float32Array([1, 1, 1]); - - /** - * A map of renderer IDs to webgl render data - * - * @private - * @member {object} - */ - this._glDatas = {}; } /** * Renders the object using the WebGL renderer * + * @param renderer {PIXI.WebGLRenderer} a reference to the WebGL renderer * @private - * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer */ _renderWebGL(renderer) { @@ -137,151 +59,43 @@ } /** - * Renders the object using the Canvas renderer + * Calculates the bounds of the mesh. The bounds calculation takes the worldTransform into account. * - * @private - * @param {PIXI.CanvasRenderer} renderer - The canvas renderer. - */ - _renderCanvas(renderer) - { - renderer.plugins.mesh.render(this); - } - - /** - * When the texture is updated, this event will fire to update the scale and frame - * - * @private - */ - _onTextureUpdate() - { - /* empty */ - } - - /** - * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. - * + * @param [matrix=this.worldTransform] {PIXI.Matrix} the transformation matrix of the sprite + * @return {PIXI.Rectangle} the framing rectangle */ _calculateBounds() { - // TODO - we can cache local bounds and use them if they are dirty (like graphics) - this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length); + // The position property could be set manually? + if (this.geometry.attributes.aVertexPosition) + { + const vertices = this.geometry.attributes.aVertexPosition.buffer.data; + + // TODO - we can cache local bounds and use them if they are dirty (like graphics) + this._bounds.addVertices(this.transform, vertices, 0, vertices.length); + } } - /** + /** * Tests if a point is inside this mesh. Works only for TRIANGLE_MESH * - * @param {PIXI.Point} point - the point to test + * @param point {PIXI.Point} the point to test * @return {boolean} the result of the test */ - containsPoint(point) - { - if (!this.getBounds().contains(point.x, point.y)) - { - return false; - } - this.worldTransform.applyInverse(point, tempPoint); - - const vertices = this.vertices; - const points = tempPolygon.points; - const indices = this.indices; - const len = this.indices.length; - const step = this.drawMode === Mesh.DRAW_MODES.TRIANGLES ? 3 : 1; - - for (let i = 0; i + 2 < len; i += step) - { - const ind0 = indices[i] * 2; - const ind1 = indices[i + 1] * 2; - const ind2 = indices[i + 2] * 2; - - points[0] = vertices[ind0]; - points[1] = vertices[ind0 + 1]; - points[2] = vertices[ind1]; - points[3] = vertices[ind1 + 1]; - points[4] = vertices[ind2]; - points[5] = vertices[ind2 + 1]; - - if (tempPolygon.contains(tempPoint.x, tempPoint.y)) - { - return true; - } - } - - return false; - } - - /** - * The texture that the mesh uses. - * - * @member {PIXI.Texture} - * @memberof PIXI.mesh.Mesh# - */ - get texture() - { - return this._texture; - } - - /** - * Sets the texture the mesh uses. - * - * @param {Texture} value - The value to set. - */ - set texture(value) - { - if (this._texture === value) - { - return; - } - - this._texture = value; - - if (value) - { - // wait for the texture to load - if (value.baseTexture.hasLoaded) - { - this._onTextureUpdate(); - } - else - { - value.once('update', this._onTextureUpdate, this); - } - } - } - - /** - * The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - * @default 0xFFFFFF - */ - get tint() - { - return core.utils.rgb2hex(this.tintRgb); - } - - /** - * Sets the tint the mesh uses. - * - * @param {number} value - The value to set. - */ - set tint(value) - { - this.tintRgb = core.utils.hex2rgb(value, this.tintRgb); - } } - /** * Different drawing buffer modes supported * * @static * @constant - * @type {object} - * @property {number} TRIANGLE_MESH - * @property {number} TRIANGLES + * @property {object} DRAW_MODES + * @property {number} DRAW_MODES.TRIANGLE_MESH + * @property {number} DRAW_MODES.TRIANGLES */ Mesh.DRAW_MODES = { - TRIANGLE_MESH: 0, - TRIANGLES: 1, + TRIANGLE_MESH: 1, + TRIANGLES: 2, + POINTS: 3, }; + diff --git a/src/mesh/Mesh_.js b/src/mesh/Mesh_.js new file mode 100644 index 0000000..21e5a61 --- /dev/null +++ b/src/mesh/Mesh_.js @@ -0,0 +1,287 @@ +import * as core from '../core'; + +const tempPoint = new core.Point(); +const tempPolygon = new core.Polygon(); + +/** + * Base mesh class + * @class + * @extends PIXI.Container + * @memberof PIXI.mesh + */ +export default class Mesh extends core.Container +{ + /** + * @param {PIXI.Texture} texture - The texture to use + * @param {Float32Array} [vertices] - if you want to specify the vertices + * @param {Float32Array} [uvs] - if you want to specify the uvs + * @param {Uint16Array} [indices] - if you want to specify the indices + * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts + */ + constructor(texture, vertices, uvs, indices, drawMode) + { + super(); + + /** + * The texture of the Mesh + * + * @member {PIXI.Texture} + * @private + */ + this._texture = null; + + /** + * The Uvs of the Mesh + * + * @member {Float32Array} + */ + this.uvs = uvs || new Float32Array([0, 0, + 1, 0, + 1, 1, + 0, 1]); + + /** + * An array of vertices + * + * @member {Float32Array} + */ + this.vertices = vertices || new Float32Array([0, 0, + 100, 0, + 100, 100, + 0, 100]); + + /* + * @member {Uint16Array} An array containing the indices of the vertices + */ + // TODO auto generate this based on draw mode! + this.indices = indices || new Uint16Array([0, 1, 3, 2]); + + /** + * Version of mesh uvs are dirty or not + * + * @member {number} + */ + this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ + this.indexDirty = 0; + + /** + * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove + * any blend mode. + * + * @member {number} + * @default PIXI.BLEND_MODES.NORMAL + * @see PIXI.BLEND_MODES + */ + this.blendMode = core.BLEND_MODES.NORMAL; + + /** + * Triangles in canvas mode are automatically antialiased, use this value to force triangles + * to overlap a bit with each other. + * + * @member {number} + */ + this.canvasPadding = 0; + + /** + * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.Mesh.DRAW_MODES} consts + * + * @member {number} + * @see PIXI.mesh.Mesh.DRAW_MODES + */ + this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; + + // run texture setter; + this.texture = texture; + + /** + * The default shader that is used if a mesh doesn't have a more specific one. + * + * @member {PIXI.Shader} + */ + this.shader = null; + + /** + * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any + * tint effect. + * + * @member {number} + * @memberof PIXI.mesh.Mesh# + */ + this.tintRgb = new Float32Array([1, 1, 1]); + + /** + * A map of renderer IDs to webgl render data + * + * @private + * @member {object} + */ + this._glDatas = {}; + } + + /** + * Renders the object using the WebGL renderer + * + * @private + * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer + */ + _renderWebGL(renderer) + { + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); + } + + /** + * Renders the object using the Canvas renderer + * + * @private + * @param {PIXI.CanvasRenderer} renderer - The canvas renderer. + */ + _renderCanvas(renderer) + { + renderer.plugins.mesh.render(this); + } + + /** + * When the texture is updated, this event will fire to update the scale and frame + * + * @private + */ + _onTextureUpdate() + { + /* empty */ + } + + /** + * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. + * + */ + _calculateBounds() + { + // TODO - we can cache local bounds and use them if they are dirty (like graphics) + this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length); + } + + /** + * Tests if a point is inside this mesh. Works only for TRIANGLE_MESH + * + * @param {PIXI.Point} point - the point to test + * @return {boolean} the result of the test + */ + containsPoint(point) + { + if (!this.getBounds().contains(point.x, point.y)) + { + return false; + } + + this.worldTransform.applyInverse(point, tempPoint); + + const vertices = this.vertices; + const points = tempPolygon.points; + const indices = this.indices; + const len = this.indices.length; + const step = this.drawMode === Mesh.DRAW_MODES.TRIANGLES ? 3 : 1; + + for (let i = 0; i + 2 < len; i += step) + { + const ind0 = indices[i] * 2; + const ind1 = indices[i + 1] * 2; + const ind2 = indices[i + 2] * 2; + + points[0] = vertices[ind0]; + points[1] = vertices[ind0 + 1]; + points[2] = vertices[ind1]; + points[3] = vertices[ind1 + 1]; + points[4] = vertices[ind2]; + points[5] = vertices[ind2 + 1]; + + if (tempPolygon.contains(tempPoint.x, tempPoint.y)) + { + return true; + } + } + + return false; + } + + /** + * The texture that the mesh uses. + * + * @member {PIXI.Texture} + * @memberof PIXI.mesh.Mesh# + */ + get texture() + { + return this._texture; + } + + /** + * Sets the texture the mesh uses. + * + * @param {Texture} value - The value to set. + */ + set texture(value) + { + if (this._texture === value) + { + return; + } + + this._texture = value; + + if (value) + { + // wait for the texture to load + if (value.baseTexture.hasLoaded) + { + this._onTextureUpdate(); + } + else + { + value.once('update', this._onTextureUpdate, this); + } + } + } + + /** + * The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect. + * + * @member {number} + * @memberof PIXI.mesh.Mesh# + * @default 0xFFFFFF + */ + get tint() + { + return core.utils.rgb2hex(this.tintRgb); + } + + /** + * Sets the tint the mesh uses. + * + * @param {number} value - The value to set. + */ + set tint(value) + { + this.tintRgb = core.utils.hex2rgb(value, this.tintRgb); + } +} + +/** + * Different drawing buffer modes supported + * + * @static + * @constant + * @type {object} + * @property {number} TRIANGLE_MESH + * @property {number} TRIANGLES + */ +Mesh.DRAW_MODES = { + TRIANGLE_MESH: 0, + TRIANGLES: 1, +}; diff --git a/src/mesh/geometry/Attribute.js b/src/mesh/geometry/Attribute.js new file mode 100644 index 0000000..7a0a7a5 --- /dev/null +++ b/src/mesh/geometry/Attribute.js @@ -0,0 +1,26 @@ +class Attribute +{ + + constructor(buffer, size = 2, stride = 0, start = 0, normalised = false) + { + this.buffer = buffer; + this.normalized = normalised; + this.size = size; + this.stride = stride; + this.start = start; + this.type = null; + } + + destroy() + { + this.buffer = null; + } + +} + +Attribute.from = function (buffer, stride, start, normalised) +{ + return new Attribute(buffer, stride, start, normalised); +}; + +module.exports = Attribute; diff --git a/src/mesh/geometry/Buffer.js b/src/mesh/geometry/Buffer.js new file mode 100644 index 0000000..7fc958d --- /dev/null +++ b/src/mesh/geometry/Buffer.js @@ -0,0 +1,73 @@ +let UID = 0; +/** + * Helper class to create a webGL buffer + * + * @class + * @memberof PIXI.glCore + * @param gl {WebGLRenderingContext} The current WebGL rendering context + * @param type {gl.ARRAY_BUFFER | gl.ELEMENT_ARRAY_BUFFER} @mat + * @param data {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} an array of data + * @param drawType {gl.STATIC_DRAW|gl.DYNAMIC_DRAW|gl.STREAM_DRAW} + */ +export default class Buffer +{ + constructor(data) + { + /** + * The type of the buffer + * + * @member {gl.ARRAY_BUFFER|gl.ELEMENT_ARRAY_BUFFER} + */ + // this.type = type || gl.ARRAY_BUFFER; + + /** + * The draw type of the buffer + * + * @member {gl.STATIC_DRAW|gl.DYNAMIC_DRAW|gl.STREAM_DRAW} + */ + // this.drawType = drawType || gl.STATIC_DRAW; + + /** + * The data in the buffer, as a typed array + * + * @member {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} + */ + this.data = data; + + this._glBuffers = []; + + this._updateID = 0; + + this.id = UID++; + } + + /** + * Uploads the buffer to the GPU + * @param data {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} an array of data to upload + * @param offset {Number} if only a subset of the data should be uploaded, this is the amount of data to subtract + */ + update() + { + this._updateID++; + } + + /** + * Destroys the buffer + * + */ + destroy() + { + for (let i = 0; i < this._glBuffers.length; i++) + { + this._glBuffers[i].destroy(); + } + + this.data = null; + } + + static from(data) + { + return new Buffer(data); + } +} + diff --git a/src/mesh/geometry/Geometry.js b/src/mesh/geometry/Geometry.js new file mode 100644 index 0000000..d734691 --- /dev/null +++ b/src/mesh/geometry/Geometry.js @@ -0,0 +1,55 @@ +import Attribute from './Attribute'; +import GeometryStyle from './GeometryStyle'; +import GeometryData from './GeometryData'; + +class Geometry +{ + + constructor(data, style) + { + this.style = style || new GeometryStyle(); + this.data = data || new GeometryData(); + + // / this.indexBuffer = null; + + this.glVertexArrayObjects = []; + } + + addAttribute(id, buffer, size = 2, stride = 0, start = 0, normalised = false) + { + this.style.addAttribute(id, new Attribute(buffer.id, size, stride, start, normalised)); + this.data.add(buffer.id, buffer); + + return this; + } + + addIndex(buffer) + { + this.data.addIndex(buffer); + + return this; + } + + destroy() + { + for (let i = 0; i < this.buffers.length; i++) + { + this.buffers[i].destroy(); + } + + this.buffers = null; + this.attributes = null; + + for (let i = 0; i < this.glVertexArrayObjects.length; i++) + { + this.glVertexArrayObjects[i].destroy(); + } + + this.glVertexArrayObjects = null; + + this.indexBuffer.destroy(); + this.indexBuffer = null; + } +} + +export default Geometry; diff --git a/src/mesh/geometry/GeometryData.js b/src/mesh/geometry/GeometryData.js new file mode 100644 index 0000000..28dd0ca --- /dev/null +++ b/src/mesh/geometry/GeometryData.js @@ -0,0 +1,35 @@ +class GeometryData +{ + constructor() + { + this.buffers = []; + this.indexBuffer = null; + } + + add(id, buffer) + { + // only one! + if (this.buffers.indexOf(buffer) === -1) + { + this.buffers.push(buffer); + this[id] = buffer; + } + + return this; + } + + addIndex(buffer) + { + buffer.index = true; + this.indexBuffer = buffer; + + if (this.buffers.indexOf(buffer) === -1) + { + this.buffers.push(buffer); + } + + return this; + } +} + +export default GeometryData; diff --git a/src/mesh/geometry/GeometryStyle.js b/src/mesh/geometry/GeometryStyle.js new file mode 100644 index 0000000..27d4a86 --- /dev/null +++ b/src/mesh/geometry/GeometryStyle.js @@ -0,0 +1,57 @@ +class GeometryStyle +{ + + constructor() + { + this.attributes = {}; + + this.indexBuffer = null; + } + + addAttribute(id, attribute) + { + this.attributes[id] = attribute; + + return this; + } + + addIndex(buffer) + { + this.indexBuffer = buffer; + + return this; + } + + generateAttributeLocations() + { + const array = []; + let i; + + for (i in this.attributes) + { + array.push(i); + } + + array.sort(); + + const map = {}; + + for (i = 0; i < array.length; i++) + { + map[array[i]] = i; + } + + console.log(map) + + return map; + } + + destroy() + { + this.attributes = null; + + this.indexBuffer = null; + } +} + +export default GeometryStyle; diff --git a/src/core/index.js b/src/core/index.js index 7ea739a..6f6b9dc 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -39,6 +39,7 @@ export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; +export { default as Shader } from './shader/Shader'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 369d7d3..93cb171 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -1,5 +1,5 @@ -import { GLTexture } from 'pixi-gl-core'; -import { WRAP_MODES, SCALE_MODES } from '../../const'; +import { GLShader } from 'pixi-gl-core'; +import { WRAP_MODES, SCALE_MODES, PRECISION} from '../../const'; import RenderTarget from './utils/RenderTarget'; import { removeItems } from '../../utils'; @@ -35,14 +35,22 @@ { let glShader = shader.glShaders[renderer.CONTEXT_UID] || this.generateShader(shader); + this.renderer._bindGLShader(glShader); this.syncUniforms(glShader, shader); - this.renderer._bindGLShader(glShader); } generateShader(shader) { - const glShader = new GLShader(this.gl, filter.vertexSrc, filter.fragmentSrc, PRECISION.DEFAULT); + + const attribMap = {}; + for (const i in shader.attributeData) + { + attribMap[i] = shader.attributeData[i].location; + } + + console.log(attribMap) + const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); shader.glShaders[renderer.CONTEXT_UID] = glShader; return glShader diff --git a/src/core/shader/Shader.js b/src/core/shader/Shader.js index 9666db0..fb0a85a 100644 --- a/src/core/shader/Shader.js +++ b/src/core/shader/Shader.js @@ -1,4 +1,5 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; // let math = require('../../../math'); /** @@ -33,6 +34,9 @@ // currently this does not extract structs only default types this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc, 'projectionMatrix|uSampler'); + this.attributeData = extractAttributesFromSrc(this.vertexSrc); + + this.uniforms = {}; for (const i in this.uniformData) diff --git a/src/core/shader/extractAttributesFromSrc.js b/src/core/shader/extractAttributesFromSrc.js new file mode 100644 index 0000000..b190ef4 --- /dev/null +++ b/src/core/shader/extractAttributesFromSrc.js @@ -0,0 +1,75 @@ +import glCore from 'pixi-gl-core'; + +const defaultValue = glCore.shader.defaultValue; + +function extractAttributesFromSrc(vertexSrc, mask) +{ + const vertAttributes = extractAttributesFromString(vertexSrc, mask); + + return vertAttributes; +} + +function extractAttributesFromString(string) +{ + const maskRegex = new RegExp('^(projectionMatrix|uSampler|filterArea)$'); + + const attributesArray = []; + let nameSplit; + const a = 20; + + // clean the lines a little - remove extra spaces / teabs etc + // then split along ';' + const lines = string.replace(/\s+/g, ' ') + .split(/\s*;\s*/); + + // loop through.. + for (let i = 0; i < lines.length; i++) + { + const line = lines[i].trim(); + + if (line.indexOf('attribute') > -1) + { + const splitLine = line.split(' '); + const type = splitLine[1]; + + let name = splitLine[2]; + let size = 1; + + if (name.indexOf('[') > -1) + { + // array! + nameSplit = name.split(/\[|\]/); + name = nameSplit[0]; + size *= Number(nameSplit[1]); + } + + if (!name.match(maskRegex)) + { + attributesArray.push({ + value: defaultValue(type, size), + name, + location: 0, + type, + }); + } + } + } + + attributesArray.sort(function (a, b) { + return (a.name > b.name) ? 1 : -1; + }); + + const attributes = {}; + + // now lets sort them alphabetically.. + for (let i = 0; i < attributesArray.length; i++) + { + const attrib = attributesArray[i]; + attrib.location = i; + attributes[attrib.name] = attrib; + } + + return attributes; +} + +export default extractAttributesFromSrc; diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 21e5a61..83418e0 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,78 +1,29 @@ import * as core from '../core'; -const tempPoint = new core.Point(); -const tempPolygon = new core.Polygon(); - /** * Base mesh class * @class * @extends PIXI.Container * @memberof PIXI.mesh + * @param texture {PIXI.Texture} The texture to use + * @param [vertices] {Float32Array} if you want to specify the vertices + * @param [uvs] {Float32Array} if you want to specify the uvs + * @param [indices] {Uint16Array} if you want to specify the indices + * @param [drawMode] {number} the drawMode, can be any of the Mesh.DRAW_MODES consts */ export default class Mesh extends core.Container { - /** - * @param {PIXI.Texture} texture - The texture to use - * @param {Float32Array} [vertices] - if you want to specify the vertices - * @param {Float32Array} [uvs] - if you want to specify the uvs - * @param {Uint16Array} [indices] - if you want to specify the indices - * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts - */ - constructor(texture, vertices, uvs, indices, drawMode) + + constructor(geometry, shader, drawMode) { super(); - /** - * The texture of the Mesh - * - * @member {PIXI.Texture} - * @private - */ - this._texture = null; + this.geometry = geometry; + + this.shader = shader; /** - * The Uvs of the Mesh - * - * @member {Float32Array} - */ - this.uvs = uvs || new Float32Array([0, 0, - 1, 0, - 1, 1, - 0, 1]); - - /** - * An array of vertices - * - * @member {Float32Array} - */ - this.vertices = vertices || new Float32Array([0, 0, - 100, 0, - 100, 100, - 0, 100]); - - /* - * @member {Uint16Array} An array containing the indices of the vertices - */ - // TODO auto generate this based on draw mode! - this.indices = indices || new Uint16Array([0, 1, 3, 2]); - - /** - * Version of mesh uvs are dirty or not - * - * @member {number} - */ - this.dirty = 0; - - /** - * Version of mesh indices - * - * @member {number} - */ - this.indexDirty = 0; - - /** - * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove - * any blend mode. + * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove any blend mode. * * @member {number} * @default PIXI.BLEND_MODES.NORMAL @@ -81,12 +32,10 @@ this.blendMode = core.BLEND_MODES.NORMAL; /** - * Triangles in canvas mode are automatically antialiased, use this value to force triangles - * to overlap a bit with each other. + * Triangles in canvas mode are automatically antialiased, use this value to force triangles to overlap a bit with each other. * * @member {number} */ - this.canvasPadding = 0; /** * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.Mesh.DRAW_MODES} consts @@ -95,40 +44,13 @@ * @see PIXI.mesh.Mesh.DRAW_MODES */ this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; - - // run texture setter; - this.texture = texture; - - /** - * The default shader that is used if a mesh doesn't have a more specific one. - * - * @member {PIXI.Shader} - */ - this.shader = null; - - /** - * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any - * tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - */ - this.tintRgb = new Float32Array([1, 1, 1]); - - /** - * A map of renderer IDs to webgl render data - * - * @private - * @member {object} - */ - this._glDatas = {}; } /** * Renders the object using the WebGL renderer * + * @param renderer {PIXI.WebGLRenderer} a reference to the WebGL renderer * @private - * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer */ _renderWebGL(renderer) { @@ -137,151 +59,43 @@ } /** - * Renders the object using the Canvas renderer + * Calculates the bounds of the mesh. The bounds calculation takes the worldTransform into account. * - * @private - * @param {PIXI.CanvasRenderer} renderer - The canvas renderer. - */ - _renderCanvas(renderer) - { - renderer.plugins.mesh.render(this); - } - - /** - * When the texture is updated, this event will fire to update the scale and frame - * - * @private - */ - _onTextureUpdate() - { - /* empty */ - } - - /** - * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. - * + * @param [matrix=this.worldTransform] {PIXI.Matrix} the transformation matrix of the sprite + * @return {PIXI.Rectangle} the framing rectangle */ _calculateBounds() { - // TODO - we can cache local bounds and use them if they are dirty (like graphics) - this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length); + // The position property could be set manually? + if (this.geometry.attributes.aVertexPosition) + { + const vertices = this.geometry.attributes.aVertexPosition.buffer.data; + + // TODO - we can cache local bounds and use them if they are dirty (like graphics) + this._bounds.addVertices(this.transform, vertices, 0, vertices.length); + } } - /** + /** * Tests if a point is inside this mesh. Works only for TRIANGLE_MESH * - * @param {PIXI.Point} point - the point to test + * @param point {PIXI.Point} the point to test * @return {boolean} the result of the test */ - containsPoint(point) - { - if (!this.getBounds().contains(point.x, point.y)) - { - return false; - } - this.worldTransform.applyInverse(point, tempPoint); - - const vertices = this.vertices; - const points = tempPolygon.points; - const indices = this.indices; - const len = this.indices.length; - const step = this.drawMode === Mesh.DRAW_MODES.TRIANGLES ? 3 : 1; - - for (let i = 0; i + 2 < len; i += step) - { - const ind0 = indices[i] * 2; - const ind1 = indices[i + 1] * 2; - const ind2 = indices[i + 2] * 2; - - points[0] = vertices[ind0]; - points[1] = vertices[ind0 + 1]; - points[2] = vertices[ind1]; - points[3] = vertices[ind1 + 1]; - points[4] = vertices[ind2]; - points[5] = vertices[ind2 + 1]; - - if (tempPolygon.contains(tempPoint.x, tempPoint.y)) - { - return true; - } - } - - return false; - } - - /** - * The texture that the mesh uses. - * - * @member {PIXI.Texture} - * @memberof PIXI.mesh.Mesh# - */ - get texture() - { - return this._texture; - } - - /** - * Sets the texture the mesh uses. - * - * @param {Texture} value - The value to set. - */ - set texture(value) - { - if (this._texture === value) - { - return; - } - - this._texture = value; - - if (value) - { - // wait for the texture to load - if (value.baseTexture.hasLoaded) - { - this._onTextureUpdate(); - } - else - { - value.once('update', this._onTextureUpdate, this); - } - } - } - - /** - * The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - * @default 0xFFFFFF - */ - get tint() - { - return core.utils.rgb2hex(this.tintRgb); - } - - /** - * Sets the tint the mesh uses. - * - * @param {number} value - The value to set. - */ - set tint(value) - { - this.tintRgb = core.utils.hex2rgb(value, this.tintRgb); - } } - /** * Different drawing buffer modes supported * * @static * @constant - * @type {object} - * @property {number} TRIANGLE_MESH - * @property {number} TRIANGLES + * @property {object} DRAW_MODES + * @property {number} DRAW_MODES.TRIANGLE_MESH + * @property {number} DRAW_MODES.TRIANGLES */ Mesh.DRAW_MODES = { - TRIANGLE_MESH: 0, - TRIANGLES: 1, + TRIANGLE_MESH: 1, + TRIANGLES: 2, + POINTS: 3, }; + diff --git a/src/mesh/Mesh_.js b/src/mesh/Mesh_.js new file mode 100644 index 0000000..21e5a61 --- /dev/null +++ b/src/mesh/Mesh_.js @@ -0,0 +1,287 @@ +import * as core from '../core'; + +const tempPoint = new core.Point(); +const tempPolygon = new core.Polygon(); + +/** + * Base mesh class + * @class + * @extends PIXI.Container + * @memberof PIXI.mesh + */ +export default class Mesh extends core.Container +{ + /** + * @param {PIXI.Texture} texture - The texture to use + * @param {Float32Array} [vertices] - if you want to specify the vertices + * @param {Float32Array} [uvs] - if you want to specify the uvs + * @param {Uint16Array} [indices] - if you want to specify the indices + * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts + */ + constructor(texture, vertices, uvs, indices, drawMode) + { + super(); + + /** + * The texture of the Mesh + * + * @member {PIXI.Texture} + * @private + */ + this._texture = null; + + /** + * The Uvs of the Mesh + * + * @member {Float32Array} + */ + this.uvs = uvs || new Float32Array([0, 0, + 1, 0, + 1, 1, + 0, 1]); + + /** + * An array of vertices + * + * @member {Float32Array} + */ + this.vertices = vertices || new Float32Array([0, 0, + 100, 0, + 100, 100, + 0, 100]); + + /* + * @member {Uint16Array} An array containing the indices of the vertices + */ + // TODO auto generate this based on draw mode! + this.indices = indices || new Uint16Array([0, 1, 3, 2]); + + /** + * Version of mesh uvs are dirty or not + * + * @member {number} + */ + this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ + this.indexDirty = 0; + + /** + * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove + * any blend mode. + * + * @member {number} + * @default PIXI.BLEND_MODES.NORMAL + * @see PIXI.BLEND_MODES + */ + this.blendMode = core.BLEND_MODES.NORMAL; + + /** + * Triangles in canvas mode are automatically antialiased, use this value to force triangles + * to overlap a bit with each other. + * + * @member {number} + */ + this.canvasPadding = 0; + + /** + * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.Mesh.DRAW_MODES} consts + * + * @member {number} + * @see PIXI.mesh.Mesh.DRAW_MODES + */ + this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; + + // run texture setter; + this.texture = texture; + + /** + * The default shader that is used if a mesh doesn't have a more specific one. + * + * @member {PIXI.Shader} + */ + this.shader = null; + + /** + * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any + * tint effect. + * + * @member {number} + * @memberof PIXI.mesh.Mesh# + */ + this.tintRgb = new Float32Array([1, 1, 1]); + + /** + * A map of renderer IDs to webgl render data + * + * @private + * @member {object} + */ + this._glDatas = {}; + } + + /** + * Renders the object using the WebGL renderer + * + * @private + * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer + */ + _renderWebGL(renderer) + { + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); + } + + /** + * Renders the object using the Canvas renderer + * + * @private + * @param {PIXI.CanvasRenderer} renderer - The canvas renderer. + */ + _renderCanvas(renderer) + { + renderer.plugins.mesh.render(this); + } + + /** + * When the texture is updated, this event will fire to update the scale and frame + * + * @private + */ + _onTextureUpdate() + { + /* empty */ + } + + /** + * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. + * + */ + _calculateBounds() + { + // TODO - we can cache local bounds and use them if they are dirty (like graphics) + this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length); + } + + /** + * Tests if a point is inside this mesh. Works only for TRIANGLE_MESH + * + * @param {PIXI.Point} point - the point to test + * @return {boolean} the result of the test + */ + containsPoint(point) + { + if (!this.getBounds().contains(point.x, point.y)) + { + return false; + } + + this.worldTransform.applyInverse(point, tempPoint); + + const vertices = this.vertices; + const points = tempPolygon.points; + const indices = this.indices; + const len = this.indices.length; + const step = this.drawMode === Mesh.DRAW_MODES.TRIANGLES ? 3 : 1; + + for (let i = 0; i + 2 < len; i += step) + { + const ind0 = indices[i] * 2; + const ind1 = indices[i + 1] * 2; + const ind2 = indices[i + 2] * 2; + + points[0] = vertices[ind0]; + points[1] = vertices[ind0 + 1]; + points[2] = vertices[ind1]; + points[3] = vertices[ind1 + 1]; + points[4] = vertices[ind2]; + points[5] = vertices[ind2 + 1]; + + if (tempPolygon.contains(tempPoint.x, tempPoint.y)) + { + return true; + } + } + + return false; + } + + /** + * The texture that the mesh uses. + * + * @member {PIXI.Texture} + * @memberof PIXI.mesh.Mesh# + */ + get texture() + { + return this._texture; + } + + /** + * Sets the texture the mesh uses. + * + * @param {Texture} value - The value to set. + */ + set texture(value) + { + if (this._texture === value) + { + return; + } + + this._texture = value; + + if (value) + { + // wait for the texture to load + if (value.baseTexture.hasLoaded) + { + this._onTextureUpdate(); + } + else + { + value.once('update', this._onTextureUpdate, this); + } + } + } + + /** + * The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect. + * + * @member {number} + * @memberof PIXI.mesh.Mesh# + * @default 0xFFFFFF + */ + get tint() + { + return core.utils.rgb2hex(this.tintRgb); + } + + /** + * Sets the tint the mesh uses. + * + * @param {number} value - The value to set. + */ + set tint(value) + { + this.tintRgb = core.utils.hex2rgb(value, this.tintRgb); + } +} + +/** + * Different drawing buffer modes supported + * + * @static + * @constant + * @type {object} + * @property {number} TRIANGLE_MESH + * @property {number} TRIANGLES + */ +Mesh.DRAW_MODES = { + TRIANGLE_MESH: 0, + TRIANGLES: 1, +}; diff --git a/src/mesh/geometry/Attribute.js b/src/mesh/geometry/Attribute.js new file mode 100644 index 0000000..7a0a7a5 --- /dev/null +++ b/src/mesh/geometry/Attribute.js @@ -0,0 +1,26 @@ +class Attribute +{ + + constructor(buffer, size = 2, stride = 0, start = 0, normalised = false) + { + this.buffer = buffer; + this.normalized = normalised; + this.size = size; + this.stride = stride; + this.start = start; + this.type = null; + } + + destroy() + { + this.buffer = null; + } + +} + +Attribute.from = function (buffer, stride, start, normalised) +{ + return new Attribute(buffer, stride, start, normalised); +}; + +module.exports = Attribute; diff --git a/src/mesh/geometry/Buffer.js b/src/mesh/geometry/Buffer.js new file mode 100644 index 0000000..7fc958d --- /dev/null +++ b/src/mesh/geometry/Buffer.js @@ -0,0 +1,73 @@ +let UID = 0; +/** + * Helper class to create a webGL buffer + * + * @class + * @memberof PIXI.glCore + * @param gl {WebGLRenderingContext} The current WebGL rendering context + * @param type {gl.ARRAY_BUFFER | gl.ELEMENT_ARRAY_BUFFER} @mat + * @param data {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} an array of data + * @param drawType {gl.STATIC_DRAW|gl.DYNAMIC_DRAW|gl.STREAM_DRAW} + */ +export default class Buffer +{ + constructor(data) + { + /** + * The type of the buffer + * + * @member {gl.ARRAY_BUFFER|gl.ELEMENT_ARRAY_BUFFER} + */ + // this.type = type || gl.ARRAY_BUFFER; + + /** + * The draw type of the buffer + * + * @member {gl.STATIC_DRAW|gl.DYNAMIC_DRAW|gl.STREAM_DRAW} + */ + // this.drawType = drawType || gl.STATIC_DRAW; + + /** + * The data in the buffer, as a typed array + * + * @member {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} + */ + this.data = data; + + this._glBuffers = []; + + this._updateID = 0; + + this.id = UID++; + } + + /** + * Uploads the buffer to the GPU + * @param data {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} an array of data to upload + * @param offset {Number} if only a subset of the data should be uploaded, this is the amount of data to subtract + */ + update() + { + this._updateID++; + } + + /** + * Destroys the buffer + * + */ + destroy() + { + for (let i = 0; i < this._glBuffers.length; i++) + { + this._glBuffers[i].destroy(); + } + + this.data = null; + } + + static from(data) + { + return new Buffer(data); + } +} + diff --git a/src/mesh/geometry/Geometry.js b/src/mesh/geometry/Geometry.js new file mode 100644 index 0000000..d734691 --- /dev/null +++ b/src/mesh/geometry/Geometry.js @@ -0,0 +1,55 @@ +import Attribute from './Attribute'; +import GeometryStyle from './GeometryStyle'; +import GeometryData from './GeometryData'; + +class Geometry +{ + + constructor(data, style) + { + this.style = style || new GeometryStyle(); + this.data = data || new GeometryData(); + + // / this.indexBuffer = null; + + this.glVertexArrayObjects = []; + } + + addAttribute(id, buffer, size = 2, stride = 0, start = 0, normalised = false) + { + this.style.addAttribute(id, new Attribute(buffer.id, size, stride, start, normalised)); + this.data.add(buffer.id, buffer); + + return this; + } + + addIndex(buffer) + { + this.data.addIndex(buffer); + + return this; + } + + destroy() + { + for (let i = 0; i < this.buffers.length; i++) + { + this.buffers[i].destroy(); + } + + this.buffers = null; + this.attributes = null; + + for (let i = 0; i < this.glVertexArrayObjects.length; i++) + { + this.glVertexArrayObjects[i].destroy(); + } + + this.glVertexArrayObjects = null; + + this.indexBuffer.destroy(); + this.indexBuffer = null; + } +} + +export default Geometry; diff --git a/src/mesh/geometry/GeometryData.js b/src/mesh/geometry/GeometryData.js new file mode 100644 index 0000000..28dd0ca --- /dev/null +++ b/src/mesh/geometry/GeometryData.js @@ -0,0 +1,35 @@ +class GeometryData +{ + constructor() + { + this.buffers = []; + this.indexBuffer = null; + } + + add(id, buffer) + { + // only one! + if (this.buffers.indexOf(buffer) === -1) + { + this.buffers.push(buffer); + this[id] = buffer; + } + + return this; + } + + addIndex(buffer) + { + buffer.index = true; + this.indexBuffer = buffer; + + if (this.buffers.indexOf(buffer) === -1) + { + this.buffers.push(buffer); + } + + return this; + } +} + +export default GeometryData; diff --git a/src/mesh/geometry/GeometryStyle.js b/src/mesh/geometry/GeometryStyle.js new file mode 100644 index 0000000..27d4a86 --- /dev/null +++ b/src/mesh/geometry/GeometryStyle.js @@ -0,0 +1,57 @@ +class GeometryStyle +{ + + constructor() + { + this.attributes = {}; + + this.indexBuffer = null; + } + + addAttribute(id, attribute) + { + this.attributes[id] = attribute; + + return this; + } + + addIndex(buffer) + { + this.indexBuffer = buffer; + + return this; + } + + generateAttributeLocations() + { + const array = []; + let i; + + for (i in this.attributes) + { + array.push(i); + } + + array.sort(); + + const map = {}; + + for (i = 0; i < array.length; i++) + { + map[array[i]] = i; + } + + console.log(map) + + return map; + } + + destroy() + { + this.attributes = null; + + this.indexBuffer = null; + } +} + +export default GeometryStyle; diff --git a/src/mesh/index.js b/src/mesh/index.js index 4d1cad6..b5d05aa 100644 --- a/src/mesh/index.js +++ b/src/mesh/index.js @@ -4,6 +4,11 @@ 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 Attribute } from './geometry/Attribute'; +export { default as GeometryData } from './geometry/GeometryData'; +export { default as GeometryStyle } from './geometry/GeometryStyle'; +export { default as Buffer } from './geometry/Buffer'; +export { default as Geometry } from './geometry/Geometry'; +//export { default as Plane } from './Plane'; +//export { default as NineSlicePlane } from './NineSlicePlane'; +//export { default as Rope } from './Rope'; diff --git a/src/core/index.js b/src/core/index.js index 7ea739a..6f6b9dc 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -39,6 +39,7 @@ export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; +export { default as Shader } from './shader/Shader'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 369d7d3..93cb171 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -1,5 +1,5 @@ -import { GLTexture } from 'pixi-gl-core'; -import { WRAP_MODES, SCALE_MODES } from '../../const'; +import { GLShader } from 'pixi-gl-core'; +import { WRAP_MODES, SCALE_MODES, PRECISION} from '../../const'; import RenderTarget from './utils/RenderTarget'; import { removeItems } from '../../utils'; @@ -35,14 +35,22 @@ { let glShader = shader.glShaders[renderer.CONTEXT_UID] || this.generateShader(shader); + this.renderer._bindGLShader(glShader); this.syncUniforms(glShader, shader); - this.renderer._bindGLShader(glShader); } generateShader(shader) { - const glShader = new GLShader(this.gl, filter.vertexSrc, filter.fragmentSrc, PRECISION.DEFAULT); + + const attribMap = {}; + for (const i in shader.attributeData) + { + attribMap[i] = shader.attributeData[i].location; + } + + console.log(attribMap) + const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); shader.glShaders[renderer.CONTEXT_UID] = glShader; return glShader diff --git a/src/core/shader/Shader.js b/src/core/shader/Shader.js index 9666db0..fb0a85a 100644 --- a/src/core/shader/Shader.js +++ b/src/core/shader/Shader.js @@ -1,4 +1,5 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; // let math = require('../../../math'); /** @@ -33,6 +34,9 @@ // currently this does not extract structs only default types this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc, 'projectionMatrix|uSampler'); + this.attributeData = extractAttributesFromSrc(this.vertexSrc); + + this.uniforms = {}; for (const i in this.uniformData) diff --git a/src/core/shader/extractAttributesFromSrc.js b/src/core/shader/extractAttributesFromSrc.js new file mode 100644 index 0000000..b190ef4 --- /dev/null +++ b/src/core/shader/extractAttributesFromSrc.js @@ -0,0 +1,75 @@ +import glCore from 'pixi-gl-core'; + +const defaultValue = glCore.shader.defaultValue; + +function extractAttributesFromSrc(vertexSrc, mask) +{ + const vertAttributes = extractAttributesFromString(vertexSrc, mask); + + return vertAttributes; +} + +function extractAttributesFromString(string) +{ + const maskRegex = new RegExp('^(projectionMatrix|uSampler|filterArea)$'); + + const attributesArray = []; + let nameSplit; + const a = 20; + + // clean the lines a little - remove extra spaces / teabs etc + // then split along ';' + const lines = string.replace(/\s+/g, ' ') + .split(/\s*;\s*/); + + // loop through.. + for (let i = 0; i < lines.length; i++) + { + const line = lines[i].trim(); + + if (line.indexOf('attribute') > -1) + { + const splitLine = line.split(' '); + const type = splitLine[1]; + + let name = splitLine[2]; + let size = 1; + + if (name.indexOf('[') > -1) + { + // array! + nameSplit = name.split(/\[|\]/); + name = nameSplit[0]; + size *= Number(nameSplit[1]); + } + + if (!name.match(maskRegex)) + { + attributesArray.push({ + value: defaultValue(type, size), + name, + location: 0, + type, + }); + } + } + } + + attributesArray.sort(function (a, b) { + return (a.name > b.name) ? 1 : -1; + }); + + const attributes = {}; + + // now lets sort them alphabetically.. + for (let i = 0; i < attributesArray.length; i++) + { + const attrib = attributesArray[i]; + attrib.location = i; + attributes[attrib.name] = attrib; + } + + return attributes; +} + +export default extractAttributesFromSrc; diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 21e5a61..83418e0 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,78 +1,29 @@ import * as core from '../core'; -const tempPoint = new core.Point(); -const tempPolygon = new core.Polygon(); - /** * Base mesh class * @class * @extends PIXI.Container * @memberof PIXI.mesh + * @param texture {PIXI.Texture} The texture to use + * @param [vertices] {Float32Array} if you want to specify the vertices + * @param [uvs] {Float32Array} if you want to specify the uvs + * @param [indices] {Uint16Array} if you want to specify the indices + * @param [drawMode] {number} the drawMode, can be any of the Mesh.DRAW_MODES consts */ export default class Mesh extends core.Container { - /** - * @param {PIXI.Texture} texture - The texture to use - * @param {Float32Array} [vertices] - if you want to specify the vertices - * @param {Float32Array} [uvs] - if you want to specify the uvs - * @param {Uint16Array} [indices] - if you want to specify the indices - * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts - */ - constructor(texture, vertices, uvs, indices, drawMode) + + constructor(geometry, shader, drawMode) { super(); - /** - * The texture of the Mesh - * - * @member {PIXI.Texture} - * @private - */ - this._texture = null; + this.geometry = geometry; + + this.shader = shader; /** - * The Uvs of the Mesh - * - * @member {Float32Array} - */ - this.uvs = uvs || new Float32Array([0, 0, - 1, 0, - 1, 1, - 0, 1]); - - /** - * An array of vertices - * - * @member {Float32Array} - */ - this.vertices = vertices || new Float32Array([0, 0, - 100, 0, - 100, 100, - 0, 100]); - - /* - * @member {Uint16Array} An array containing the indices of the vertices - */ - // TODO auto generate this based on draw mode! - this.indices = indices || new Uint16Array([0, 1, 3, 2]); - - /** - * Version of mesh uvs are dirty or not - * - * @member {number} - */ - this.dirty = 0; - - /** - * Version of mesh indices - * - * @member {number} - */ - this.indexDirty = 0; - - /** - * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove - * any blend mode. + * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove any blend mode. * * @member {number} * @default PIXI.BLEND_MODES.NORMAL @@ -81,12 +32,10 @@ this.blendMode = core.BLEND_MODES.NORMAL; /** - * Triangles in canvas mode are automatically antialiased, use this value to force triangles - * to overlap a bit with each other. + * Triangles in canvas mode are automatically antialiased, use this value to force triangles to overlap a bit with each other. * * @member {number} */ - this.canvasPadding = 0; /** * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.Mesh.DRAW_MODES} consts @@ -95,40 +44,13 @@ * @see PIXI.mesh.Mesh.DRAW_MODES */ this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; - - // run texture setter; - this.texture = texture; - - /** - * The default shader that is used if a mesh doesn't have a more specific one. - * - * @member {PIXI.Shader} - */ - this.shader = null; - - /** - * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any - * tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - */ - this.tintRgb = new Float32Array([1, 1, 1]); - - /** - * A map of renderer IDs to webgl render data - * - * @private - * @member {object} - */ - this._glDatas = {}; } /** * Renders the object using the WebGL renderer * + * @param renderer {PIXI.WebGLRenderer} a reference to the WebGL renderer * @private - * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer */ _renderWebGL(renderer) { @@ -137,151 +59,43 @@ } /** - * Renders the object using the Canvas renderer + * Calculates the bounds of the mesh. The bounds calculation takes the worldTransform into account. * - * @private - * @param {PIXI.CanvasRenderer} renderer - The canvas renderer. - */ - _renderCanvas(renderer) - { - renderer.plugins.mesh.render(this); - } - - /** - * When the texture is updated, this event will fire to update the scale and frame - * - * @private - */ - _onTextureUpdate() - { - /* empty */ - } - - /** - * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. - * + * @param [matrix=this.worldTransform] {PIXI.Matrix} the transformation matrix of the sprite + * @return {PIXI.Rectangle} the framing rectangle */ _calculateBounds() { - // TODO - we can cache local bounds and use them if they are dirty (like graphics) - this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length); + // The position property could be set manually? + if (this.geometry.attributes.aVertexPosition) + { + const vertices = this.geometry.attributes.aVertexPosition.buffer.data; + + // TODO - we can cache local bounds and use them if they are dirty (like graphics) + this._bounds.addVertices(this.transform, vertices, 0, vertices.length); + } } - /** + /** * Tests if a point is inside this mesh. Works only for TRIANGLE_MESH * - * @param {PIXI.Point} point - the point to test + * @param point {PIXI.Point} the point to test * @return {boolean} the result of the test */ - containsPoint(point) - { - if (!this.getBounds().contains(point.x, point.y)) - { - return false; - } - this.worldTransform.applyInverse(point, tempPoint); - - const vertices = this.vertices; - const points = tempPolygon.points; - const indices = this.indices; - const len = this.indices.length; - const step = this.drawMode === Mesh.DRAW_MODES.TRIANGLES ? 3 : 1; - - for (let i = 0; i + 2 < len; i += step) - { - const ind0 = indices[i] * 2; - const ind1 = indices[i + 1] * 2; - const ind2 = indices[i + 2] * 2; - - points[0] = vertices[ind0]; - points[1] = vertices[ind0 + 1]; - points[2] = vertices[ind1]; - points[3] = vertices[ind1 + 1]; - points[4] = vertices[ind2]; - points[5] = vertices[ind2 + 1]; - - if (tempPolygon.contains(tempPoint.x, tempPoint.y)) - { - return true; - } - } - - return false; - } - - /** - * The texture that the mesh uses. - * - * @member {PIXI.Texture} - * @memberof PIXI.mesh.Mesh# - */ - get texture() - { - return this._texture; - } - - /** - * Sets the texture the mesh uses. - * - * @param {Texture} value - The value to set. - */ - set texture(value) - { - if (this._texture === value) - { - return; - } - - this._texture = value; - - if (value) - { - // wait for the texture to load - if (value.baseTexture.hasLoaded) - { - this._onTextureUpdate(); - } - else - { - value.once('update', this._onTextureUpdate, this); - } - } - } - - /** - * The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - * @default 0xFFFFFF - */ - get tint() - { - return core.utils.rgb2hex(this.tintRgb); - } - - /** - * Sets the tint the mesh uses. - * - * @param {number} value - The value to set. - */ - set tint(value) - { - this.tintRgb = core.utils.hex2rgb(value, this.tintRgb); - } } - /** * Different drawing buffer modes supported * * @static * @constant - * @type {object} - * @property {number} TRIANGLE_MESH - * @property {number} TRIANGLES + * @property {object} DRAW_MODES + * @property {number} DRAW_MODES.TRIANGLE_MESH + * @property {number} DRAW_MODES.TRIANGLES */ Mesh.DRAW_MODES = { - TRIANGLE_MESH: 0, - TRIANGLES: 1, + TRIANGLE_MESH: 1, + TRIANGLES: 2, + POINTS: 3, }; + diff --git a/src/mesh/Mesh_.js b/src/mesh/Mesh_.js new file mode 100644 index 0000000..21e5a61 --- /dev/null +++ b/src/mesh/Mesh_.js @@ -0,0 +1,287 @@ +import * as core from '../core'; + +const tempPoint = new core.Point(); +const tempPolygon = new core.Polygon(); + +/** + * Base mesh class + * @class + * @extends PIXI.Container + * @memberof PIXI.mesh + */ +export default class Mesh extends core.Container +{ + /** + * @param {PIXI.Texture} texture - The texture to use + * @param {Float32Array} [vertices] - if you want to specify the vertices + * @param {Float32Array} [uvs] - if you want to specify the uvs + * @param {Uint16Array} [indices] - if you want to specify the indices + * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts + */ + constructor(texture, vertices, uvs, indices, drawMode) + { + super(); + + /** + * The texture of the Mesh + * + * @member {PIXI.Texture} + * @private + */ + this._texture = null; + + /** + * The Uvs of the Mesh + * + * @member {Float32Array} + */ + this.uvs = uvs || new Float32Array([0, 0, + 1, 0, + 1, 1, + 0, 1]); + + /** + * An array of vertices + * + * @member {Float32Array} + */ + this.vertices = vertices || new Float32Array([0, 0, + 100, 0, + 100, 100, + 0, 100]); + + /* + * @member {Uint16Array} An array containing the indices of the vertices + */ + // TODO auto generate this based on draw mode! + this.indices = indices || new Uint16Array([0, 1, 3, 2]); + + /** + * Version of mesh uvs are dirty or not + * + * @member {number} + */ + this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ + this.indexDirty = 0; + + /** + * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove + * any blend mode. + * + * @member {number} + * @default PIXI.BLEND_MODES.NORMAL + * @see PIXI.BLEND_MODES + */ + this.blendMode = core.BLEND_MODES.NORMAL; + + /** + * Triangles in canvas mode are automatically antialiased, use this value to force triangles + * to overlap a bit with each other. + * + * @member {number} + */ + this.canvasPadding = 0; + + /** + * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.Mesh.DRAW_MODES} consts + * + * @member {number} + * @see PIXI.mesh.Mesh.DRAW_MODES + */ + this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; + + // run texture setter; + this.texture = texture; + + /** + * The default shader that is used if a mesh doesn't have a more specific one. + * + * @member {PIXI.Shader} + */ + this.shader = null; + + /** + * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any + * tint effect. + * + * @member {number} + * @memberof PIXI.mesh.Mesh# + */ + this.tintRgb = new Float32Array([1, 1, 1]); + + /** + * A map of renderer IDs to webgl render data + * + * @private + * @member {object} + */ + this._glDatas = {}; + } + + /** + * Renders the object using the WebGL renderer + * + * @private + * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer + */ + _renderWebGL(renderer) + { + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); + } + + /** + * Renders the object using the Canvas renderer + * + * @private + * @param {PIXI.CanvasRenderer} renderer - The canvas renderer. + */ + _renderCanvas(renderer) + { + renderer.plugins.mesh.render(this); + } + + /** + * When the texture is updated, this event will fire to update the scale and frame + * + * @private + */ + _onTextureUpdate() + { + /* empty */ + } + + /** + * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. + * + */ + _calculateBounds() + { + // TODO - we can cache local bounds and use them if they are dirty (like graphics) + this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length); + } + + /** + * Tests if a point is inside this mesh. Works only for TRIANGLE_MESH + * + * @param {PIXI.Point} point - the point to test + * @return {boolean} the result of the test + */ + containsPoint(point) + { + if (!this.getBounds().contains(point.x, point.y)) + { + return false; + } + + this.worldTransform.applyInverse(point, tempPoint); + + const vertices = this.vertices; + const points = tempPolygon.points; + const indices = this.indices; + const len = this.indices.length; + const step = this.drawMode === Mesh.DRAW_MODES.TRIANGLES ? 3 : 1; + + for (let i = 0; i + 2 < len; i += step) + { + const ind0 = indices[i] * 2; + const ind1 = indices[i + 1] * 2; + const ind2 = indices[i + 2] * 2; + + points[0] = vertices[ind0]; + points[1] = vertices[ind0 + 1]; + points[2] = vertices[ind1]; + points[3] = vertices[ind1 + 1]; + points[4] = vertices[ind2]; + points[5] = vertices[ind2 + 1]; + + if (tempPolygon.contains(tempPoint.x, tempPoint.y)) + { + return true; + } + } + + return false; + } + + /** + * The texture that the mesh uses. + * + * @member {PIXI.Texture} + * @memberof PIXI.mesh.Mesh# + */ + get texture() + { + return this._texture; + } + + /** + * Sets the texture the mesh uses. + * + * @param {Texture} value - The value to set. + */ + set texture(value) + { + if (this._texture === value) + { + return; + } + + this._texture = value; + + if (value) + { + // wait for the texture to load + if (value.baseTexture.hasLoaded) + { + this._onTextureUpdate(); + } + else + { + value.once('update', this._onTextureUpdate, this); + } + } + } + + /** + * The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect. + * + * @member {number} + * @memberof PIXI.mesh.Mesh# + * @default 0xFFFFFF + */ + get tint() + { + return core.utils.rgb2hex(this.tintRgb); + } + + /** + * Sets the tint the mesh uses. + * + * @param {number} value - The value to set. + */ + set tint(value) + { + this.tintRgb = core.utils.hex2rgb(value, this.tintRgb); + } +} + +/** + * Different drawing buffer modes supported + * + * @static + * @constant + * @type {object} + * @property {number} TRIANGLE_MESH + * @property {number} TRIANGLES + */ +Mesh.DRAW_MODES = { + TRIANGLE_MESH: 0, + TRIANGLES: 1, +}; diff --git a/src/mesh/geometry/Attribute.js b/src/mesh/geometry/Attribute.js new file mode 100644 index 0000000..7a0a7a5 --- /dev/null +++ b/src/mesh/geometry/Attribute.js @@ -0,0 +1,26 @@ +class Attribute +{ + + constructor(buffer, size = 2, stride = 0, start = 0, normalised = false) + { + this.buffer = buffer; + this.normalized = normalised; + this.size = size; + this.stride = stride; + this.start = start; + this.type = null; + } + + destroy() + { + this.buffer = null; + } + +} + +Attribute.from = function (buffer, stride, start, normalised) +{ + return new Attribute(buffer, stride, start, normalised); +}; + +module.exports = Attribute; diff --git a/src/mesh/geometry/Buffer.js b/src/mesh/geometry/Buffer.js new file mode 100644 index 0000000..7fc958d --- /dev/null +++ b/src/mesh/geometry/Buffer.js @@ -0,0 +1,73 @@ +let UID = 0; +/** + * Helper class to create a webGL buffer + * + * @class + * @memberof PIXI.glCore + * @param gl {WebGLRenderingContext} The current WebGL rendering context + * @param type {gl.ARRAY_BUFFER | gl.ELEMENT_ARRAY_BUFFER} @mat + * @param data {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} an array of data + * @param drawType {gl.STATIC_DRAW|gl.DYNAMIC_DRAW|gl.STREAM_DRAW} + */ +export default class Buffer +{ + constructor(data) + { + /** + * The type of the buffer + * + * @member {gl.ARRAY_BUFFER|gl.ELEMENT_ARRAY_BUFFER} + */ + // this.type = type || gl.ARRAY_BUFFER; + + /** + * The draw type of the buffer + * + * @member {gl.STATIC_DRAW|gl.DYNAMIC_DRAW|gl.STREAM_DRAW} + */ + // this.drawType = drawType || gl.STATIC_DRAW; + + /** + * The data in the buffer, as a typed array + * + * @member {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} + */ + this.data = data; + + this._glBuffers = []; + + this._updateID = 0; + + this.id = UID++; + } + + /** + * Uploads the buffer to the GPU + * @param data {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} an array of data to upload + * @param offset {Number} if only a subset of the data should be uploaded, this is the amount of data to subtract + */ + update() + { + this._updateID++; + } + + /** + * Destroys the buffer + * + */ + destroy() + { + for (let i = 0; i < this._glBuffers.length; i++) + { + this._glBuffers[i].destroy(); + } + + this.data = null; + } + + static from(data) + { + return new Buffer(data); + } +} + diff --git a/src/mesh/geometry/Geometry.js b/src/mesh/geometry/Geometry.js new file mode 100644 index 0000000..d734691 --- /dev/null +++ b/src/mesh/geometry/Geometry.js @@ -0,0 +1,55 @@ +import Attribute from './Attribute'; +import GeometryStyle from './GeometryStyle'; +import GeometryData from './GeometryData'; + +class Geometry +{ + + constructor(data, style) + { + this.style = style || new GeometryStyle(); + this.data = data || new GeometryData(); + + // / this.indexBuffer = null; + + this.glVertexArrayObjects = []; + } + + addAttribute(id, buffer, size = 2, stride = 0, start = 0, normalised = false) + { + this.style.addAttribute(id, new Attribute(buffer.id, size, stride, start, normalised)); + this.data.add(buffer.id, buffer); + + return this; + } + + addIndex(buffer) + { + this.data.addIndex(buffer); + + return this; + } + + destroy() + { + for (let i = 0; i < this.buffers.length; i++) + { + this.buffers[i].destroy(); + } + + this.buffers = null; + this.attributes = null; + + for (let i = 0; i < this.glVertexArrayObjects.length; i++) + { + this.glVertexArrayObjects[i].destroy(); + } + + this.glVertexArrayObjects = null; + + this.indexBuffer.destroy(); + this.indexBuffer = null; + } +} + +export default Geometry; diff --git a/src/mesh/geometry/GeometryData.js b/src/mesh/geometry/GeometryData.js new file mode 100644 index 0000000..28dd0ca --- /dev/null +++ b/src/mesh/geometry/GeometryData.js @@ -0,0 +1,35 @@ +class GeometryData +{ + constructor() + { + this.buffers = []; + this.indexBuffer = null; + } + + add(id, buffer) + { + // only one! + if (this.buffers.indexOf(buffer) === -1) + { + this.buffers.push(buffer); + this[id] = buffer; + } + + return this; + } + + addIndex(buffer) + { + buffer.index = true; + this.indexBuffer = buffer; + + if (this.buffers.indexOf(buffer) === -1) + { + this.buffers.push(buffer); + } + + return this; + } +} + +export default GeometryData; diff --git a/src/mesh/geometry/GeometryStyle.js b/src/mesh/geometry/GeometryStyle.js new file mode 100644 index 0000000..27d4a86 --- /dev/null +++ b/src/mesh/geometry/GeometryStyle.js @@ -0,0 +1,57 @@ +class GeometryStyle +{ + + constructor() + { + this.attributes = {}; + + this.indexBuffer = null; + } + + addAttribute(id, attribute) + { + this.attributes[id] = attribute; + + return this; + } + + addIndex(buffer) + { + this.indexBuffer = buffer; + + return this; + } + + generateAttributeLocations() + { + const array = []; + let i; + + for (i in this.attributes) + { + array.push(i); + } + + array.sort(); + + const map = {}; + + for (i = 0; i < array.length; i++) + { + map[array[i]] = i; + } + + console.log(map) + + return map; + } + + destroy() + { + this.attributes = null; + + this.indexBuffer = null; + } +} + +export default GeometryStyle; diff --git a/src/mesh/index.js b/src/mesh/index.js index 4d1cad6..b5d05aa 100644 --- a/src/mesh/index.js +++ b/src/mesh/index.js @@ -4,6 +4,11 @@ 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 Attribute } from './geometry/Attribute'; +export { default as GeometryData } from './geometry/GeometryData'; +export { default as GeometryStyle } from './geometry/GeometryStyle'; +export { default as Buffer } from './geometry/Buffer'; +export { default as Geometry } from './geometry/Geometry'; +//export { default as Plane } from './Plane'; +//export { default as NineSlicePlane } from './NineSlicePlane'; +//export { default as Rope } from './Rope'; diff --git a/src/mesh/webgl/MeshRenderer copy.js b/src/mesh/webgl/MeshRenderer copy.js new file mode 100644 index 0000000..e712a9f --- /dev/null +++ b/src/mesh/webgl/MeshRenderer copy.js @@ -0,0 +1,110 @@ +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 default 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 glCore.GLShader(gl, + glslify('./mesh.vert'), + glslify('./mesh.frag'), + core.PRECISION.DEFAULT); + } + + /** + * 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._bindGLShader(glData.shader); + + glData.shader.uniforms.uSampler = renderer.bindTexture(texture); + 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/index.js b/src/core/index.js index 7ea739a..6f6b9dc 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -39,6 +39,7 @@ export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; +export { default as Shader } from './shader/Shader'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 369d7d3..93cb171 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -1,5 +1,5 @@ -import { GLTexture } from 'pixi-gl-core'; -import { WRAP_MODES, SCALE_MODES } from '../../const'; +import { GLShader } from 'pixi-gl-core'; +import { WRAP_MODES, SCALE_MODES, PRECISION} from '../../const'; import RenderTarget from './utils/RenderTarget'; import { removeItems } from '../../utils'; @@ -35,14 +35,22 @@ { let glShader = shader.glShaders[renderer.CONTEXT_UID] || this.generateShader(shader); + this.renderer._bindGLShader(glShader); this.syncUniforms(glShader, shader); - this.renderer._bindGLShader(glShader); } generateShader(shader) { - const glShader = new GLShader(this.gl, filter.vertexSrc, filter.fragmentSrc, PRECISION.DEFAULT); + + const attribMap = {}; + for (const i in shader.attributeData) + { + attribMap[i] = shader.attributeData[i].location; + } + + console.log(attribMap) + const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); shader.glShaders[renderer.CONTEXT_UID] = glShader; return glShader diff --git a/src/core/shader/Shader.js b/src/core/shader/Shader.js index 9666db0..fb0a85a 100644 --- a/src/core/shader/Shader.js +++ b/src/core/shader/Shader.js @@ -1,4 +1,5 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; // let math = require('../../../math'); /** @@ -33,6 +34,9 @@ // currently this does not extract structs only default types this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc, 'projectionMatrix|uSampler'); + this.attributeData = extractAttributesFromSrc(this.vertexSrc); + + this.uniforms = {}; for (const i in this.uniformData) diff --git a/src/core/shader/extractAttributesFromSrc.js b/src/core/shader/extractAttributesFromSrc.js new file mode 100644 index 0000000..b190ef4 --- /dev/null +++ b/src/core/shader/extractAttributesFromSrc.js @@ -0,0 +1,75 @@ +import glCore from 'pixi-gl-core'; + +const defaultValue = glCore.shader.defaultValue; + +function extractAttributesFromSrc(vertexSrc, mask) +{ + const vertAttributes = extractAttributesFromString(vertexSrc, mask); + + return vertAttributes; +} + +function extractAttributesFromString(string) +{ + const maskRegex = new RegExp('^(projectionMatrix|uSampler|filterArea)$'); + + const attributesArray = []; + let nameSplit; + const a = 20; + + // clean the lines a little - remove extra spaces / teabs etc + // then split along ';' + const lines = string.replace(/\s+/g, ' ') + .split(/\s*;\s*/); + + // loop through.. + for (let i = 0; i < lines.length; i++) + { + const line = lines[i].trim(); + + if (line.indexOf('attribute') > -1) + { + const splitLine = line.split(' '); + const type = splitLine[1]; + + let name = splitLine[2]; + let size = 1; + + if (name.indexOf('[') > -1) + { + // array! + nameSplit = name.split(/\[|\]/); + name = nameSplit[0]; + size *= Number(nameSplit[1]); + } + + if (!name.match(maskRegex)) + { + attributesArray.push({ + value: defaultValue(type, size), + name, + location: 0, + type, + }); + } + } + } + + attributesArray.sort(function (a, b) { + return (a.name > b.name) ? 1 : -1; + }); + + const attributes = {}; + + // now lets sort them alphabetically.. + for (let i = 0; i < attributesArray.length; i++) + { + const attrib = attributesArray[i]; + attrib.location = i; + attributes[attrib.name] = attrib; + } + + return attributes; +} + +export default extractAttributesFromSrc; diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 21e5a61..83418e0 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -1,78 +1,29 @@ import * as core from '../core'; -const tempPoint = new core.Point(); -const tempPolygon = new core.Polygon(); - /** * Base mesh class * @class * @extends PIXI.Container * @memberof PIXI.mesh + * @param texture {PIXI.Texture} The texture to use + * @param [vertices] {Float32Array} if you want to specify the vertices + * @param [uvs] {Float32Array} if you want to specify the uvs + * @param [indices] {Uint16Array} if you want to specify the indices + * @param [drawMode] {number} the drawMode, can be any of the Mesh.DRAW_MODES consts */ export default class Mesh extends core.Container { - /** - * @param {PIXI.Texture} texture - The texture to use - * @param {Float32Array} [vertices] - if you want to specify the vertices - * @param {Float32Array} [uvs] - if you want to specify the uvs - * @param {Uint16Array} [indices] - if you want to specify the indices - * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts - */ - constructor(texture, vertices, uvs, indices, drawMode) + + constructor(geometry, shader, drawMode) { super(); - /** - * The texture of the Mesh - * - * @member {PIXI.Texture} - * @private - */ - this._texture = null; + this.geometry = geometry; + + this.shader = shader; /** - * The Uvs of the Mesh - * - * @member {Float32Array} - */ - this.uvs = uvs || new Float32Array([0, 0, - 1, 0, - 1, 1, - 0, 1]); - - /** - * An array of vertices - * - * @member {Float32Array} - */ - this.vertices = vertices || new Float32Array([0, 0, - 100, 0, - 100, 100, - 0, 100]); - - /* - * @member {Uint16Array} An array containing the indices of the vertices - */ - // TODO auto generate this based on draw mode! - this.indices = indices || new Uint16Array([0, 1, 3, 2]); - - /** - * Version of mesh uvs are dirty or not - * - * @member {number} - */ - this.dirty = 0; - - /** - * Version of mesh indices - * - * @member {number} - */ - this.indexDirty = 0; - - /** - * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove - * any blend mode. + * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove any blend mode. * * @member {number} * @default PIXI.BLEND_MODES.NORMAL @@ -81,12 +32,10 @@ this.blendMode = core.BLEND_MODES.NORMAL; /** - * Triangles in canvas mode are automatically antialiased, use this value to force triangles - * to overlap a bit with each other. + * Triangles in canvas mode are automatically antialiased, use this value to force triangles to overlap a bit with each other. * * @member {number} */ - this.canvasPadding = 0; /** * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.Mesh.DRAW_MODES} consts @@ -95,40 +44,13 @@ * @see PIXI.mesh.Mesh.DRAW_MODES */ this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; - - // run texture setter; - this.texture = texture; - - /** - * The default shader that is used if a mesh doesn't have a more specific one. - * - * @member {PIXI.Shader} - */ - this.shader = null; - - /** - * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any - * tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - */ - this.tintRgb = new Float32Array([1, 1, 1]); - - /** - * A map of renderer IDs to webgl render data - * - * @private - * @member {object} - */ - this._glDatas = {}; } /** * Renders the object using the WebGL renderer * + * @param renderer {PIXI.WebGLRenderer} a reference to the WebGL renderer * @private - * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer */ _renderWebGL(renderer) { @@ -137,151 +59,43 @@ } /** - * Renders the object using the Canvas renderer + * Calculates the bounds of the mesh. The bounds calculation takes the worldTransform into account. * - * @private - * @param {PIXI.CanvasRenderer} renderer - The canvas renderer. - */ - _renderCanvas(renderer) - { - renderer.plugins.mesh.render(this); - } - - /** - * When the texture is updated, this event will fire to update the scale and frame - * - * @private - */ - _onTextureUpdate() - { - /* empty */ - } - - /** - * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. - * + * @param [matrix=this.worldTransform] {PIXI.Matrix} the transformation matrix of the sprite + * @return {PIXI.Rectangle} the framing rectangle */ _calculateBounds() { - // TODO - we can cache local bounds and use them if they are dirty (like graphics) - this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length); + // The position property could be set manually? + if (this.geometry.attributes.aVertexPosition) + { + const vertices = this.geometry.attributes.aVertexPosition.buffer.data; + + // TODO - we can cache local bounds and use them if they are dirty (like graphics) + this._bounds.addVertices(this.transform, vertices, 0, vertices.length); + } } - /** + /** * Tests if a point is inside this mesh. Works only for TRIANGLE_MESH * - * @param {PIXI.Point} point - the point to test + * @param point {PIXI.Point} the point to test * @return {boolean} the result of the test */ - containsPoint(point) - { - if (!this.getBounds().contains(point.x, point.y)) - { - return false; - } - this.worldTransform.applyInverse(point, tempPoint); - - const vertices = this.vertices; - const points = tempPolygon.points; - const indices = this.indices; - const len = this.indices.length; - const step = this.drawMode === Mesh.DRAW_MODES.TRIANGLES ? 3 : 1; - - for (let i = 0; i + 2 < len; i += step) - { - const ind0 = indices[i] * 2; - const ind1 = indices[i + 1] * 2; - const ind2 = indices[i + 2] * 2; - - points[0] = vertices[ind0]; - points[1] = vertices[ind0 + 1]; - points[2] = vertices[ind1]; - points[3] = vertices[ind1 + 1]; - points[4] = vertices[ind2]; - points[5] = vertices[ind2 + 1]; - - if (tempPolygon.contains(tempPoint.x, tempPoint.y)) - { - return true; - } - } - - return false; - } - - /** - * The texture that the mesh uses. - * - * @member {PIXI.Texture} - * @memberof PIXI.mesh.Mesh# - */ - get texture() - { - return this._texture; - } - - /** - * Sets the texture the mesh uses. - * - * @param {Texture} value - The value to set. - */ - set texture(value) - { - if (this._texture === value) - { - return; - } - - this._texture = value; - - if (value) - { - // wait for the texture to load - if (value.baseTexture.hasLoaded) - { - this._onTextureUpdate(); - } - else - { - value.once('update', this._onTextureUpdate, this); - } - } - } - - /** - * The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect. - * - * @member {number} - * @memberof PIXI.mesh.Mesh# - * @default 0xFFFFFF - */ - get tint() - { - return core.utils.rgb2hex(this.tintRgb); - } - - /** - * Sets the tint the mesh uses. - * - * @param {number} value - The value to set. - */ - set tint(value) - { - this.tintRgb = core.utils.hex2rgb(value, this.tintRgb); - } } - /** * Different drawing buffer modes supported * * @static * @constant - * @type {object} - * @property {number} TRIANGLE_MESH - * @property {number} TRIANGLES + * @property {object} DRAW_MODES + * @property {number} DRAW_MODES.TRIANGLE_MESH + * @property {number} DRAW_MODES.TRIANGLES */ Mesh.DRAW_MODES = { - TRIANGLE_MESH: 0, - TRIANGLES: 1, + TRIANGLE_MESH: 1, + TRIANGLES: 2, + POINTS: 3, }; + diff --git a/src/mesh/Mesh_.js b/src/mesh/Mesh_.js new file mode 100644 index 0000000..21e5a61 --- /dev/null +++ b/src/mesh/Mesh_.js @@ -0,0 +1,287 @@ +import * as core from '../core'; + +const tempPoint = new core.Point(); +const tempPolygon = new core.Polygon(); + +/** + * Base mesh class + * @class + * @extends PIXI.Container + * @memberof PIXI.mesh + */ +export default class Mesh extends core.Container +{ + /** + * @param {PIXI.Texture} texture - The texture to use + * @param {Float32Array} [vertices] - if you want to specify the vertices + * @param {Float32Array} [uvs] - if you want to specify the uvs + * @param {Uint16Array} [indices] - if you want to specify the indices + * @param {number} [drawMode] - the drawMode, can be any of the Mesh.DRAW_MODES consts + */ + constructor(texture, vertices, uvs, indices, drawMode) + { + super(); + + /** + * The texture of the Mesh + * + * @member {PIXI.Texture} + * @private + */ + this._texture = null; + + /** + * The Uvs of the Mesh + * + * @member {Float32Array} + */ + this.uvs = uvs || new Float32Array([0, 0, + 1, 0, + 1, 1, + 0, 1]); + + /** + * An array of vertices + * + * @member {Float32Array} + */ + this.vertices = vertices || new Float32Array([0, 0, + 100, 0, + 100, 100, + 0, 100]); + + /* + * @member {Uint16Array} An array containing the indices of the vertices + */ + // TODO auto generate this based on draw mode! + this.indices = indices || new Uint16Array([0, 1, 3, 2]); + + /** + * Version of mesh uvs are dirty or not + * + * @member {number} + */ + this.dirty = 0; + + /** + * Version of mesh indices + * + * @member {number} + */ + this.indexDirty = 0; + + /** + * The blend mode to be applied to the sprite. Set to `PIXI.BLEND_MODES.NORMAL` to remove + * any blend mode. + * + * @member {number} + * @default PIXI.BLEND_MODES.NORMAL + * @see PIXI.BLEND_MODES + */ + this.blendMode = core.BLEND_MODES.NORMAL; + + /** + * Triangles in canvas mode are automatically antialiased, use this value to force triangles + * to overlap a bit with each other. + * + * @member {number} + */ + this.canvasPadding = 0; + + /** + * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.Mesh.DRAW_MODES} consts + * + * @member {number} + * @see PIXI.mesh.Mesh.DRAW_MODES + */ + this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH; + + // run texture setter; + this.texture = texture; + + /** + * The default shader that is used if a mesh doesn't have a more specific one. + * + * @member {PIXI.Shader} + */ + this.shader = null; + + /** + * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any + * tint effect. + * + * @member {number} + * @memberof PIXI.mesh.Mesh# + */ + this.tintRgb = new Float32Array([1, 1, 1]); + + /** + * A map of renderer IDs to webgl render data + * + * @private + * @member {object} + */ + this._glDatas = {}; + } + + /** + * Renders the object using the WebGL renderer + * + * @private + * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer + */ + _renderWebGL(renderer) + { + renderer.setObjectRenderer(renderer.plugins.mesh); + renderer.plugins.mesh.render(this); + } + + /** + * Renders the object using the Canvas renderer + * + * @private + * @param {PIXI.CanvasRenderer} renderer - The canvas renderer. + */ + _renderCanvas(renderer) + { + renderer.plugins.mesh.render(this); + } + + /** + * When the texture is updated, this event will fire to update the scale and frame + * + * @private + */ + _onTextureUpdate() + { + /* empty */ + } + + /** + * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. + * + */ + _calculateBounds() + { + // TODO - we can cache local bounds and use them if they are dirty (like graphics) + this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length); + } + + /** + * Tests if a point is inside this mesh. Works only for TRIANGLE_MESH + * + * @param {PIXI.Point} point - the point to test + * @return {boolean} the result of the test + */ + containsPoint(point) + { + if (!this.getBounds().contains(point.x, point.y)) + { + return false; + } + + this.worldTransform.applyInverse(point, tempPoint); + + const vertices = this.vertices; + const points = tempPolygon.points; + const indices = this.indices; + const len = this.indices.length; + const step = this.drawMode === Mesh.DRAW_MODES.TRIANGLES ? 3 : 1; + + for (let i = 0; i + 2 < len; i += step) + { + const ind0 = indices[i] * 2; + const ind1 = indices[i + 1] * 2; + const ind2 = indices[i + 2] * 2; + + points[0] = vertices[ind0]; + points[1] = vertices[ind0 + 1]; + points[2] = vertices[ind1]; + points[3] = vertices[ind1 + 1]; + points[4] = vertices[ind2]; + points[5] = vertices[ind2 + 1]; + + if (tempPolygon.contains(tempPoint.x, tempPoint.y)) + { + return true; + } + } + + return false; + } + + /** + * The texture that the mesh uses. + * + * @member {PIXI.Texture} + * @memberof PIXI.mesh.Mesh# + */ + get texture() + { + return this._texture; + } + + /** + * Sets the texture the mesh uses. + * + * @param {Texture} value - The value to set. + */ + set texture(value) + { + if (this._texture === value) + { + return; + } + + this._texture = value; + + if (value) + { + // wait for the texture to load + if (value.baseTexture.hasLoaded) + { + this._onTextureUpdate(); + } + else + { + value.once('update', this._onTextureUpdate, this); + } + } + } + + /** + * The tint applied to the mesh. This is a hex value. A value of 0xFFFFFF will remove any tint effect. + * + * @member {number} + * @memberof PIXI.mesh.Mesh# + * @default 0xFFFFFF + */ + get tint() + { + return core.utils.rgb2hex(this.tintRgb); + } + + /** + * Sets the tint the mesh uses. + * + * @param {number} value - The value to set. + */ + set tint(value) + { + this.tintRgb = core.utils.hex2rgb(value, this.tintRgb); + } +} + +/** + * Different drawing buffer modes supported + * + * @static + * @constant + * @type {object} + * @property {number} TRIANGLE_MESH + * @property {number} TRIANGLES + */ +Mesh.DRAW_MODES = { + TRIANGLE_MESH: 0, + TRIANGLES: 1, +}; diff --git a/src/mesh/geometry/Attribute.js b/src/mesh/geometry/Attribute.js new file mode 100644 index 0000000..7a0a7a5 --- /dev/null +++ b/src/mesh/geometry/Attribute.js @@ -0,0 +1,26 @@ +class Attribute +{ + + constructor(buffer, size = 2, stride = 0, start = 0, normalised = false) + { + this.buffer = buffer; + this.normalized = normalised; + this.size = size; + this.stride = stride; + this.start = start; + this.type = null; + } + + destroy() + { + this.buffer = null; + } + +} + +Attribute.from = function (buffer, stride, start, normalised) +{ + return new Attribute(buffer, stride, start, normalised); +}; + +module.exports = Attribute; diff --git a/src/mesh/geometry/Buffer.js b/src/mesh/geometry/Buffer.js new file mode 100644 index 0000000..7fc958d --- /dev/null +++ b/src/mesh/geometry/Buffer.js @@ -0,0 +1,73 @@ +let UID = 0; +/** + * Helper class to create a webGL buffer + * + * @class + * @memberof PIXI.glCore + * @param gl {WebGLRenderingContext} The current WebGL rendering context + * @param type {gl.ARRAY_BUFFER | gl.ELEMENT_ARRAY_BUFFER} @mat + * @param data {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} an array of data + * @param drawType {gl.STATIC_DRAW|gl.DYNAMIC_DRAW|gl.STREAM_DRAW} + */ +export default class Buffer +{ + constructor(data) + { + /** + * The type of the buffer + * + * @member {gl.ARRAY_BUFFER|gl.ELEMENT_ARRAY_BUFFER} + */ + // this.type = type || gl.ARRAY_BUFFER; + + /** + * The draw type of the buffer + * + * @member {gl.STATIC_DRAW|gl.DYNAMIC_DRAW|gl.STREAM_DRAW} + */ + // this.drawType = drawType || gl.STATIC_DRAW; + + /** + * The data in the buffer, as a typed array + * + * @member {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} + */ + this.data = data; + + this._glBuffers = []; + + this._updateID = 0; + + this.id = UID++; + } + + /** + * Uploads the buffer to the GPU + * @param data {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} an array of data to upload + * @param offset {Number} if only a subset of the data should be uploaded, this is the amount of data to subtract + */ + update() + { + this._updateID++; + } + + /** + * Destroys the buffer + * + */ + destroy() + { + for (let i = 0; i < this._glBuffers.length; i++) + { + this._glBuffers[i].destroy(); + } + + this.data = null; + } + + static from(data) + { + return new Buffer(data); + } +} + diff --git a/src/mesh/geometry/Geometry.js b/src/mesh/geometry/Geometry.js new file mode 100644 index 0000000..d734691 --- /dev/null +++ b/src/mesh/geometry/Geometry.js @@ -0,0 +1,55 @@ +import Attribute from './Attribute'; +import GeometryStyle from './GeometryStyle'; +import GeometryData from './GeometryData'; + +class Geometry +{ + + constructor(data, style) + { + this.style = style || new GeometryStyle(); + this.data = data || new GeometryData(); + + // / this.indexBuffer = null; + + this.glVertexArrayObjects = []; + } + + addAttribute(id, buffer, size = 2, stride = 0, start = 0, normalised = false) + { + this.style.addAttribute(id, new Attribute(buffer.id, size, stride, start, normalised)); + this.data.add(buffer.id, buffer); + + return this; + } + + addIndex(buffer) + { + this.data.addIndex(buffer); + + return this; + } + + destroy() + { + for (let i = 0; i < this.buffers.length; i++) + { + this.buffers[i].destroy(); + } + + this.buffers = null; + this.attributes = null; + + for (let i = 0; i < this.glVertexArrayObjects.length; i++) + { + this.glVertexArrayObjects[i].destroy(); + } + + this.glVertexArrayObjects = null; + + this.indexBuffer.destroy(); + this.indexBuffer = null; + } +} + +export default Geometry; diff --git a/src/mesh/geometry/GeometryData.js b/src/mesh/geometry/GeometryData.js new file mode 100644 index 0000000..28dd0ca --- /dev/null +++ b/src/mesh/geometry/GeometryData.js @@ -0,0 +1,35 @@ +class GeometryData +{ + constructor() + { + this.buffers = []; + this.indexBuffer = null; + } + + add(id, buffer) + { + // only one! + if (this.buffers.indexOf(buffer) === -1) + { + this.buffers.push(buffer); + this[id] = buffer; + } + + return this; + } + + addIndex(buffer) + { + buffer.index = true; + this.indexBuffer = buffer; + + if (this.buffers.indexOf(buffer) === -1) + { + this.buffers.push(buffer); + } + + return this; + } +} + +export default GeometryData; diff --git a/src/mesh/geometry/GeometryStyle.js b/src/mesh/geometry/GeometryStyle.js new file mode 100644 index 0000000..27d4a86 --- /dev/null +++ b/src/mesh/geometry/GeometryStyle.js @@ -0,0 +1,57 @@ +class GeometryStyle +{ + + constructor() + { + this.attributes = {}; + + this.indexBuffer = null; + } + + addAttribute(id, attribute) + { + this.attributes[id] = attribute; + + return this; + } + + addIndex(buffer) + { + this.indexBuffer = buffer; + + return this; + } + + generateAttributeLocations() + { + const array = []; + let i; + + for (i in this.attributes) + { + array.push(i); + } + + array.sort(); + + const map = {}; + + for (i = 0; i < array.length; i++) + { + map[array[i]] = i; + } + + console.log(map) + + return map; + } + + destroy() + { + this.attributes = null; + + this.indexBuffer = null; + } +} + +export default GeometryStyle; diff --git a/src/mesh/index.js b/src/mesh/index.js index 4d1cad6..b5d05aa 100644 --- a/src/mesh/index.js +++ b/src/mesh/index.js @@ -4,6 +4,11 @@ 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 Attribute } from './geometry/Attribute'; +export { default as GeometryData } from './geometry/GeometryData'; +export { default as GeometryStyle } from './geometry/GeometryStyle'; +export { default as Buffer } from './geometry/Buffer'; +export { default as Geometry } from './geometry/Geometry'; +//export { default as Plane } from './Plane'; +//export { default as NineSlicePlane } from './NineSlicePlane'; +//export { default as Rope } from './Rope'; diff --git a/src/mesh/webgl/MeshRenderer copy.js b/src/mesh/webgl/MeshRenderer copy.js new file mode 100644 index 0000000..e712a9f --- /dev/null +++ b/src/mesh/webgl/MeshRenderer copy.js @@ -0,0 +1,110 @@ +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 default 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 glCore.GLShader(gl, + glslify('./mesh.vert'), + glslify('./mesh.frag'), + core.PRECISION.DEFAULT); + } + + /** + * 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._bindGLShader(glData.shader); + + glData.shader.uniforms.uSampler = renderer.bindTexture(texture); + 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/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js index e018173..3e43867 100644 --- a/src/mesh/webgl/MeshRenderer.js +++ b/src/mesh/webgl/MeshRenderer.js @@ -2,8 +2,6 @@ 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 */ @@ -28,12 +26,9 @@ */ onContextChange() { - const gl = this.renderer.gl; + this.gl = this.renderer.gl; - this.shader = new glCore.GLShader(gl, - glslify('./mesh.vert'), - glslify('./mesh.frag'), - core.PRECISION.DEFAULT); + //nothing to see here! } /** @@ -43,69 +38,156 @@ */ render(mesh) { - const renderer = this.renderer; - const gl = renderer.gl; - const texture = mesh._texture; + // get rid of any thing that may be batching. +// renderer.flush(); - if (!texture.valid) + // always use shaders - rather than GLShadr + + // generate geometry structure from a shader :) + + // set the shader props.. + if (mesh.shader.uniforms.translationMatrix) { - return; + // the transform! + mesh.shader.uniforms.translationMatrix = mesh.transform.worldTransform.toArray(true); } - let glData = mesh._glDatas[renderer.CONTEXT_UID]; - - if (!glData) - { - renderer.bindVao(null); - - 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(mesh.uvs); - } - - if (mesh.indexDirty !== glData.indexDirty) - { - glData.indexDirty = mesh.indexDirty; - glData.indexBuffer.upload(mesh.indices); - } - - glData.vertexBuffer.upload(mesh.vertices); - - renderer._bindGLShader(glData.shader); - - glData.shader.uniforms.uSampler = renderer.bindTexture(texture); - + // set the correct blend mode 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; + // bind the shader.. + // TODO rename filter to shader + renderer.bindShader(mesh.shader); - const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; + // now time for geometry.. - renderer.bindVao(glData.vao); - glData.vao.draw(drawMode, mesh.indices.length, 0); + // bind the geometry... + this.bindGeometry(mesh.geometry); + + // then render it.. + this.renderGeometry(mesh.geometry, mesh.drawMode); + + // then unbind it.. + // TODO - maybe create a state in renderer for geometry? + // maybe renderer shouldxwww be a renderer? + // although pretty much ALL items will simply be geometry + shader + // TODO wont be required! +// this.unbindGeometry(mesh.geometry); + + } + + bindGeometry(geometry) + { + const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry); + + this.renderer.bindVao(vao); + + if (geometry.autoUpdate) + { + // TODO - optimise later! + for (let i = 0; i < geometry.buffers.length; i++) + { + const buffer = geometry.buffers[i]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + if (buffer._updateID !== glBuffer._updateID) + { + glBuffer._updateID = buffer._updateID; + + // TODO - partial upload?? + glBuffer.upload(buffer.data, 0); + } + } + } + } + + unbindGeometry(geometry) + { + const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID]; + vao.unbind(); + } + + renderGeometry(geometry, drawMode) + { + const gl = this.gl; + + const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID]; + + // TODO - build a map +/* if (drawMode === CONST.DRAW_MODES.TRIANGLE_MESH) + { +<<<<<<< 79177787e99a4893e30f81fc1672bf6ccc026c1e + glData.dirty = mesh.dirty; + glData.uvBuffer.upload(mesh.uvs); +======= + drawMode = gl.POINTS; +>>>>>>> pass one of geom + } + else if (drawMode === CONST.DRAW_MODES.POINTS) + { + drawMode = gl.POINTS; + } + else + { + drawMode = gl.POINTS; + }*/ + + drawMode = gl.TRIANGLES; + + vao.draw(drawMode); + } + + initGeometryVao(geometry) + { + const gl = this.gl; + + const vao = this.renderer.createVao(); + + const buffers = geometry.data.buffers; + + // first update - and creat the buffers! + for (let i = 0; i < buffers.length; i++) + { + const buffer = buffers[i]; + + if (!buffer._glBuffers[this.CONTEXT_UID]) + { + if (buffer.index) + { + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); + } + else + { + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data); + } + } + } + + // first update the index buffer.. + vao.addIndex(geometry.data.indexBuffer._glBuffers[this.CONTEXT_UID]); + + const map = geometry.style.generateAttributeLocations(); + + // next update the attributes buffer.. + for (const j in geometry.style.attributes) + { + const attribute = geometry.style.attributes[j]; + const buffer = geometry.data[attribute.buffer]; + + // need to know the shader.. + // or DO we... NOPE! + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + vao.addAttribute(glBuffer, { + size: attribute.size, + location: map[j], + }, gl.FLOAT, false, attribute.stride, attribute.start); + } + + geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; + + return vao; } }