123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- import { Logger } from "../Misc/logger.js";
- import { Vector3, Vector2 } from "../Maths/math.vector.js";
- import { VertexBuffer } from "../Buffers/buffer.js";
- import { Mesh } from "../Meshes/mesh.js";
- import { VertexData } from "../Meshes/mesh.vertexData.js";
- import { Path2 } from "../Maths/math.path.js";
- import { Epsilon } from "../Maths/math.constants.js";
- import { EngineStore } from "../Engines/engineStore.js";
- /**
- * Vector2 wth index property
- */
- class IndexedVector2 extends Vector2 {
- constructor(original,
- /** Index of the vector2 */
- index) {
- super(original.x, original.y);
- this.index = index;
- }
- }
- /**
- * Defines points to create a polygon
- */
- class PolygonPoints {
- constructor() {
- this.elements = [];
- }
- add(originalPoints) {
- const result = [];
- originalPoints.forEach((point) => {
- const newPoint = new IndexedVector2(point, this.elements.length);
- result.push(newPoint);
- this.elements.push(newPoint);
- });
- return result;
- }
- computeBounds() {
- const lmin = new Vector2(this.elements[0].x, this.elements[0].y);
- const lmax = new Vector2(this.elements[0].x, this.elements[0].y);
- this.elements.forEach((point) => {
- // x
- if (point.x < lmin.x) {
- lmin.x = point.x;
- }
- else if (point.x > lmax.x) {
- lmax.x = point.x;
- }
- // y
- if (point.y < lmin.y) {
- lmin.y = point.y;
- }
- else if (point.y > lmax.y) {
- lmax.y = point.y;
- }
- });
- return {
- min: lmin,
- max: lmax,
- width: lmax.x - lmin.x,
- height: lmax.y - lmin.y,
- };
- }
- }
- /**
- * Polygon
- * @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/creation/param#non-regular-polygon
- */
- export class Polygon {
- /**
- * Creates a rectangle
- * @param xmin bottom X coord
- * @param ymin bottom Y coord
- * @param xmax top X coord
- * @param ymax top Y coord
- * @returns points that make the resulting rectangle
- */
- static Rectangle(xmin, ymin, xmax, ymax) {
- return [new Vector2(xmin, ymin), new Vector2(xmax, ymin), new Vector2(xmax, ymax), new Vector2(xmin, ymax)];
- }
- /**
- * Creates a circle
- * @param radius radius of circle
- * @param cx scale in x
- * @param cy scale in y
- * @param numberOfSides number of sides that make up the circle
- * @returns points that make the resulting circle
- */
- static Circle(radius, cx = 0, cy = 0, numberOfSides = 32) {
- const result = [];
- let angle = 0;
- const increment = (Math.PI * 2) / numberOfSides;
- for (let i = 0; i < numberOfSides; i++) {
- result.push(new Vector2(cx + Math.cos(angle) * radius, cy + Math.sin(angle) * radius));
- angle -= increment;
- }
- return result;
- }
- /**
- * Creates a polygon from input string
- * @param input Input polygon data
- * @returns the parsed points
- */
- static Parse(input) {
- const floats = input
- .split(/[^-+eE.\d]+/)
- .map(parseFloat)
- .filter((val) => !isNaN(val));
- let i;
- const result = [];
- for (i = 0; i < (floats.length & 0x7ffffffe); i += 2) {
- result.push(new Vector2(floats[i], floats[i + 1]));
- }
- return result;
- }
- /**
- * Starts building a polygon from x and y coordinates
- * @param x x coordinate
- * @param y y coordinate
- * @returns the started path2
- */
- static StartingAt(x, y) {
- return Path2.StartingAt(x, y);
- }
- }
- /**
- * Builds a polygon
- * @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/creation/param/polyMeshBuilder
- */
- export class PolygonMeshBuilder {
- _addToepoint(points) {
- for (const p of points) {
- this._epoints.push(p.x, p.y);
- }
- }
- /**
- * Creates a PolygonMeshBuilder
- * @param name name of the builder
- * @param contours Path of the polygon
- * @param scene scene to add to when creating the mesh
- * @param earcutInjection can be used to inject your own earcut reference
- */
- constructor(name, contours, scene, earcutInjection = earcut) {
- this._points = new PolygonPoints();
- this._outlinepoints = new PolygonPoints();
- this._holes = new Array();
- this._epoints = new Array();
- this._eholes = new Array();
- this.bjsEarcut = earcutInjection;
- this._name = name;
- this._scene = scene || EngineStore.LastCreatedScene;
- let points;
- if (contours instanceof Path2) {
- points = contours.getPoints();
- }
- else {
- points = contours;
- }
- this._addToepoint(points);
- this._points.add(points);
- this._outlinepoints.add(points);
- if (typeof this.bjsEarcut === "undefined") {
- Logger.Warn("Earcut was not found, the polygon will not be built.");
- }
- }
- /**
- * Adds a hole within the polygon
- * @param hole Array of points defining the hole
- * @returns this
- */
- addHole(hole) {
- this._points.add(hole);
- const holepoints = new PolygonPoints();
- holepoints.add(hole);
- this._holes.push(holepoints);
- this._eholes.push(this._epoints.length / 2);
- this._addToepoint(hole);
- return this;
- }
- /**
- * Creates the polygon
- * @param updatable If the mesh should be updatable
- * @param depth The depth of the mesh created
- * @param smoothingThreshold Dot product threshold for smoothed normals
- * @returns the created mesh
- */
- build(updatable = false, depth = 0, smoothingThreshold = 2) {
- const result = new Mesh(this._name, this._scene);
- const vertexData = this.buildVertexData(depth, smoothingThreshold);
- result.setVerticesData(VertexBuffer.PositionKind, vertexData.positions, updatable);
- result.setVerticesData(VertexBuffer.NormalKind, vertexData.normals, updatable);
- result.setVerticesData(VertexBuffer.UVKind, vertexData.uvs, updatable);
- result.setIndices(vertexData.indices);
- return result;
- }
- /**
- * Creates the polygon
- * @param depth The depth of the mesh created
- * @param smoothingThreshold Dot product threshold for smoothed normals
- * @returns the created VertexData
- */
- buildVertexData(depth = 0, smoothingThreshold = 2) {
- const result = new VertexData();
- const normals = [];
- const positions = [];
- const uvs = [];
- const bounds = this._points.computeBounds();
- this._points.elements.forEach((p) => {
- normals.push(0, 1.0, 0);
- positions.push(p.x, 0, p.y);
- uvs.push((p.x - bounds.min.x) / bounds.width, (p.y - bounds.min.y) / bounds.height);
- });
- const indices = [];
- const res = this.bjsEarcut(this._epoints, this._eholes, 2);
- for (let i = 0; i < res.length; i++) {
- indices.push(res[i]);
- }
- if (depth > 0) {
- const positionscount = positions.length / 3; //get the current pointcount
- this._points.elements.forEach((p) => {
- //add the elements at the depth
- normals.push(0, -1.0, 0);
- positions.push(p.x, -depth, p.y);
- uvs.push(1 - (p.x - bounds.min.x) / bounds.width, 1 - (p.y - bounds.min.y) / bounds.height);
- });
- const totalCount = indices.length;
- for (let i = 0; i < totalCount; i += 3) {
- const i0 = indices[i + 0];
- const i1 = indices[i + 1];
- const i2 = indices[i + 2];
- indices.push(i2 + positionscount);
- indices.push(i1 + positionscount);
- indices.push(i0 + positionscount);
- }
- //Add the sides
- this._addSide(positions, normals, uvs, indices, bounds, this._outlinepoints, depth, false, smoothingThreshold);
- this._holes.forEach((hole) => {
- this._addSide(positions, normals, uvs, indices, bounds, hole, depth, true, smoothingThreshold);
- });
- }
- result.indices = indices;
- result.positions = positions;
- result.normals = normals;
- result.uvs = uvs;
- return result;
- }
- /**
- * Adds a side to the polygon
- * @param positions points that make the polygon
- * @param normals normals of the polygon
- * @param uvs uvs of the polygon
- * @param indices indices of the polygon
- * @param bounds bounds of the polygon
- * @param points points of the polygon
- * @param depth depth of the polygon
- * @param flip flip of the polygon
- * @param smoothingThreshold
- */
- _addSide(positions, normals, uvs, indices, bounds, points, depth, flip, smoothingThreshold) {
- let startIndex = positions.length / 3;
- let ulength = 0;
- for (let i = 0; i < points.elements.length; i++) {
- const p = points.elements[i];
- const p1 = points.elements[(i + 1) % points.elements.length];
- positions.push(p.x, 0, p.y);
- positions.push(p.x, -depth, p.y);
- positions.push(p1.x, 0, p1.y);
- positions.push(p1.x, -depth, p1.y);
- const p0 = points.elements[(i + points.elements.length - 1) % points.elements.length];
- const p2 = points.elements[(i + 2) % points.elements.length];
- let vc = new Vector3(-(p1.y - p.y), 0, p1.x - p.x);
- let vp = new Vector3(-(p.y - p0.y), 0, p.x - p0.x);
- let vn = new Vector3(-(p2.y - p1.y), 0, p2.x - p1.x);
- if (!flip) {
- vc = vc.scale(-1);
- vp = vp.scale(-1);
- vn = vn.scale(-1);
- }
- const vc_norm = vc.normalizeToNew();
- let vp_norm = vp.normalizeToNew();
- let vn_norm = vn.normalizeToNew();
- const dotp = Vector3.Dot(vp_norm, vc_norm);
- if (dotp > smoothingThreshold) {
- if (dotp < Epsilon - 1) {
- vp_norm = new Vector3(p.x, 0, p.y).subtract(new Vector3(p1.x, 0, p1.y)).normalize();
- }
- else {
- // cheap average weighed by side length
- vp_norm = vp.add(vc).normalize();
- }
- }
- else {
- vp_norm = vc_norm;
- }
- const dotn = Vector3.Dot(vn, vc);
- if (dotn > smoothingThreshold) {
- if (dotn < Epsilon - 1) {
- // back to back
- vn_norm = new Vector3(p1.x, 0, p1.y).subtract(new Vector3(p.x, 0, p.y)).normalize();
- }
- else {
- // cheap average weighed by side length
- vn_norm = vn.add(vc).normalize();
- }
- }
- else {
- vn_norm = vc_norm;
- }
- uvs.push(ulength / bounds.width, 0);
- uvs.push(ulength / bounds.width, 1);
- ulength += vc.length();
- uvs.push(ulength / bounds.width, 0);
- uvs.push(ulength / bounds.width, 1);
- normals.push(vp_norm.x, vp_norm.y, vp_norm.z);
- normals.push(vp_norm.x, vp_norm.y, vp_norm.z);
- normals.push(vn_norm.x, vn_norm.y, vn_norm.z);
- normals.push(vn_norm.x, vn_norm.y, vn_norm.z);
- if (!flip) {
- indices.push(startIndex);
- indices.push(startIndex + 1);
- indices.push(startIndex + 2);
- indices.push(startIndex + 1);
- indices.push(startIndex + 3);
- indices.push(startIndex + 2);
- }
- else {
- indices.push(startIndex);
- indices.push(startIndex + 2);
- indices.push(startIndex + 1);
- indices.push(startIndex + 1);
- indices.push(startIndex + 2);
- indices.push(startIndex + 3);
- }
- startIndex += 4;
- }
- }
- }
- //# sourceMappingURL=polygonMesh.js.map
|