diff --git a/src/core/const.js b/src/core/const.js index 5e3aff8..ef6f662 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -135,9 +135,9 @@ }; export const TYPES = { - FLOAT: 5126, - UNSIGNED_SHORT:5123, - UNSIGNED_BYTE: 5121 + FLOAT: 5126, + UNSIGNED_SHORT: 5123, + UNSIGNED_BYTE: 5121, }; /** diff --git a/src/core/const.js b/src/core/const.js index 5e3aff8..ef6f662 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -135,9 +135,9 @@ }; export const TYPES = { - FLOAT: 5126, - UNSIGNED_SHORT:5123, - UNSIGNED_BYTE: 5121 + FLOAT: 5126, + UNSIGNED_SHORT: 5123, + UNSIGNED_BYTE: 5121, }; /** diff --git a/src/core/utils/getBufferType.js b/src/core/utils/getBufferType.js new file mode 100644 index 0000000..a4a3b29 --- /dev/null +++ b/src/core/utils/getBufferType.js @@ -0,0 +1,26 @@ +export default function getBufferType(array) +{ + if (array.BYTES_PER_ELEMENT === 4) + { + if (array instanceof Float32Array) + { + return 'Float32Array'; + } + else if (array instanceof Uint32Array) + { + return 'Uint32Array'; + } + + return 'Int32Array'; + } + else if (array.BYTES_PER_ELEMENT === 2) + { + if (array instanceof Uint16Array) + { + return 'Uint16Array'; + } + } + + // TODO map out the rest of the array elements! + return null; +} diff --git a/src/core/const.js b/src/core/const.js index 5e3aff8..ef6f662 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -135,9 +135,9 @@ }; export const TYPES = { - FLOAT: 5126, - UNSIGNED_SHORT:5123, - UNSIGNED_BYTE: 5121 + FLOAT: 5126, + UNSIGNED_SHORT: 5123, + UNSIGNED_BYTE: 5121, }; /** diff --git a/src/core/utils/getBufferType.js b/src/core/utils/getBufferType.js new file mode 100644 index 0000000..a4a3b29 --- /dev/null +++ b/src/core/utils/getBufferType.js @@ -0,0 +1,26 @@ +export default function getBufferType(array) +{ + if (array.BYTES_PER_ELEMENT === 4) + { + if (array instanceof Float32Array) + { + return 'Float32Array'; + } + else if (array instanceof Uint32Array) + { + return 'Uint32Array'; + } + + return 'Int32Array'; + } + else if (array.BYTES_PER_ELEMENT === 2) + { + if (array instanceof Uint16Array) + { + return 'Uint16Array'; + } + } + + // TODO map out the rest of the array elements! + return null; +} diff --git a/src/core/utils/interleaveTypedArrays.js b/src/core/utils/interleaveTypedArrays.js new file mode 100644 index 0000000..8da791f --- /dev/null +++ b/src/core/utils/interleaveTypedArrays.js @@ -0,0 +1,49 @@ +import getBufferType from './getBufferType'; + +/* eslint-disable object-shorthand */ +const map = { Float32Array: Float32Array, Uint32Array: Uint32Array, Int32Array: Int32Array }; + +export default function interleaveTypedArrays(arrays, sizes) +{ + let outSize = 0; + let stride = 0; + const views = {}; + + for (let i = 0; i < arrays.length; i++) + { + stride += sizes[i]; + outSize += arrays[i].length; + } + + const buffer = new ArrayBuffer(outSize * 4); + + let out = null; + let littleOffset = 0; + + for (let i = 0; i < arrays.length; i++) + { + const size = sizes[i]; + const array = arrays[i]; + + const type = getBufferType(array); + + if (!views[type]) + { + views[type] = new map[type](buffer); + } + + out = views[type]; + + for (let j = 0; j < array.length; j++) + { + const indexStart = ((j / size | 0) * stride) + littleOffset; + const index = j % size; + + out[indexStart + index] = array[j]; + } + + littleOffset += size; + } + + return new Float32Array(buffer); +} diff --git a/src/core/const.js b/src/core/const.js index 5e3aff8..ef6f662 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -135,9 +135,9 @@ }; export const TYPES = { - FLOAT: 5126, - UNSIGNED_SHORT:5123, - UNSIGNED_BYTE: 5121 + FLOAT: 5126, + UNSIGNED_SHORT: 5123, + UNSIGNED_BYTE: 5121, }; /** diff --git a/src/core/utils/getBufferType.js b/src/core/utils/getBufferType.js new file mode 100644 index 0000000..a4a3b29 --- /dev/null +++ b/src/core/utils/getBufferType.js @@ -0,0 +1,26 @@ +export default function getBufferType(array) +{ + if (array.BYTES_PER_ELEMENT === 4) + { + if (array instanceof Float32Array) + { + return 'Float32Array'; + } + else if (array instanceof Uint32Array) + { + return 'Uint32Array'; + } + + return 'Int32Array'; + } + else if (array.BYTES_PER_ELEMENT === 2) + { + if (array instanceof Uint16Array) + { + return 'Uint16Array'; + } + } + + // TODO map out the rest of the array elements! + return null; +} diff --git a/src/core/utils/interleaveTypedArrays.js b/src/core/utils/interleaveTypedArrays.js new file mode 100644 index 0000000..8da791f --- /dev/null +++ b/src/core/utils/interleaveTypedArrays.js @@ -0,0 +1,49 @@ +import getBufferType from './getBufferType'; + +/* eslint-disable object-shorthand */ +const map = { Float32Array: Float32Array, Uint32Array: Uint32Array, Int32Array: Int32Array }; + +export default function interleaveTypedArrays(arrays, sizes) +{ + let outSize = 0; + let stride = 0; + const views = {}; + + for (let i = 0; i < arrays.length; i++) + { + stride += sizes[i]; + outSize += arrays[i].length; + } + + const buffer = new ArrayBuffer(outSize * 4); + + let out = null; + let littleOffset = 0; + + for (let i = 0; i < arrays.length; i++) + { + const size = sizes[i]; + const array = arrays[i]; + + const type = getBufferType(array); + + if (!views[type]) + { + views[type] = new map[type](buffer); + } + + out = views[type]; + + for (let j = 0; j < array.length; j++) + { + const indexStart = ((j / size | 0) * stride) + littleOffset; + const index = j % size; + + out[indexStart + index] = array[j]; + } + + littleOffset += size; + } + + return new Float32Array(buffer); +} diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 74f06a2..ffc4c18 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -47,6 +47,8 @@ this._tint = 0xFFFFFF; this.tint = 0xFFFFFF; + + this.blendMode = core.BLEND_MODES.NORMAL; } /** @@ -74,6 +76,23 @@ } /** + * 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 + */ + get blendMode() + { + return this.state.blendMode; + } + + set blendMode(value) // eslint-disable-line require-jsdoc + { + this.state.blendMode = value; + } + + /** * The texture that the mesh uses. * * @member {PIXI.Texture} diff --git a/src/core/const.js b/src/core/const.js index 5e3aff8..ef6f662 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -135,9 +135,9 @@ }; export const TYPES = { - FLOAT: 5126, - UNSIGNED_SHORT:5123, - UNSIGNED_BYTE: 5121 + FLOAT: 5126, + UNSIGNED_SHORT: 5123, + UNSIGNED_BYTE: 5121, }; /** diff --git a/src/core/utils/getBufferType.js b/src/core/utils/getBufferType.js new file mode 100644 index 0000000..a4a3b29 --- /dev/null +++ b/src/core/utils/getBufferType.js @@ -0,0 +1,26 @@ +export default function getBufferType(array) +{ + if (array.BYTES_PER_ELEMENT === 4) + { + if (array instanceof Float32Array) + { + return 'Float32Array'; + } + else if (array instanceof Uint32Array) + { + return 'Uint32Array'; + } + + return 'Int32Array'; + } + else if (array.BYTES_PER_ELEMENT === 2) + { + if (array instanceof Uint16Array) + { + return 'Uint16Array'; + } + } + + // TODO map out the rest of the array elements! + return null; +} diff --git a/src/core/utils/interleaveTypedArrays.js b/src/core/utils/interleaveTypedArrays.js new file mode 100644 index 0000000..8da791f --- /dev/null +++ b/src/core/utils/interleaveTypedArrays.js @@ -0,0 +1,49 @@ +import getBufferType from './getBufferType'; + +/* eslint-disable object-shorthand */ +const map = { Float32Array: Float32Array, Uint32Array: Uint32Array, Int32Array: Int32Array }; + +export default function interleaveTypedArrays(arrays, sizes) +{ + let outSize = 0; + let stride = 0; + const views = {}; + + for (let i = 0; i < arrays.length; i++) + { + stride += sizes[i]; + outSize += arrays[i].length; + } + + const buffer = new ArrayBuffer(outSize * 4); + + let out = null; + let littleOffset = 0; + + for (let i = 0; i < arrays.length; i++) + { + const size = sizes[i]; + const array = arrays[i]; + + const type = getBufferType(array); + + if (!views[type]) + { + views[type] = new map[type](buffer); + } + + out = views[type]; + + for (let j = 0; j < array.length; j++) + { + const indexStart = ((j / size | 0) * stride) + littleOffset; + const index = j % size; + + out[indexStart + index] = array[j]; + } + + littleOffset += size; + } + + return new Float32Array(buffer); +} diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 74f06a2..ffc4c18 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -47,6 +47,8 @@ this._tint = 0xFFFFFF; this.tint = 0xFFFFFF; + + this.blendMode = core.BLEND_MODES.NORMAL; } /** @@ -74,6 +76,23 @@ } /** + * 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 + */ + get blendMode() + { + return this.state.blendMode; + } + + set blendMode(value) // eslint-disable-line require-jsdoc + { + this.state.blendMode = value; + } + + /** * The texture that the mesh uses. * * @member {PIXI.Texture} diff --git a/src/mesh/RawMesh.js b/src/mesh/RawMesh.js index f4d59b9..cda86c5 100644 --- a/src/mesh/RawMesh.js +++ b/src/mesh/RawMesh.js @@ -53,16 +53,6 @@ this.state = state || new core.State(); /** - * 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; - //this.state.blendMode = this.blendMode; - - /** * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.RawMesh.DRAW_MODES} consts * * @member {number} @@ -141,7 +131,7 @@ _calculateBounds() { // The position property could be set manually? - if (this.geometry.style.attributes.aVertexPosition) + if (this.geometry.attributes.aVertexPosition) { const vertices = this.geometry.getAttribute('aVertexPosition').data; diff --git a/src/core/const.js b/src/core/const.js index 5e3aff8..ef6f662 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -135,9 +135,9 @@ }; export const TYPES = { - FLOAT: 5126, - UNSIGNED_SHORT:5123, - UNSIGNED_BYTE: 5121 + FLOAT: 5126, + UNSIGNED_SHORT: 5123, + UNSIGNED_BYTE: 5121, }; /** diff --git a/src/core/utils/getBufferType.js b/src/core/utils/getBufferType.js new file mode 100644 index 0000000..a4a3b29 --- /dev/null +++ b/src/core/utils/getBufferType.js @@ -0,0 +1,26 @@ +export default function getBufferType(array) +{ + if (array.BYTES_PER_ELEMENT === 4) + { + if (array instanceof Float32Array) + { + return 'Float32Array'; + } + else if (array instanceof Uint32Array) + { + return 'Uint32Array'; + } + + return 'Int32Array'; + } + else if (array.BYTES_PER_ELEMENT === 2) + { + if (array instanceof Uint16Array) + { + return 'Uint16Array'; + } + } + + // TODO map out the rest of the array elements! + return null; +} diff --git a/src/core/utils/interleaveTypedArrays.js b/src/core/utils/interleaveTypedArrays.js new file mode 100644 index 0000000..8da791f --- /dev/null +++ b/src/core/utils/interleaveTypedArrays.js @@ -0,0 +1,49 @@ +import getBufferType from './getBufferType'; + +/* eslint-disable object-shorthand */ +const map = { Float32Array: Float32Array, Uint32Array: Uint32Array, Int32Array: Int32Array }; + +export default function interleaveTypedArrays(arrays, sizes) +{ + let outSize = 0; + let stride = 0; + const views = {}; + + for (let i = 0; i < arrays.length; i++) + { + stride += sizes[i]; + outSize += arrays[i].length; + } + + const buffer = new ArrayBuffer(outSize * 4); + + let out = null; + let littleOffset = 0; + + for (let i = 0; i < arrays.length; i++) + { + const size = sizes[i]; + const array = arrays[i]; + + const type = getBufferType(array); + + if (!views[type]) + { + views[type] = new map[type](buffer); + } + + out = views[type]; + + for (let j = 0; j < array.length; j++) + { + const indexStart = ((j / size | 0) * stride) + littleOffset; + const index = j % size; + + out[indexStart + index] = array[j]; + } + + littleOffset += size; + } + + return new Float32Array(buffer); +} diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 74f06a2..ffc4c18 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -47,6 +47,8 @@ this._tint = 0xFFFFFF; this.tint = 0xFFFFFF; + + this.blendMode = core.BLEND_MODES.NORMAL; } /** @@ -74,6 +76,23 @@ } /** + * 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 + */ + get blendMode() + { + return this.state.blendMode; + } + + set blendMode(value) // eslint-disable-line require-jsdoc + { + this.state.blendMode = value; + } + + /** * The texture that the mesh uses. * * @member {PIXI.Texture} diff --git a/src/mesh/RawMesh.js b/src/mesh/RawMesh.js index f4d59b9..cda86c5 100644 --- a/src/mesh/RawMesh.js +++ b/src/mesh/RawMesh.js @@ -53,16 +53,6 @@ this.state = state || new core.State(); /** - * 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; - //this.state.blendMode = this.blendMode; - - /** * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.RawMesh.DRAW_MODES} consts * * @member {number} @@ -141,7 +131,7 @@ _calculateBounds() { // The position property could be set manually? - if (this.geometry.style.attributes.aVertexPosition) + if (this.geometry.attributes.aVertexPosition) { const vertices = this.geometry.getAttribute('aVertexPosition').data; diff --git a/src/mesh/geometry/Attribute.js b/src/mesh/geometry/Attribute.js index f414439..633f9bb 100644 --- a/src/mesh/geometry/Attribute.js +++ b/src/mesh/geometry/Attribute.js @@ -12,14 +12,16 @@ { /** * @param {string} buffer the id of the buffer that this attribute will look for - * @param {Number} [size=2] the size of the attribute. If you hava 2 floats per vertex (eg position x and y) this would be 2 + * @param {Number} [size=0] the size of the attribute. If you hava 2 floats per vertex (eg position x and y) this would be 2. + * @param {Boolean} [normalised=false] should the data be normalised. + * @param {Number} [type=PIXI.TYPES.FLOAT] what type of numbe is the attribute. Check {PIXI.TYPES} to see the ones available * @param {Number} [stride=0] How far apart (in floats) the start of each value is. (used for interleaving data) * @param {Number} [start=0] How far into the array to start reading values (used for interleaving data) - * @param {Boolean} [normalised=false] should the data be normalised. */ - constructor(buffer, normalised = false, type, stride, start) + constructor(buffer, size, normalised = false, type = 5126, stride, start) { this.buffer = buffer; + this.size = size; this.normalized = normalised; this.type = type; this.stride = stride; diff --git a/src/core/const.js b/src/core/const.js index 5e3aff8..ef6f662 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -135,9 +135,9 @@ }; export const TYPES = { - FLOAT: 5126, - UNSIGNED_SHORT:5123, - UNSIGNED_BYTE: 5121 + FLOAT: 5126, + UNSIGNED_SHORT: 5123, + UNSIGNED_BYTE: 5121, }; /** diff --git a/src/core/utils/getBufferType.js b/src/core/utils/getBufferType.js new file mode 100644 index 0000000..a4a3b29 --- /dev/null +++ b/src/core/utils/getBufferType.js @@ -0,0 +1,26 @@ +export default function getBufferType(array) +{ + if (array.BYTES_PER_ELEMENT === 4) + { + if (array instanceof Float32Array) + { + return 'Float32Array'; + } + else if (array instanceof Uint32Array) + { + return 'Uint32Array'; + } + + return 'Int32Array'; + } + else if (array.BYTES_PER_ELEMENT === 2) + { + if (array instanceof Uint16Array) + { + return 'Uint16Array'; + } + } + + // TODO map out the rest of the array elements! + return null; +} diff --git a/src/core/utils/interleaveTypedArrays.js b/src/core/utils/interleaveTypedArrays.js new file mode 100644 index 0000000..8da791f --- /dev/null +++ b/src/core/utils/interleaveTypedArrays.js @@ -0,0 +1,49 @@ +import getBufferType from './getBufferType'; + +/* eslint-disable object-shorthand */ +const map = { Float32Array: Float32Array, Uint32Array: Uint32Array, Int32Array: Int32Array }; + +export default function interleaveTypedArrays(arrays, sizes) +{ + let outSize = 0; + let stride = 0; + const views = {}; + + for (let i = 0; i < arrays.length; i++) + { + stride += sizes[i]; + outSize += arrays[i].length; + } + + const buffer = new ArrayBuffer(outSize * 4); + + let out = null; + let littleOffset = 0; + + for (let i = 0; i < arrays.length; i++) + { + const size = sizes[i]; + const array = arrays[i]; + + const type = getBufferType(array); + + if (!views[type]) + { + views[type] = new map[type](buffer); + } + + out = views[type]; + + for (let j = 0; j < array.length; j++) + { + const indexStart = ((j / size | 0) * stride) + littleOffset; + const index = j % size; + + out[indexStart + index] = array[j]; + } + + littleOffset += size; + } + + return new Float32Array(buffer); +} diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 74f06a2..ffc4c18 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -47,6 +47,8 @@ this._tint = 0xFFFFFF; this.tint = 0xFFFFFF; + + this.blendMode = core.BLEND_MODES.NORMAL; } /** @@ -74,6 +76,23 @@ } /** + * 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 + */ + get blendMode() + { + return this.state.blendMode; + } + + set blendMode(value) // eslint-disable-line require-jsdoc + { + this.state.blendMode = value; + } + + /** * The texture that the mesh uses. * * @member {PIXI.Texture} diff --git a/src/mesh/RawMesh.js b/src/mesh/RawMesh.js index f4d59b9..cda86c5 100644 --- a/src/mesh/RawMesh.js +++ b/src/mesh/RawMesh.js @@ -53,16 +53,6 @@ this.state = state || new core.State(); /** - * 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; - //this.state.blendMode = this.blendMode; - - /** * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.RawMesh.DRAW_MODES} consts * * @member {number} @@ -141,7 +131,7 @@ _calculateBounds() { // The position property could be set manually? - if (this.geometry.style.attributes.aVertexPosition) + if (this.geometry.attributes.aVertexPosition) { const vertices = this.geometry.getAttribute('aVertexPosition').data; diff --git a/src/mesh/geometry/Attribute.js b/src/mesh/geometry/Attribute.js index f414439..633f9bb 100644 --- a/src/mesh/geometry/Attribute.js +++ b/src/mesh/geometry/Attribute.js @@ -12,14 +12,16 @@ { /** * @param {string} buffer the id of the buffer that this attribute will look for - * @param {Number} [size=2] the size of the attribute. If you hava 2 floats per vertex (eg position x and y) this would be 2 + * @param {Number} [size=0] the size of the attribute. If you hava 2 floats per vertex (eg position x and y) this would be 2. + * @param {Boolean} [normalised=false] should the data be normalised. + * @param {Number} [type=PIXI.TYPES.FLOAT] what type of numbe is the attribute. Check {PIXI.TYPES} to see the ones available * @param {Number} [stride=0] How far apart (in floats) the start of each value is. (used for interleaving data) * @param {Number} [start=0] How far into the array to start reading values (used for interleaving data) - * @param {Boolean} [normalised=false] should the data be normalised. */ - constructor(buffer, normalised = false, type, stride, start) + constructor(buffer, size, normalised = false, type = 5126, stride, start) { this.buffer = buffer; + this.size = size; this.normalized = normalised; this.type = type; this.stride = stride; diff --git a/src/mesh/geometry/Buffer.js b/src/mesh/geometry/Buffer.js index 5492687..1fc8ef6 100644 --- a/src/mesh/geometry/Buffer.js +++ b/src/mesh/geometry/Buffer.js @@ -31,6 +31,8 @@ this._updateID = 0; + this.index = false; + this.id = UID++; } diff --git a/src/core/const.js b/src/core/const.js index 5e3aff8..ef6f662 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -135,9 +135,9 @@ }; export const TYPES = { - FLOAT: 5126, - UNSIGNED_SHORT:5123, - UNSIGNED_BYTE: 5121 + FLOAT: 5126, + UNSIGNED_SHORT: 5123, + UNSIGNED_BYTE: 5121, }; /** diff --git a/src/core/utils/getBufferType.js b/src/core/utils/getBufferType.js new file mode 100644 index 0000000..a4a3b29 --- /dev/null +++ b/src/core/utils/getBufferType.js @@ -0,0 +1,26 @@ +export default function getBufferType(array) +{ + if (array.BYTES_PER_ELEMENT === 4) + { + if (array instanceof Float32Array) + { + return 'Float32Array'; + } + else if (array instanceof Uint32Array) + { + return 'Uint32Array'; + } + + return 'Int32Array'; + } + else if (array.BYTES_PER_ELEMENT === 2) + { + if (array instanceof Uint16Array) + { + return 'Uint16Array'; + } + } + + // TODO map out the rest of the array elements! + return null; +} diff --git a/src/core/utils/interleaveTypedArrays.js b/src/core/utils/interleaveTypedArrays.js new file mode 100644 index 0000000..8da791f --- /dev/null +++ b/src/core/utils/interleaveTypedArrays.js @@ -0,0 +1,49 @@ +import getBufferType from './getBufferType'; + +/* eslint-disable object-shorthand */ +const map = { Float32Array: Float32Array, Uint32Array: Uint32Array, Int32Array: Int32Array }; + +export default function interleaveTypedArrays(arrays, sizes) +{ + let outSize = 0; + let stride = 0; + const views = {}; + + for (let i = 0; i < arrays.length; i++) + { + stride += sizes[i]; + outSize += arrays[i].length; + } + + const buffer = new ArrayBuffer(outSize * 4); + + let out = null; + let littleOffset = 0; + + for (let i = 0; i < arrays.length; i++) + { + const size = sizes[i]; + const array = arrays[i]; + + const type = getBufferType(array); + + if (!views[type]) + { + views[type] = new map[type](buffer); + } + + out = views[type]; + + for (let j = 0; j < array.length; j++) + { + const indexStart = ((j / size | 0) * stride) + littleOffset; + const index = j % size; + + out[indexStart + index] = array[j]; + } + + littleOffset += size; + } + + return new Float32Array(buffer); +} diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 74f06a2..ffc4c18 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -47,6 +47,8 @@ this._tint = 0xFFFFFF; this.tint = 0xFFFFFF; + + this.blendMode = core.BLEND_MODES.NORMAL; } /** @@ -74,6 +76,23 @@ } /** + * 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 + */ + get blendMode() + { + return this.state.blendMode; + } + + set blendMode(value) // eslint-disable-line require-jsdoc + { + this.state.blendMode = value; + } + + /** * The texture that the mesh uses. * * @member {PIXI.Texture} diff --git a/src/mesh/RawMesh.js b/src/mesh/RawMesh.js index f4d59b9..cda86c5 100644 --- a/src/mesh/RawMesh.js +++ b/src/mesh/RawMesh.js @@ -53,16 +53,6 @@ this.state = state || new core.State(); /** - * 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; - //this.state.blendMode = this.blendMode; - - /** * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.RawMesh.DRAW_MODES} consts * * @member {number} @@ -141,7 +131,7 @@ _calculateBounds() { // The position property could be set manually? - if (this.geometry.style.attributes.aVertexPosition) + if (this.geometry.attributes.aVertexPosition) { const vertices = this.geometry.getAttribute('aVertexPosition').data; diff --git a/src/mesh/geometry/Attribute.js b/src/mesh/geometry/Attribute.js index f414439..633f9bb 100644 --- a/src/mesh/geometry/Attribute.js +++ b/src/mesh/geometry/Attribute.js @@ -12,14 +12,16 @@ { /** * @param {string} buffer the id of the buffer that this attribute will look for - * @param {Number} [size=2] the size of the attribute. If you hava 2 floats per vertex (eg position x and y) this would be 2 + * @param {Number} [size=0] the size of the attribute. If you hava 2 floats per vertex (eg position x and y) this would be 2. + * @param {Boolean} [normalised=false] should the data be normalised. + * @param {Number} [type=PIXI.TYPES.FLOAT] what type of numbe is the attribute. Check {PIXI.TYPES} to see the ones available * @param {Number} [stride=0] How far apart (in floats) the start of each value is. (used for interleaving data) * @param {Number} [start=0] How far into the array to start reading values (used for interleaving data) - * @param {Boolean} [normalised=false] should the data be normalised. */ - constructor(buffer, normalised = false, type, stride, start) + constructor(buffer, size, normalised = false, type = 5126, stride, start) { this.buffer = buffer; + this.size = size; this.normalized = normalised; this.type = type; this.stride = stride; diff --git a/src/mesh/geometry/Buffer.js b/src/mesh/geometry/Buffer.js index 5492687..1fc8ef6 100644 --- a/src/mesh/geometry/Buffer.js +++ b/src/mesh/geometry/Buffer.js @@ -31,6 +31,8 @@ this._updateID = 0; + this.index = false; + this.id = UID++; } diff --git a/src/mesh/geometry/Geometry.js b/src/mesh/geometry/Geometry.js index 3458ac2..b3a3fc2 100644 --- a/src/mesh/geometry/Geometry.js +++ b/src/mesh/geometry/Geometry.js @@ -1,5 +1,17 @@ import Attribute from './Attribute'; import Buffer from './Buffer'; +import interleaveTypedArrays from '../../core/utils/interleaveTypedArrays'; +import getBufferType from '../../core/utils/getBufferType'; + +const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; + +/* eslint-disable object-shorthand */ +const map = { + Float32Array: Float32Array, + Uint32Array: Uint32Array, + Int32Array: Int32Array, + Uint16Array: Uint16Array, +}; /* eslint-disable max-len */ @@ -26,7 +38,7 @@ export default class Geometry { /** - * @param {array} data this consists of buffers. optional. + * @param {array} buffers an array of buffers. optional. * @param {object} attributes of the geometry, optional structure of the attributes layout */ constructor(buffers, attributes) @@ -52,14 +64,15 @@ * * @param {String} id - the name of the attribute (matching up to a shader) * @param {PIXI.mesh.Buffer} [buffer] the buffer that holds the data of the attribute . You can also provide an Array and a buffer will be created from it. - * @param {Number} [size=2] the size of the attribute. If you hava 2 floats per vertex (eg position x and y) this would be 2 + * @param {Number} [size=0] the size of the attribute. If you hava 2 floats per vertex (eg position x and y) this would be 2 + * @param {Boolean} [normalised=false] should the data be normalised. + * @param {Number} [type=PIXI.TYPES.FLOAT] what type of numbe is the attribute. Check {PIXI.TYPES} to see the ones available * @param {Number} [stride=0] How far apart (in floats) the start of each value is. (used for interleaving data) * @param {Number} [start=0] How far into the array to start reading values (used for interleaving data) - * @param {Boolean} [normalised=false] should the data be normalised. * * @return {PIXI.mesh.Geometry} returns self, useful for chaining. */ - addAttribute(id, buffer, normalised = false, type, stride, start) + addAttribute(id, buffer, size, normalised = false, type, stride, start) { // check if this is a buffer! if (!buffer.data) @@ -73,13 +86,13 @@ buffer = new Buffer(buffer); } - var ids = id.split('|'); + const ids = id.split('|'); - if(ids.length > 1) + if (ids.length > 1) { - for (var i = 0; i < ids.length; i++) + for (let i = 0; i < ids.length; i++) { - this.addAttribute(ids[i], buffer, normalised, type) + this.addAttribute(ids[i], buffer, size, normalised, type); } return this; @@ -90,10 +103,10 @@ if (bufferIndex === -1) { this.buffers.push(buffer); - bufferIndex = this.buffers.length-1 + bufferIndex = this.buffers.length - 1; } - this.attributes[id] = new Attribute(bufferIndex, normalised, type, stride, start); + this.attributes[id] = new Attribute(bufferIndex, size, normalised, type, stride, start); return this; } @@ -152,6 +165,56 @@ } /** + * this function modifies the structure so that all current attributes become interleaved into a single buffer + * This can be useful if your model remains static as it offers a little performance boost + * + * @return {PIXI.mesh.Geometry} returns self, useful for chaining. + */ + interleave() + { + // a simple check to see if buffers are already interleaved.. + if (this.buffers.length === 1 || (this.buffers.length === 2 && this.indexBuffer)) return this; + + // assume already that no buffers are interleaved + const arrays = []; + const sizes = []; + const interleavedBuffer = new Buffer(); + let i; + + for (i in this.attributes) + { + const attribute = this.attributes[i]; + + const buffer = this.buffers[attribute.buffer]; + + arrays.push(buffer.data); + + sizes.push((attribute.size * byteSizeMap[attribute.type]) / 4); + + attribute.buffer = 0; + } + + interleavedBuffer.data = interleaveTypedArrays(arrays, sizes); + + for (i = 0; i < this.buffers.length; i++) + { + if (this.buffers[i] !== this.indexBuffer) + { + this.buffers[i].destroy(); + } + } + + this.buffers = [interleavedBuffer]; + + if (this.indexBuffer) + { + this.buffers.push(this.indexBuffer); + } + + return this; + } + + /** * Destroys the geometry. */ destroy() @@ -173,4 +236,108 @@ this.attributes = null; } + + /** + * merges an array of geometries into a new single one + * geometry attribute styles must match for this operation to work + * + * @param {array|PIXI.mesh.Geometry} geometries array of geometries to merge + * @returns {PIXI.mesh.Geometry} shiney new geometry + */ + static merge(geometries) + { + // todo add a geometry check! + // also a size check.. cant be too big!] + + const geometryOut = new Geometry(); + + const arrays = []; + const sizes = []; + const offsets = []; + + let geometry; + + // pass one.. get sizes.. + for (let i = 0; i < geometries.length; i++) + { + geometry = geometries[i]; + + for (let j = 0; j < geometry.buffers.length; j++) + { + sizes[j] = sizes[j] || 0; + sizes[j] += geometry.buffers[j].data.length; + offsets[j] = 0; + } + } + + // build the correct size arrays.. + for (let i = 0; i < geometry.buffers.length; i++) + { + // TODO types! + arrays[i] = new map[getBufferType(geometry.buffers[i].data)](sizes[i]); + geometryOut.buffers[i] = new Buffer(arrays[i]); + } + + // pass to set data.. + for (let i = 0; i < geometries.length; i++) + { + geometry = geometries[i]; + + for (let j = 0; j < geometry.buffers.length; j++) + { + arrays[j].set(geometry.buffers[j].data, offsets[j]); + offsets[j] += geometry.buffers[j].data.length; + } + } + + geometryOut.attributes = geometry.attributes; + + if (geometry.indexBuffer) + { + geometryOut.indexBuffer = geometryOut.buffers[geometry.buffers.indexOf(geometry.indexBuffer)]; + geometryOut.indexBuffer.index = true; + + let offset = 0; + let stride = 0; + let offset2 = 0; + let bufferIndexToCount = 0; + + // get a buffer + for (let i = 0; i < geometry.buffers.length; i++) + { + if (geometry.buffers[i] !== geometry.indexBuffer) + { + bufferIndexToCount = i; + break; + } + } + + // figure out the stride of one buffer.. + for (const i in geometry.attributes) + { + const attribute = geometry.attributes[i]; + + if ((attribute.buffer | 0) === bufferIndexToCount) + { + stride += ((attribute.size * byteSizeMap[attribute.type]) / 4); + } + } + + // time to off set all indexes.. + for (let i = 0; i < geometries.length; i++) + { + const indexBufferData = geometries[i].indexBuffer.data; + + for (let j = 0; j < indexBufferData.length; j++) + { + geometryOut.indexBuffer.data[j + offset2] += offset; + } + + offset += geometry.buffers[bufferIndexToCount].data.length / (stride); + offset2 += indexBufferData.length; + } + } + + return geometryOut; + } } diff --git a/src/core/const.js b/src/core/const.js index 5e3aff8..ef6f662 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -135,9 +135,9 @@ }; export const TYPES = { - FLOAT: 5126, - UNSIGNED_SHORT:5123, - UNSIGNED_BYTE: 5121 + FLOAT: 5126, + UNSIGNED_SHORT: 5123, + UNSIGNED_BYTE: 5121, }; /** diff --git a/src/core/utils/getBufferType.js b/src/core/utils/getBufferType.js new file mode 100644 index 0000000..a4a3b29 --- /dev/null +++ b/src/core/utils/getBufferType.js @@ -0,0 +1,26 @@ +export default function getBufferType(array) +{ + if (array.BYTES_PER_ELEMENT === 4) + { + if (array instanceof Float32Array) + { + return 'Float32Array'; + } + else if (array instanceof Uint32Array) + { + return 'Uint32Array'; + } + + return 'Int32Array'; + } + else if (array.BYTES_PER_ELEMENT === 2) + { + if (array instanceof Uint16Array) + { + return 'Uint16Array'; + } + } + + // TODO map out the rest of the array elements! + return null; +} diff --git a/src/core/utils/interleaveTypedArrays.js b/src/core/utils/interleaveTypedArrays.js new file mode 100644 index 0000000..8da791f --- /dev/null +++ b/src/core/utils/interleaveTypedArrays.js @@ -0,0 +1,49 @@ +import getBufferType from './getBufferType'; + +/* eslint-disable object-shorthand */ +const map = { Float32Array: Float32Array, Uint32Array: Uint32Array, Int32Array: Int32Array }; + +export default function interleaveTypedArrays(arrays, sizes) +{ + let outSize = 0; + let stride = 0; + const views = {}; + + for (let i = 0; i < arrays.length; i++) + { + stride += sizes[i]; + outSize += arrays[i].length; + } + + const buffer = new ArrayBuffer(outSize * 4); + + let out = null; + let littleOffset = 0; + + for (let i = 0; i < arrays.length; i++) + { + const size = sizes[i]; + const array = arrays[i]; + + const type = getBufferType(array); + + if (!views[type]) + { + views[type] = new map[type](buffer); + } + + out = views[type]; + + for (let j = 0; j < array.length; j++) + { + const indexStart = ((j / size | 0) * stride) + littleOffset; + const index = j % size; + + out[indexStart + index] = array[j]; + } + + littleOffset += size; + } + + return new Float32Array(buffer); +} diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 74f06a2..ffc4c18 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -47,6 +47,8 @@ this._tint = 0xFFFFFF; this.tint = 0xFFFFFF; + + this.blendMode = core.BLEND_MODES.NORMAL; } /** @@ -74,6 +76,23 @@ } /** + * 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 + */ + get blendMode() + { + return this.state.blendMode; + } + + set blendMode(value) // eslint-disable-line require-jsdoc + { + this.state.blendMode = value; + } + + /** * The texture that the mesh uses. * * @member {PIXI.Texture} diff --git a/src/mesh/RawMesh.js b/src/mesh/RawMesh.js index f4d59b9..cda86c5 100644 --- a/src/mesh/RawMesh.js +++ b/src/mesh/RawMesh.js @@ -53,16 +53,6 @@ this.state = state || new core.State(); /** - * 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; - //this.state.blendMode = this.blendMode; - - /** * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.RawMesh.DRAW_MODES} consts * * @member {number} @@ -141,7 +131,7 @@ _calculateBounds() { // The position property could be set manually? - if (this.geometry.style.attributes.aVertexPosition) + if (this.geometry.attributes.aVertexPosition) { const vertices = this.geometry.getAttribute('aVertexPosition').data; diff --git a/src/mesh/geometry/Attribute.js b/src/mesh/geometry/Attribute.js index f414439..633f9bb 100644 --- a/src/mesh/geometry/Attribute.js +++ b/src/mesh/geometry/Attribute.js @@ -12,14 +12,16 @@ { /** * @param {string} buffer the id of the buffer that this attribute will look for - * @param {Number} [size=2] the size of the attribute. If you hava 2 floats per vertex (eg position x and y) this would be 2 + * @param {Number} [size=0] the size of the attribute. If you hava 2 floats per vertex (eg position x and y) this would be 2. + * @param {Boolean} [normalised=false] should the data be normalised. + * @param {Number} [type=PIXI.TYPES.FLOAT] what type of numbe is the attribute. Check {PIXI.TYPES} to see the ones available * @param {Number} [stride=0] How far apart (in floats) the start of each value is. (used for interleaving data) * @param {Number} [start=0] How far into the array to start reading values (used for interleaving data) - * @param {Boolean} [normalised=false] should the data be normalised. */ - constructor(buffer, normalised = false, type, stride, start) + constructor(buffer, size, normalised = false, type = 5126, stride, start) { this.buffer = buffer; + this.size = size; this.normalized = normalised; this.type = type; this.stride = stride; diff --git a/src/mesh/geometry/Buffer.js b/src/mesh/geometry/Buffer.js index 5492687..1fc8ef6 100644 --- a/src/mesh/geometry/Buffer.js +++ b/src/mesh/geometry/Buffer.js @@ -31,6 +31,8 @@ this._updateID = 0; + this.index = false; + this.id = UID++; } diff --git a/src/mesh/geometry/Geometry.js b/src/mesh/geometry/Geometry.js index 3458ac2..b3a3fc2 100644 --- a/src/mesh/geometry/Geometry.js +++ b/src/mesh/geometry/Geometry.js @@ -1,5 +1,17 @@ import Attribute from './Attribute'; import Buffer from './Buffer'; +import interleaveTypedArrays from '../../core/utils/interleaveTypedArrays'; +import getBufferType from '../../core/utils/getBufferType'; + +const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; + +/* eslint-disable object-shorthand */ +const map = { + Float32Array: Float32Array, + Uint32Array: Uint32Array, + Int32Array: Int32Array, + Uint16Array: Uint16Array, +}; /* eslint-disable max-len */ @@ -26,7 +38,7 @@ export default class Geometry { /** - * @param {array} data this consists of buffers. optional. + * @param {array} buffers an array of buffers. optional. * @param {object} attributes of the geometry, optional structure of the attributes layout */ constructor(buffers, attributes) @@ -52,14 +64,15 @@ * * @param {String} id - the name of the attribute (matching up to a shader) * @param {PIXI.mesh.Buffer} [buffer] the buffer that holds the data of the attribute . You can also provide an Array and a buffer will be created from it. - * @param {Number} [size=2] the size of the attribute. If you hava 2 floats per vertex (eg position x and y) this would be 2 + * @param {Number} [size=0] the size of the attribute. If you hava 2 floats per vertex (eg position x and y) this would be 2 + * @param {Boolean} [normalised=false] should the data be normalised. + * @param {Number} [type=PIXI.TYPES.FLOAT] what type of numbe is the attribute. Check {PIXI.TYPES} to see the ones available * @param {Number} [stride=0] How far apart (in floats) the start of each value is. (used for interleaving data) * @param {Number} [start=0] How far into the array to start reading values (used for interleaving data) - * @param {Boolean} [normalised=false] should the data be normalised. * * @return {PIXI.mesh.Geometry} returns self, useful for chaining. */ - addAttribute(id, buffer, normalised = false, type, stride, start) + addAttribute(id, buffer, size, normalised = false, type, stride, start) { // check if this is a buffer! if (!buffer.data) @@ -73,13 +86,13 @@ buffer = new Buffer(buffer); } - var ids = id.split('|'); + const ids = id.split('|'); - if(ids.length > 1) + if (ids.length > 1) { - for (var i = 0; i < ids.length; i++) + for (let i = 0; i < ids.length; i++) { - this.addAttribute(ids[i], buffer, normalised, type) + this.addAttribute(ids[i], buffer, size, normalised, type); } return this; @@ -90,10 +103,10 @@ if (bufferIndex === -1) { this.buffers.push(buffer); - bufferIndex = this.buffers.length-1 + bufferIndex = this.buffers.length - 1; } - this.attributes[id] = new Attribute(bufferIndex, normalised, type, stride, start); + this.attributes[id] = new Attribute(bufferIndex, size, normalised, type, stride, start); return this; } @@ -152,6 +165,56 @@ } /** + * this function modifies the structure so that all current attributes become interleaved into a single buffer + * This can be useful if your model remains static as it offers a little performance boost + * + * @return {PIXI.mesh.Geometry} returns self, useful for chaining. + */ + interleave() + { + // a simple check to see if buffers are already interleaved.. + if (this.buffers.length === 1 || (this.buffers.length === 2 && this.indexBuffer)) return this; + + // assume already that no buffers are interleaved + const arrays = []; + const sizes = []; + const interleavedBuffer = new Buffer(); + let i; + + for (i in this.attributes) + { + const attribute = this.attributes[i]; + + const buffer = this.buffers[attribute.buffer]; + + arrays.push(buffer.data); + + sizes.push((attribute.size * byteSizeMap[attribute.type]) / 4); + + attribute.buffer = 0; + } + + interleavedBuffer.data = interleaveTypedArrays(arrays, sizes); + + for (i = 0; i < this.buffers.length; i++) + { + if (this.buffers[i] !== this.indexBuffer) + { + this.buffers[i].destroy(); + } + } + + this.buffers = [interleavedBuffer]; + + if (this.indexBuffer) + { + this.buffers.push(this.indexBuffer); + } + + return this; + } + + /** * Destroys the geometry. */ destroy() @@ -173,4 +236,108 @@ this.attributes = null; } + + /** + * merges an array of geometries into a new single one + * geometry attribute styles must match for this operation to work + * + * @param {array|PIXI.mesh.Geometry} geometries array of geometries to merge + * @returns {PIXI.mesh.Geometry} shiney new geometry + */ + static merge(geometries) + { + // todo add a geometry check! + // also a size check.. cant be too big!] + + const geometryOut = new Geometry(); + + const arrays = []; + const sizes = []; + const offsets = []; + + let geometry; + + // pass one.. get sizes.. + for (let i = 0; i < geometries.length; i++) + { + geometry = geometries[i]; + + for (let j = 0; j < geometry.buffers.length; j++) + { + sizes[j] = sizes[j] || 0; + sizes[j] += geometry.buffers[j].data.length; + offsets[j] = 0; + } + } + + // build the correct size arrays.. + for (let i = 0; i < geometry.buffers.length; i++) + { + // TODO types! + arrays[i] = new map[getBufferType(geometry.buffers[i].data)](sizes[i]); + geometryOut.buffers[i] = new Buffer(arrays[i]); + } + + // pass to set data.. + for (let i = 0; i < geometries.length; i++) + { + geometry = geometries[i]; + + for (let j = 0; j < geometry.buffers.length; j++) + { + arrays[j].set(geometry.buffers[j].data, offsets[j]); + offsets[j] += geometry.buffers[j].data.length; + } + } + + geometryOut.attributes = geometry.attributes; + + if (geometry.indexBuffer) + { + geometryOut.indexBuffer = geometryOut.buffers[geometry.buffers.indexOf(geometry.indexBuffer)]; + geometryOut.indexBuffer.index = true; + + let offset = 0; + let stride = 0; + let offset2 = 0; + let bufferIndexToCount = 0; + + // get a buffer + for (let i = 0; i < geometry.buffers.length; i++) + { + if (geometry.buffers[i] !== geometry.indexBuffer) + { + bufferIndexToCount = i; + break; + } + } + + // figure out the stride of one buffer.. + for (const i in geometry.attributes) + { + const attribute = geometry.attributes[i]; + + if ((attribute.buffer | 0) === bufferIndexToCount) + { + stride += ((attribute.size * byteSizeMap[attribute.type]) / 4); + } + } + + // time to off set all indexes.. + for (let i = 0; i < geometries.length; i++) + { + const indexBufferData = geometries[i].indexBuffer.data; + + for (let j = 0; j < indexBufferData.length; j++) + { + geometryOut.indexBuffer.data[j + offset2] += offset; + } + + offset += geometry.buffers[bufferIndexToCount].data.length / (stride); + offset2 += indexBufferData.length; + } + } + + return geometryOut; + } } diff --git a/src/mesh/index.js b/src/mesh/index.js index 1ab5333..a402b70 100644 --- a/src/mesh/index.js +++ b/src/mesh/index.js @@ -6,8 +6,6 @@ export { default as MeshRenderer } from './webgl/MeshRenderer'; export { default as CanvasMeshRenderer } from './canvas/CanvasMeshRenderer'; 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'; diff --git a/src/core/const.js b/src/core/const.js index 5e3aff8..ef6f662 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -135,9 +135,9 @@ }; export const TYPES = { - FLOAT: 5126, - UNSIGNED_SHORT:5123, - UNSIGNED_BYTE: 5121 + FLOAT: 5126, + UNSIGNED_SHORT: 5123, + UNSIGNED_BYTE: 5121, }; /** diff --git a/src/core/utils/getBufferType.js b/src/core/utils/getBufferType.js new file mode 100644 index 0000000..a4a3b29 --- /dev/null +++ b/src/core/utils/getBufferType.js @@ -0,0 +1,26 @@ +export default function getBufferType(array) +{ + if (array.BYTES_PER_ELEMENT === 4) + { + if (array instanceof Float32Array) + { + return 'Float32Array'; + } + else if (array instanceof Uint32Array) + { + return 'Uint32Array'; + } + + return 'Int32Array'; + } + else if (array.BYTES_PER_ELEMENT === 2) + { + if (array instanceof Uint16Array) + { + return 'Uint16Array'; + } + } + + // TODO map out the rest of the array elements! + return null; +} diff --git a/src/core/utils/interleaveTypedArrays.js b/src/core/utils/interleaveTypedArrays.js new file mode 100644 index 0000000..8da791f --- /dev/null +++ b/src/core/utils/interleaveTypedArrays.js @@ -0,0 +1,49 @@ +import getBufferType from './getBufferType'; + +/* eslint-disable object-shorthand */ +const map = { Float32Array: Float32Array, Uint32Array: Uint32Array, Int32Array: Int32Array }; + +export default function interleaveTypedArrays(arrays, sizes) +{ + let outSize = 0; + let stride = 0; + const views = {}; + + for (let i = 0; i < arrays.length; i++) + { + stride += sizes[i]; + outSize += arrays[i].length; + } + + const buffer = new ArrayBuffer(outSize * 4); + + let out = null; + let littleOffset = 0; + + for (let i = 0; i < arrays.length; i++) + { + const size = sizes[i]; + const array = arrays[i]; + + const type = getBufferType(array); + + if (!views[type]) + { + views[type] = new map[type](buffer); + } + + out = views[type]; + + for (let j = 0; j < array.length; j++) + { + const indexStart = ((j / size | 0) * stride) + littleOffset; + const index = j % size; + + out[indexStart + index] = array[j]; + } + + littleOffset += size; + } + + return new Float32Array(buffer); +} diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 74f06a2..ffc4c18 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -47,6 +47,8 @@ this._tint = 0xFFFFFF; this.tint = 0xFFFFFF; + + this.blendMode = core.BLEND_MODES.NORMAL; } /** @@ -74,6 +76,23 @@ } /** + * 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 + */ + get blendMode() + { + return this.state.blendMode; + } + + set blendMode(value) // eslint-disable-line require-jsdoc + { + this.state.blendMode = value; + } + + /** * The texture that the mesh uses. * * @member {PIXI.Texture} diff --git a/src/mesh/RawMesh.js b/src/mesh/RawMesh.js index f4d59b9..cda86c5 100644 --- a/src/mesh/RawMesh.js +++ b/src/mesh/RawMesh.js @@ -53,16 +53,6 @@ this.state = state || new core.State(); /** - * 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; - //this.state.blendMode = this.blendMode; - - /** * The way the Mesh should be drawn, can be any of the {@link PIXI.mesh.RawMesh.DRAW_MODES} consts * * @member {number} @@ -141,7 +131,7 @@ _calculateBounds() { // The position property could be set manually? - if (this.geometry.style.attributes.aVertexPosition) + if (this.geometry.attributes.aVertexPosition) { const vertices = this.geometry.getAttribute('aVertexPosition').data; diff --git a/src/mesh/geometry/Attribute.js b/src/mesh/geometry/Attribute.js index f414439..633f9bb 100644 --- a/src/mesh/geometry/Attribute.js +++ b/src/mesh/geometry/Attribute.js @@ -12,14 +12,16 @@ { /** * @param {string} buffer the id of the buffer that this attribute will look for - * @param {Number} [size=2] the size of the attribute. If you hava 2 floats per vertex (eg position x and y) this would be 2 + * @param {Number} [size=0] the size of the attribute. If you hava 2 floats per vertex (eg position x and y) this would be 2. + * @param {Boolean} [normalised=false] should the data be normalised. + * @param {Number} [type=PIXI.TYPES.FLOAT] what type of numbe is the attribute. Check {PIXI.TYPES} to see the ones available * @param {Number} [stride=0] How far apart (in floats) the start of each value is. (used for interleaving data) * @param {Number} [start=0] How far into the array to start reading values (used for interleaving data) - * @param {Boolean} [normalised=false] should the data be normalised. */ - constructor(buffer, normalised = false, type, stride, start) + constructor(buffer, size, normalised = false, type = 5126, stride, start) { this.buffer = buffer; + this.size = size; this.normalized = normalised; this.type = type; this.stride = stride; diff --git a/src/mesh/geometry/Buffer.js b/src/mesh/geometry/Buffer.js index 5492687..1fc8ef6 100644 --- a/src/mesh/geometry/Buffer.js +++ b/src/mesh/geometry/Buffer.js @@ -31,6 +31,8 @@ this._updateID = 0; + this.index = false; + this.id = UID++; } diff --git a/src/mesh/geometry/Geometry.js b/src/mesh/geometry/Geometry.js index 3458ac2..b3a3fc2 100644 --- a/src/mesh/geometry/Geometry.js +++ b/src/mesh/geometry/Geometry.js @@ -1,5 +1,17 @@ import Attribute from './Attribute'; import Buffer from './Buffer'; +import interleaveTypedArrays from '../../core/utils/interleaveTypedArrays'; +import getBufferType from '../../core/utils/getBufferType'; + +const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; + +/* eslint-disable object-shorthand */ +const map = { + Float32Array: Float32Array, + Uint32Array: Uint32Array, + Int32Array: Int32Array, + Uint16Array: Uint16Array, +}; /* eslint-disable max-len */ @@ -26,7 +38,7 @@ export default class Geometry { /** - * @param {array} data this consists of buffers. optional. + * @param {array} buffers an array of buffers. optional. * @param {object} attributes of the geometry, optional structure of the attributes layout */ constructor(buffers, attributes) @@ -52,14 +64,15 @@ * * @param {String} id - the name of the attribute (matching up to a shader) * @param {PIXI.mesh.Buffer} [buffer] the buffer that holds the data of the attribute . You can also provide an Array and a buffer will be created from it. - * @param {Number} [size=2] the size of the attribute. If you hava 2 floats per vertex (eg position x and y) this would be 2 + * @param {Number} [size=0] the size of the attribute. If you hava 2 floats per vertex (eg position x and y) this would be 2 + * @param {Boolean} [normalised=false] should the data be normalised. + * @param {Number} [type=PIXI.TYPES.FLOAT] what type of numbe is the attribute. Check {PIXI.TYPES} to see the ones available * @param {Number} [stride=0] How far apart (in floats) the start of each value is. (used for interleaving data) * @param {Number} [start=0] How far into the array to start reading values (used for interleaving data) - * @param {Boolean} [normalised=false] should the data be normalised. * * @return {PIXI.mesh.Geometry} returns self, useful for chaining. */ - addAttribute(id, buffer, normalised = false, type, stride, start) + addAttribute(id, buffer, size, normalised = false, type, stride, start) { // check if this is a buffer! if (!buffer.data) @@ -73,13 +86,13 @@ buffer = new Buffer(buffer); } - var ids = id.split('|'); + const ids = id.split('|'); - if(ids.length > 1) + if (ids.length > 1) { - for (var i = 0; i < ids.length; i++) + for (let i = 0; i < ids.length; i++) { - this.addAttribute(ids[i], buffer, normalised, type) + this.addAttribute(ids[i], buffer, size, normalised, type); } return this; @@ -90,10 +103,10 @@ if (bufferIndex === -1) { this.buffers.push(buffer); - bufferIndex = this.buffers.length-1 + bufferIndex = this.buffers.length - 1; } - this.attributes[id] = new Attribute(bufferIndex, normalised, type, stride, start); + this.attributes[id] = new Attribute(bufferIndex, size, normalised, type, stride, start); return this; } @@ -152,6 +165,56 @@ } /** + * this function modifies the structure so that all current attributes become interleaved into a single buffer + * This can be useful if your model remains static as it offers a little performance boost + * + * @return {PIXI.mesh.Geometry} returns self, useful for chaining. + */ + interleave() + { + // a simple check to see if buffers are already interleaved.. + if (this.buffers.length === 1 || (this.buffers.length === 2 && this.indexBuffer)) return this; + + // assume already that no buffers are interleaved + const arrays = []; + const sizes = []; + const interleavedBuffer = new Buffer(); + let i; + + for (i in this.attributes) + { + const attribute = this.attributes[i]; + + const buffer = this.buffers[attribute.buffer]; + + arrays.push(buffer.data); + + sizes.push((attribute.size * byteSizeMap[attribute.type]) / 4); + + attribute.buffer = 0; + } + + interleavedBuffer.data = interleaveTypedArrays(arrays, sizes); + + for (i = 0; i < this.buffers.length; i++) + { + if (this.buffers[i] !== this.indexBuffer) + { + this.buffers[i].destroy(); + } + } + + this.buffers = [interleavedBuffer]; + + if (this.indexBuffer) + { + this.buffers.push(this.indexBuffer); + } + + return this; + } + + /** * Destroys the geometry. */ destroy() @@ -173,4 +236,108 @@ this.attributes = null; } + + /** + * merges an array of geometries into a new single one + * geometry attribute styles must match for this operation to work + * + * @param {array|PIXI.mesh.Geometry} geometries array of geometries to merge + * @returns {PIXI.mesh.Geometry} shiney new geometry + */ + static merge(geometries) + { + // todo add a geometry check! + // also a size check.. cant be too big!] + + const geometryOut = new Geometry(); + + const arrays = []; + const sizes = []; + const offsets = []; + + let geometry; + + // pass one.. get sizes.. + for (let i = 0; i < geometries.length; i++) + { + geometry = geometries[i]; + + for (let j = 0; j < geometry.buffers.length; j++) + { + sizes[j] = sizes[j] || 0; + sizes[j] += geometry.buffers[j].data.length; + offsets[j] = 0; + } + } + + // build the correct size arrays.. + for (let i = 0; i < geometry.buffers.length; i++) + { + // TODO types! + arrays[i] = new map[getBufferType(geometry.buffers[i].data)](sizes[i]); + geometryOut.buffers[i] = new Buffer(arrays[i]); + } + + // pass to set data.. + for (let i = 0; i < geometries.length; i++) + { + geometry = geometries[i]; + + for (let j = 0; j < geometry.buffers.length; j++) + { + arrays[j].set(geometry.buffers[j].data, offsets[j]); + offsets[j] += geometry.buffers[j].data.length; + } + } + + geometryOut.attributes = geometry.attributes; + + if (geometry.indexBuffer) + { + geometryOut.indexBuffer = geometryOut.buffers[geometry.buffers.indexOf(geometry.indexBuffer)]; + geometryOut.indexBuffer.index = true; + + let offset = 0; + let stride = 0; + let offset2 = 0; + let bufferIndexToCount = 0; + + // get a buffer + for (let i = 0; i < geometry.buffers.length; i++) + { + if (geometry.buffers[i] !== geometry.indexBuffer) + { + bufferIndexToCount = i; + break; + } + } + + // figure out the stride of one buffer.. + for (const i in geometry.attributes) + { + const attribute = geometry.attributes[i]; + + if ((attribute.buffer | 0) === bufferIndexToCount) + { + stride += ((attribute.size * byteSizeMap[attribute.type]) / 4); + } + } + + // time to off set all indexes.. + for (let i = 0; i < geometries.length; i++) + { + const indexBufferData = geometries[i].indexBuffer.data; + + for (let j = 0; j < indexBufferData.length; j++) + { + geometryOut.indexBuffer.data[j + offset2] += offset; + } + + offset += geometry.buffers[bufferIndexToCount].data.length / (stride); + offset2 += indexBufferData.length; + } + } + + return geometryOut; + } } diff --git a/src/mesh/index.js b/src/mesh/index.js index 1ab5333..a402b70 100644 --- a/src/mesh/index.js +++ b/src/mesh/index.js @@ -6,8 +6,6 @@ export { default as MeshRenderer } from './webgl/MeshRenderer'; export { default as CanvasMeshRenderer } from './canvas/CanvasMeshRenderer'; 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'; diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js index 30507eb..6af9a08 100644 --- a/src/mesh/webgl/MeshRenderer.js +++ b/src/mesh/webgl/MeshRenderer.js @@ -1,6 +1,8 @@ import * as core from '../../core'; import glCore from 'pixi-gl-core'; +const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; + /** * WebGL renderer plugin for tiling sprites * @@ -59,7 +61,7 @@ // bind the geometry... this.bindGeometry(mesh.geometry, glShader); - // then render it.. + // then render it mesh.geometry.glVertexArrayObjects[this.CONTEXT_UID].draw(mesh.drawMode, mesh.size, mesh.start); } @@ -67,6 +69,7 @@ * Binds geometry so that is can be drawn. Creating a Vao if required * @private * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. */ bindGeometry(geometry, glShader) { @@ -93,9 +96,10 @@ } /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry + * Creates a Vao with the same structure as the geometry and stores it on the geometry. * @private * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. */ initGeometryVao(geometry, glShader) @@ -109,8 +113,7 @@ const buffers = geometry.buffers; const attributes = geometry.attributes; - - // first update - and creat the buffers! + // first update - and create the buffers! for (let i = 0; i < buffers.length; i++) { const buffer = buffers[i]; @@ -128,35 +131,33 @@ } } - if (geometry.indexBuffer) { // first update the index buffer if we have one.. vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); } - var stride = 0; const tempStride = {}; const tempStart = {}; - for(const j in buffers) + for (const j in buffers) { - tempStride[buffers[j].id] = 0; - tempStart[buffers[j].id] = 0; + tempStride[j] = 0; + tempStart[j] = 0; } for (const j in attributes) { - // calculate stride.. - tempStride[attributes[j].buffer] += glShader.attributes[j].size; - //assuming float for now! + tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; } - let start = 0; - - - console.log(buffers) - console.log(attributes) + for (const j in attributes) + { + if (tempStride[attributes[j].buffer] === glShader.attributes[j].size * byteSizeMap[attributes[j].type]) + { + tempStride[attributes[j].buffer] = 0; + } + } // next update the attributes buffer.. for (const j in attributes) @@ -168,13 +169,16 @@ // need to know the shader as it means we can be lazy and let pixi do the work for us.. // stride, start, type? - vao.addAttribute(glBuffer, glShader.attributes[j], attribute.type || 5126, attribute.normalized, 0, 0)//, tempStride[attribute.buffer] * 4, tempStart[attribute.buffer] * 4); + vao.addAttribute(glBuffer, + glShader.attributes[j], + attribute.type || 5126, // (5126 = FLOAT) + attribute.normalized, + tempStride[attribute.buffer], + tempStart[attribute.buffer]); - tempStart[attribute.buffer] += glShader.attributes[j].size + tempStart[attribute.buffer] += glShader.attributes[j].size * byteSizeMap[attribute.type]; } - console.log(vao); - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; return vao;