import { State } from '@pixi/core'; import { DRAW_MODES } from '@pixi/constants'; import { Point, Polygon } from '@pixi/math'; import { Container } from '@pixi/display'; const tempPoint = new Point(); const tempPolygon = new Polygon(); /** * Base mesh class. * The reason for this class is to empower you to have maximum flexbilty to render any kind of webGL you can think of. * This class assumes a certain level of webGL knowledge. * If you know a bit this should abstract enough away to make you life easier! * Pretty much ALL WebGL can be broken down into the following: * Geometry - The structure and data for the mesh. This can include anything from positions, uvs, normals, colors etc.. * Shader - This is the shader that pixi will render the geometry with. (attributes in the shader must match the geometry!) * Uniforms - These are the values passed to the shader when the mesh is rendered. * As a shader can be resued accross multiple objects, it made sense to allow uniforms to exist outside of the shader * State - This is the state of WebGL required to render the mesh. * Through a combination of the above elements you can render anything you want, 2D or 3D! * * @class * @extends PIXI.Container * @memberof PIXI */ export default class RawMesh extends Container { /** * @param {PIXI.Geometry} geometry the geometry the mesh will use * @param {PIXI.Shader} shader the shader the mesh will use * @param {PIXI.State} state the state that the webGL context is required to be in to render the mesh * @param {number} drawMode the drawMode, can be any of the PIXI.DRAW_MODES consts */ constructor(geometry, shader, state, drawMode = DRAW_MODES.TRIANGLES) { super(); /** * the geometry the mesh will use * @type {PIXI.Geometry} */ this.geometry = geometry; /** * the shader the mesh will use * @type {PIXI.Shader} */ this.shader = shader; /** * the webGL state the mesh requires to render * @type {PIXI.State} */ this.state = state || new State(); /** * The way the Mesh should be drawn, can be any of the {@link PIXI.RawMesh.DRAW_MODES} consts * * @member {number} * @see PIXI.RawMesh.DRAW_MODES */ this.drawMode = drawMode; /** * The way uniforms that will be used by the mesh's shader. * @member {Object} */ /** * A map of renderer IDs to webgl render data * * @private * @member {object<number, object>} */ this._glDatas = {}; /** * Plugin that is responsible for rendering this element. * Allows to customize the rendering process without overriding '_render' & '_renderCanvas' methods. * * @member {string} * @default 'mesh' */ this.pluginName = 'mesh'; this.start = 0; this.size = 0; } /** * Renders the object using the WebGL renderer * * @param {PIXI.Renderer} renderer a reference to the WebGL renderer * @private */ _render(renderer) { renderer.batch.setObjectRenderer(renderer.plugins[this.pluginName]); renderer.plugins[this.pluginName].render(this); } /** * Updates the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. * there must be a aVertexPosition attribute present in the geometry for bounds to be calcualted correctly. * * @private */ _calculateBounds() { // The position property could be set manually? if (this.geometry.attributes.aVertexPosition) { const vertices = this.geometry.getAttribute('aVertexPosition').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 * @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.geometry.getAttribute('aVertexPosition').data; const points = tempPolygon.points; const indices = this.geometry.getIndex().data; const len = indices.length; const step = this.drawMode === 4 ? 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; } }