UNPKG

p5

Version:

[![npm version](https://badge.fury.io/js/p5.svg)](https://www.npmjs.com/package/p5)

193 lines (175 loc) 5.83 kB
import { T as TRIANGLE_STRIP, Q as QUAD_STRIP, K as TRIANGLE_FAN, c as TRIANGLES } from '../constants-DwbuOBz3.js'; import '../math/p5.Matrix.js'; import { Geometry } from './p5.Geometry.js'; import { Matrix } from '../math/Matrices/Matrix.js'; import '../math/p5.Vector.js'; import '../math/Matrices/MatrixInterface.js'; import './p5.DataArray.js'; import '../io/utilities.js'; /** * @private * A class responsible for converting successive WebGL draw calls into a single * `p5.Geometry` that can be reused and drawn with `model()`. */ class GeometryBuilder { constructor(renderer) { this.renderer = renderer; renderer._pInst.push(); this.identityMatrix = new Matrix(4); renderer.states.setValue('uModelMatrix', new Matrix(4)); this.geometry = new Geometry( undefined, undefined, undefined, this.renderer ); this.geometry.gid = `_p5_GeometryBuilder_${GeometryBuilder.nextGeometryId}`; GeometryBuilder.nextGeometryId++; this.hasTransform = false; } /** * @private * Applies the current transformation matrix to each vertex. */ transformVertices(vertices) { if (!this.hasTransform) return vertices; return vertices.map(v => this.renderer.states.uModelMatrix.multiplyPoint(v) ); } /** * @private * Applies the current normal matrix to each normal. */ transformNormals(normals) { if (!this.hasTransform) return normals; return normals.map( v => this.renderer.scratchMat3.multiplyVec(v) // this is a vec3 ); } /** * @private * Adds a p5.Geometry to the builder's combined geometry, flattening * transformations. */ addGeometry(input) { this.hasTransform = !this.renderer.states.uModelMatrix.mat4 .every((v, i) => v === this.identityMatrix.mat4[i]); if (this.hasTransform) { this.renderer.scratchMat3.inverseTranspose4x4( this.renderer.states.uModelMatrix ); } let startIdx = this.geometry.vertices.length; for (const v of this.transformVertices(input.vertices)) { this.geometry.vertices.push(v); } for (const vn of this.transformNormals(input.vertexNormals)) { this.geometry.vertexNormals.push(vn); } for (const val of input.uvs) { this.geometry.uvs.push(val); } const inputUserVertexProps = input.userVertexProperties; const builtUserVertexProps = this.geometry.userVertexProperties; const numPreviousVertices = this.geometry.vertices.length - input.vertices.length; for (const propName in builtUserVertexProps){ if (propName in inputUserVertexProps){ continue; } const prop = builtUserVertexProps[propName]; const size = prop.getDataSize(); const numMissingValues = size * input.vertices.length; const missingValues = Array(numMissingValues).fill(0); prop.pushDirect(missingValues); } for (const propName in inputUserVertexProps){ const prop = inputUserVertexProps[propName]; const data = prop.getSrcArray(); const size = prop.getDataSize(); if (numPreviousVertices > 0 && !(propName in builtUserVertexProps)){ const numMissingValues = size * numPreviousVertices; const missingValues = Array(numMissingValues).fill(0); this.geometry.vertexProperty(propName, missingValues, size); } this.geometry.vertexProperty(propName, data, size); } if (this.renderer.states.fillColor) { this.geometry.faces.push( ...input.faces.map(f => f.map(idx => idx + startIdx)) ); } if (this.renderer.states.strokeColor) { for (const edge of input.edges.map(edge => edge.map(idx => idx + startIdx))) { this.geometry.edges.push(edge); } } const vertexColors = [...input.vertexColors]; while (vertexColors.length < input.vertices.length * 4) { vertexColors.push(...this.renderer.states.curFillColor); } for (const c of vertexColors) { this.geometry.vertexColors.push(c); } } /** * Adds geometry from the renderer's immediate mode into the builder's * combined geometry. */ addImmediate(geometry, shapeMode, { validateFaces = false } = {}) { const faces = []; if (this.renderer.states.fillColor) { if ( shapeMode === TRIANGLE_STRIP || shapeMode === QUAD_STRIP ) { for (let i = 2; i < geometry.vertices.length; i++) { if (i % 2 === 0) { faces.push([i, i - 1, i - 2]); } else { faces.push([i, i - 2, i - 1]); } } } else if (shapeMode === TRIANGLE_FAN) { for (let i = 2; i < geometry.vertices.length; i++) { faces.push([0, i - 1, i]); } } else if (shapeMode === TRIANGLES) { for (let i = 0; i < geometry.vertices.length; i += 3) { if ( !validateFaces || geometry.vertices[i].copy().sub(geometry.vertices[i+1]) .cross(geometry.vertices[i].copy().sub(geometry.vertices[i+2])) .magSq() > 0 ) { faces.push([i, i + 1, i + 2]); } } } } this.addGeometry(Object.assign({}, geometry, { faces })); } /** * Adds geometry from the renderer's retained mode into the builder's * combined geometry. */ addRetained(geometry) { this.addGeometry(geometry); } /** * Cleans up the state of the renderer and returns the combined geometry that * was built. * @returns p5.Geometry The flattened, combined geometry */ finish() { this.renderer._pInst.pop(); return this.geometry; } } /** * Keeps track of how many custom geometry objects have been made so that each * can be assigned a unique ID. */ GeometryBuilder.nextGeometryId = 0; export { GeometryBuilder as default };