csg.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  1. import { Quaternion, Matrix, Vector3, Vector2 } from "../Maths/math.vector.js";
  2. import { VertexBuffer } from "../Buffers/buffer.js";
  3. import { SubMesh } from "../Meshes/subMesh.js";
  4. import { Mesh } from "../Meshes/mesh.js";
  5. import { Color4 } from "../Maths/math.color.js";
  6. import { VertexData } from "./mesh.vertexData.js";
  7. /**
  8. * Unique ID when we import meshes from Babylon to CSG
  9. */
  10. let currentCSGMeshId = 0;
  11. /**
  12. * Represents a vertex of a polygon. Use your own vertex class instead of this
  13. * one to provide additional features like texture coordinates and vertex
  14. * colors. Custom vertex classes need to provide a `pos` property and `clone()`,
  15. * `flip()`, and `interpolate()` methods that behave analogous to the ones
  16. * defined by `BABYLON.CSG.Vertex`. This class provides `normal` so convenience
  17. * functions like `BABYLON.CSG.sphere()` can return a smooth vertex normal, but `normal`
  18. * is not used anywhere else.
  19. * Same goes for uv, it allows to keep the original vertex uv coordinates of the 2 meshes
  20. */
  21. class Vertex {
  22. /**
  23. * Initializes the vertex
  24. * @param pos The position of the vertex
  25. * @param normal The normal of the vertex
  26. * @param uv The texture coordinate of the vertex
  27. * @param vertColor The RGBA color of the vertex
  28. */
  29. constructor(
  30. /**
  31. * The position of the vertex
  32. */
  33. pos,
  34. /**
  35. * The normal of the vertex
  36. */
  37. normal,
  38. /**
  39. * The texture coordinate of the vertex
  40. */
  41. uv,
  42. /**
  43. * The texture coordinate of the vertex
  44. */
  45. vertColor) {
  46. this.pos = pos;
  47. this.normal = normal;
  48. this.uv = uv;
  49. this.vertColor = vertColor;
  50. }
  51. /**
  52. * Make a clone, or deep copy, of the vertex
  53. * @returns A new Vertex
  54. */
  55. clone() {
  56. return new Vertex(this.pos.clone(), this.normal.clone(), this.uv?.clone(), this.vertColor?.clone());
  57. }
  58. /**
  59. * Invert all orientation-specific data (e.g. vertex normal). Called when the
  60. * orientation of a polygon is flipped.
  61. */
  62. flip() {
  63. this.normal = this.normal.scale(-1);
  64. }
  65. /**
  66. * Create a new vertex between this vertex and `other` by linearly
  67. * interpolating all properties using a parameter of `t`. Subclasses should
  68. * override this to interpolate additional properties.
  69. * @param other the vertex to interpolate against
  70. * @param t The factor used to linearly interpolate between the vertices
  71. * @returns The new interpolated vertex
  72. */
  73. interpolate(other, t) {
  74. return new Vertex(Vector3.Lerp(this.pos, other.pos, t), Vector3.Lerp(this.normal, other.normal, t), this.uv && other.uv ? Vector2.Lerp(this.uv, other.uv, t) : undefined, this.vertColor && other.vertColor ? Color4.Lerp(this.vertColor, other.vertColor, t) : undefined);
  75. }
  76. }
  77. /**
  78. * Represents a plane in 3D space.
  79. */
  80. class CSGPlane {
  81. /**
  82. * Initializes the plane
  83. * @param normal The normal for the plane
  84. * @param w
  85. */
  86. constructor(normal, w) {
  87. this.normal = normal;
  88. this.w = w;
  89. }
  90. /**
  91. * Construct a plane from three points
  92. * @param a Point a
  93. * @param b Point b
  94. * @param c Point c
  95. * @returns A new plane
  96. */
  97. static FromPoints(a, b, c) {
  98. const v0 = c.subtract(a);
  99. const v1 = b.subtract(a);
  100. if (v0.lengthSquared() === 0 || v1.lengthSquared() === 0) {
  101. return null;
  102. }
  103. const n = Vector3.Normalize(Vector3.Cross(v0, v1));
  104. return new CSGPlane(n, Vector3.Dot(n, a));
  105. }
  106. /**
  107. * Clone, or make a deep copy of the plane
  108. * @returns a new Plane
  109. */
  110. clone() {
  111. return new CSGPlane(this.normal.clone(), this.w);
  112. }
  113. /**
  114. * Flip the face of the plane
  115. */
  116. flip() {
  117. this.normal.scaleInPlace(-1);
  118. this.w = -this.w;
  119. }
  120. /**
  121. * Split `polygon` by this plane if needed, then put the polygon or polygon
  122. * fragments in the appropriate lists. Coplanar polygons go into either
  123. `* coplanarFront` or `coplanarBack` depending on their orientation with
  124. * respect to this plane. Polygons in front or in back of this plane go into
  125. * either `front` or `back`
  126. * @param polygon The polygon to be split
  127. * @param coplanarFront Will contain polygons coplanar with the plane that are oriented to the front of the plane
  128. * @param coplanarBack Will contain polygons coplanar with the plane that are oriented to the back of the plane
  129. * @param front Will contain the polygons in front of the plane
  130. * @param back Will contain the polygons begind the plane
  131. */
  132. splitPolygon(polygon, coplanarFront, coplanarBack, front, back) {
  133. const COPLANAR = 0;
  134. const FRONT = 1;
  135. const BACK = 2;
  136. const SPANNING = 3;
  137. // Classify each point as well as the entire polygon into one of the above
  138. // four classes.
  139. let polygonType = 0;
  140. const types = [];
  141. let i;
  142. let t;
  143. for (i = 0; i < polygon.vertices.length; i++) {
  144. t = Vector3.Dot(this.normal, polygon.vertices[i].pos) - this.w;
  145. const type = t < -CSGPlane.EPSILON ? BACK : t > CSGPlane.EPSILON ? FRONT : COPLANAR;
  146. polygonType |= type;
  147. types.push(type);
  148. }
  149. // Put the polygon in the correct list, splitting it when necessary
  150. switch (polygonType) {
  151. case COPLANAR:
  152. (Vector3.Dot(this.normal, polygon.plane.normal) > 0 ? coplanarFront : coplanarBack).push(polygon);
  153. break;
  154. case FRONT:
  155. front.push(polygon);
  156. break;
  157. case BACK:
  158. back.push(polygon);
  159. break;
  160. case SPANNING: {
  161. const f = [], b = [];
  162. for (i = 0; i < polygon.vertices.length; i++) {
  163. const j = (i + 1) % polygon.vertices.length;
  164. const ti = types[i], tj = types[j];
  165. const vi = polygon.vertices[i], vj = polygon.vertices[j];
  166. if (ti !== BACK) {
  167. f.push(vi);
  168. }
  169. if (ti !== FRONT) {
  170. b.push(ti !== BACK ? vi.clone() : vi);
  171. }
  172. if ((ti | tj) === SPANNING) {
  173. t = (this.w - Vector3.Dot(this.normal, vi.pos)) / Vector3.Dot(this.normal, vj.pos.subtract(vi.pos));
  174. const v = vi.interpolate(vj, t);
  175. f.push(v);
  176. b.push(v.clone());
  177. }
  178. }
  179. let poly;
  180. if (f.length >= 3) {
  181. poly = new CSGPolygon(f, polygon.shared);
  182. if (poly.plane) {
  183. front.push(poly);
  184. }
  185. }
  186. if (b.length >= 3) {
  187. poly = new CSGPolygon(b, polygon.shared);
  188. if (poly.plane) {
  189. back.push(poly);
  190. }
  191. }
  192. break;
  193. }
  194. }
  195. }
  196. }
  197. /**
  198. * `CSG.Plane.EPSILON` is the tolerance used by `splitPolygon()` to decide if a
  199. * point is on the plane
  200. */
  201. CSGPlane.EPSILON = 1e-5;
  202. /**
  203. * Represents a convex polygon. The vertices used to initialize a polygon must
  204. * be coplanar and form a convex loop.
  205. *
  206. * Each convex polygon has a `shared` property, which is shared between all
  207. * polygons that are clones of each other or were split from the same polygon.
  208. * This can be used to define per-polygon properties (such as surface color)
  209. */
  210. class CSGPolygon {
  211. /**
  212. * Initializes the polygon
  213. * @param vertices The vertices of the polygon
  214. * @param shared The properties shared across all polygons
  215. */
  216. constructor(vertices, shared) {
  217. this.vertices = vertices;
  218. this.shared = shared;
  219. this.plane = CSGPlane.FromPoints(vertices[0].pos, vertices[1].pos, vertices[2].pos);
  220. }
  221. /**
  222. * Clones, or makes a deep copy, or the polygon
  223. * @returns A new CSGPolygon
  224. */
  225. clone() {
  226. const vertices = this.vertices.map((v) => v.clone());
  227. return new CSGPolygon(vertices, this.shared);
  228. }
  229. /**
  230. * Flips the faces of the polygon
  231. */
  232. flip() {
  233. this.vertices.reverse().map((v) => {
  234. v.flip();
  235. });
  236. this.plane.flip();
  237. }
  238. }
  239. /**
  240. * Holds a node in a BSP tree. A BSP tree is built from a collection of polygons
  241. * by picking a polygon to split along. That polygon (and all other coplanar
  242. * polygons) are added directly to that node and the other polygons are added to
  243. * the front and/or back subtrees. This is not a leafy BSP tree since there is
  244. * no distinction between internal and leaf nodes
  245. */
  246. class Node {
  247. /**
  248. * Initializes the node
  249. * @param polygons A collection of polygons held in the node
  250. */
  251. constructor(polygons) {
  252. this._plane = null;
  253. this._front = null;
  254. this._back = null;
  255. this._polygons = new Array();
  256. if (polygons) {
  257. this.build(polygons);
  258. }
  259. }
  260. /**
  261. * Clones, or makes a deep copy, of the node
  262. * @returns The cloned node
  263. */
  264. clone() {
  265. const node = new Node();
  266. node._plane = this._plane && this._plane.clone();
  267. node._front = this._front && this._front.clone();
  268. node._back = this._back && this._back.clone();
  269. node._polygons = this._polygons.map((p) => p.clone());
  270. return node;
  271. }
  272. /**
  273. * Convert solid space to empty space and empty space to solid space
  274. */
  275. invert() {
  276. for (let i = 0; i < this._polygons.length; i++) {
  277. this._polygons[i].flip();
  278. }
  279. if (this._plane) {
  280. this._plane.flip();
  281. }
  282. if (this._front) {
  283. this._front.invert();
  284. }
  285. if (this._back) {
  286. this._back.invert();
  287. }
  288. const temp = this._front;
  289. this._front = this._back;
  290. this._back = temp;
  291. }
  292. /**
  293. * Recursively remove all polygons in `polygons` that are inside this BSP
  294. * tree.
  295. * @param polygons Polygons to remove from the BSP
  296. * @returns Polygons clipped from the BSP
  297. */
  298. clipPolygons(polygons) {
  299. if (!this._plane) {
  300. return polygons.slice();
  301. }
  302. let front = [], back = [];
  303. for (let i = 0; i < polygons.length; i++) {
  304. this._plane.splitPolygon(polygons[i], front, back, front, back);
  305. }
  306. if (this._front) {
  307. front = this._front.clipPolygons(front);
  308. }
  309. if (this._back) {
  310. back = this._back.clipPolygons(back);
  311. }
  312. else {
  313. back = [];
  314. }
  315. return front.concat(back);
  316. }
  317. /**
  318. * Remove all polygons in this BSP tree that are inside the other BSP tree
  319. * `bsp`.
  320. * @param bsp BSP containing polygons to remove from this BSP
  321. */
  322. clipTo(bsp) {
  323. this._polygons = bsp.clipPolygons(this._polygons);
  324. if (this._front) {
  325. this._front.clipTo(bsp);
  326. }
  327. if (this._back) {
  328. this._back.clipTo(bsp);
  329. }
  330. }
  331. /**
  332. * Return a list of all polygons in this BSP tree
  333. * @returns List of all polygons in this BSP tree
  334. */
  335. allPolygons() {
  336. let polygons = this._polygons.slice();
  337. if (this._front) {
  338. polygons = polygons.concat(this._front.allPolygons());
  339. }
  340. if (this._back) {
  341. polygons = polygons.concat(this._back.allPolygons());
  342. }
  343. return polygons;
  344. }
  345. /**
  346. * Build a BSP tree out of `polygons`. When called on an existing tree, the
  347. * new polygons are filtered down to the bottom of the tree and become new
  348. * nodes there. Each set of polygons is partitioned using the first polygon
  349. * (no heuristic is used to pick a good split)
  350. * @param polygons Polygons used to construct the BSP tree
  351. */
  352. build(polygons) {
  353. if (!polygons.length) {
  354. return;
  355. }
  356. if (!this._plane) {
  357. this._plane = polygons[0].plane.clone();
  358. }
  359. const front = [], back = [];
  360. for (let i = 0; i < polygons.length; i++) {
  361. this._plane.splitPolygon(polygons[i], this._polygons, this._polygons, front, back);
  362. }
  363. if (front.length) {
  364. if (!this._front) {
  365. this._front = new Node();
  366. }
  367. this._front.build(front);
  368. }
  369. if (back.length) {
  370. if (!this._back) {
  371. this._back = new Node();
  372. }
  373. this._back.build(back);
  374. }
  375. }
  376. }
  377. /**
  378. * Class for building Constructive Solid Geometry
  379. */
  380. export class CSG {
  381. constructor() {
  382. this._polygons = new Array();
  383. }
  384. /**
  385. * Convert a VertexData to CSG
  386. * @param data defines the VertexData to convert to CSG
  387. * @returns the new CSG
  388. */
  389. static FromVertexData(data) {
  390. let vertex, polygon, vertices;
  391. const polygons = [];
  392. const indices = data.indices;
  393. const positions = data.positions;
  394. const normals = data.normals;
  395. const uvs = data.uvs;
  396. const vertColors = data.colors;
  397. if (!indices || !positions) {
  398. // eslint-disable-next-line no-throw-literal
  399. throw "BABYLON.CSG: VertexData must at least contain positions and indices";
  400. }
  401. for (let i = 0; i < indices.length; i += 3) {
  402. vertices = [];
  403. for (let j = 0; j < 3; j++) {
  404. const indexIndices = i + j;
  405. const offset = indices[indexIndices];
  406. const normal = normals ? Vector3.FromArray(normals, offset * 3) : Vector3.Zero();
  407. const uv = uvs ? Vector2.FromArray(uvs, offset * 2) : undefined;
  408. const vertColor = vertColors ? Color4.FromArray(vertColors, offset * 4) : undefined;
  409. const position = Vector3.FromArray(positions, offset * 3);
  410. vertex = new Vertex(position, normal, uv, vertColor);
  411. vertices.push(vertex);
  412. }
  413. polygon = new CSGPolygon(vertices, { subMeshId: 0, meshId: currentCSGMeshId, materialIndex: 0 });
  414. // To handle the case of degenerated triangle
  415. // polygon.plane == null <=> the polygon does not represent 1 single plane <=> the triangle is degenerated
  416. if (polygon.plane) {
  417. polygons.push(polygon);
  418. }
  419. }
  420. const csg = CSG._FromPolygons(polygons);
  421. csg.matrix = Matrix.Identity();
  422. csg.position = Vector3.Zero();
  423. csg.rotation = Vector3.Zero();
  424. csg.scaling = Vector3.One();
  425. csg.rotationQuaternion = Quaternion.Identity();
  426. currentCSGMeshId++;
  427. return csg;
  428. }
  429. /**
  430. * Convert the Mesh to CSG
  431. * @param mesh The Mesh to convert to CSG
  432. * @param absolute If true, the final (local) matrix transformation is set to the identity and not to that of `mesh`. It can help when dealing with right-handed meshes (default: false)
  433. * @returns A new CSG from the Mesh
  434. */
  435. static FromMesh(mesh, absolute = false) {
  436. let vertex, normal, uv = undefined, position, vertColor = undefined, polygon, vertices;
  437. const polygons = [];
  438. let matrix, meshPosition, meshRotation, meshRotationQuaternion = null, meshScaling;
  439. let invertWinding = false;
  440. if (mesh instanceof Mesh) {
  441. mesh.computeWorldMatrix(true);
  442. matrix = mesh.getWorldMatrix();
  443. meshPosition = mesh.position.clone();
  444. meshRotation = mesh.rotation.clone();
  445. if (mesh.rotationQuaternion) {
  446. meshRotationQuaternion = mesh.rotationQuaternion.clone();
  447. }
  448. meshScaling = mesh.scaling.clone();
  449. if (mesh.material && absolute) {
  450. invertWinding = mesh.material.sideOrientation === 0;
  451. }
  452. }
  453. else {
  454. // eslint-disable-next-line no-throw-literal
  455. throw "BABYLON.CSG: Wrong Mesh type, must be BABYLON.Mesh";
  456. }
  457. const indices = mesh.getIndices(), positions = mesh.getVerticesData(VertexBuffer.PositionKind), normals = mesh.getVerticesData(VertexBuffer.NormalKind), uvs = mesh.getVerticesData(VertexBuffer.UVKind), vertColors = mesh.getVerticesData(VertexBuffer.ColorKind);
  458. const subMeshes = mesh.subMeshes;
  459. for (let sm = 0, sml = subMeshes.length; sm < sml; sm++) {
  460. for (let i = subMeshes[sm].indexStart, il = subMeshes[sm].indexCount + subMeshes[sm].indexStart; i < il; i += 3) {
  461. vertices = [];
  462. for (let j = 0; j < 3; j++) {
  463. const indexIndices = j === 0 ? i + j : invertWinding ? i + 3 - j : i + j;
  464. const sourceNormal = new Vector3(normals[indices[indexIndices] * 3], normals[indices[indexIndices] * 3 + 1], normals[indices[indexIndices] * 3 + 2]);
  465. if (uvs) {
  466. uv = new Vector2(uvs[indices[indexIndices] * 2], uvs[indices[indexIndices] * 2 + 1]);
  467. }
  468. if (vertColors) {
  469. vertColor = new Color4(vertColors[indices[indexIndices] * 4], vertColors[indices[indexIndices] * 4 + 1], vertColors[indices[indexIndices] * 4 + 2], vertColors[indices[indexIndices] * 4 + 3]);
  470. }
  471. const sourcePosition = new Vector3(positions[indices[indexIndices] * 3], positions[indices[indexIndices] * 3 + 1], positions[indices[indexIndices] * 3 + 2]);
  472. position = Vector3.TransformCoordinates(sourcePosition, matrix);
  473. normal = Vector3.TransformNormal(sourceNormal, matrix);
  474. vertex = new Vertex(position, normal, uv, vertColor);
  475. vertices.push(vertex);
  476. }
  477. polygon = new CSGPolygon(vertices, { subMeshId: sm, meshId: currentCSGMeshId, materialIndex: subMeshes[sm].materialIndex });
  478. // To handle the case of degenerated triangle
  479. // polygon.plane == null <=> the polygon does not represent 1 single plane <=> the triangle is degenerated
  480. if (polygon.plane) {
  481. polygons.push(polygon);
  482. }
  483. }
  484. }
  485. const csg = CSG._FromPolygons(polygons);
  486. csg.matrix = absolute ? Matrix.Identity() : matrix;
  487. csg.position = absolute ? Vector3.Zero() : meshPosition;
  488. csg.rotation = absolute ? Vector3.Zero() : meshRotation;
  489. csg.scaling = absolute ? Vector3.One() : meshScaling;
  490. csg.rotationQuaternion = absolute && meshRotationQuaternion ? Quaternion.Identity() : meshRotationQuaternion;
  491. currentCSGMeshId++;
  492. return csg;
  493. }
  494. /**
  495. * Construct a CSG solid from a list of `CSG.Polygon` instances.
  496. * @param polygons Polygons used to construct a CSG solid
  497. * @returns A new CSG solid
  498. */
  499. static _FromPolygons(polygons) {
  500. const csg = new CSG();
  501. csg._polygons = polygons;
  502. return csg;
  503. }
  504. /**
  505. * Clones, or makes a deep copy, of the CSG
  506. * @returns A new CSG
  507. */
  508. clone() {
  509. const csg = new CSG();
  510. csg._polygons = this._polygons.map((p) => p.clone());
  511. csg.copyTransformAttributes(this);
  512. return csg;
  513. }
  514. /**
  515. * Unions this CSG with another CSG
  516. * @param csg The CSG to union against this CSG
  517. * @returns The unioned CSG
  518. */
  519. union(csg) {
  520. const a = new Node(this.clone()._polygons);
  521. const b = new Node(csg.clone()._polygons);
  522. a.clipTo(b);
  523. b.clipTo(a);
  524. b.invert();
  525. b.clipTo(a);
  526. b.invert();
  527. a.build(b.allPolygons());
  528. return CSG._FromPolygons(a.allPolygons()).copyTransformAttributes(this);
  529. }
  530. /**
  531. * Unions this CSG with another CSG in place
  532. * @param csg The CSG to union against this CSG
  533. */
  534. unionInPlace(csg) {
  535. const a = new Node(this._polygons);
  536. const b = new Node(csg._polygons);
  537. a.clipTo(b);
  538. b.clipTo(a);
  539. b.invert();
  540. b.clipTo(a);
  541. b.invert();
  542. a.build(b.allPolygons());
  543. this._polygons = a.allPolygons();
  544. }
  545. /**
  546. * Subtracts this CSG with another CSG
  547. * @param csg The CSG to subtract against this CSG
  548. * @returns A new CSG
  549. */
  550. subtract(csg) {
  551. const a = new Node(this.clone()._polygons);
  552. const b = new Node(csg.clone()._polygons);
  553. a.invert();
  554. a.clipTo(b);
  555. b.clipTo(a);
  556. b.invert();
  557. b.clipTo(a);
  558. b.invert();
  559. a.build(b.allPolygons());
  560. a.invert();
  561. return CSG._FromPolygons(a.allPolygons()).copyTransformAttributes(this);
  562. }
  563. /**
  564. * Subtracts this CSG with another CSG in place
  565. * @param csg The CSG to subtract against this CSG
  566. */
  567. subtractInPlace(csg) {
  568. const a = new Node(this._polygons);
  569. const b = new Node(csg._polygons);
  570. a.invert();
  571. a.clipTo(b);
  572. b.clipTo(a);
  573. b.invert();
  574. b.clipTo(a);
  575. b.invert();
  576. a.build(b.allPolygons());
  577. a.invert();
  578. this._polygons = a.allPolygons();
  579. }
  580. /**
  581. * Intersect this CSG with another CSG
  582. * @param csg The CSG to intersect against this CSG
  583. * @returns A new CSG
  584. */
  585. intersect(csg) {
  586. const a = new Node(this.clone()._polygons);
  587. const b = new Node(csg.clone()._polygons);
  588. a.invert();
  589. b.clipTo(a);
  590. b.invert();
  591. a.clipTo(b);
  592. b.clipTo(a);
  593. a.build(b.allPolygons());
  594. a.invert();
  595. return CSG._FromPolygons(a.allPolygons()).copyTransformAttributes(this);
  596. }
  597. /**
  598. * Intersects this CSG with another CSG in place
  599. * @param csg The CSG to intersect against this CSG
  600. */
  601. intersectInPlace(csg) {
  602. const a = new Node(this._polygons);
  603. const b = new Node(csg._polygons);
  604. a.invert();
  605. b.clipTo(a);
  606. b.invert();
  607. a.clipTo(b);
  608. b.clipTo(a);
  609. a.build(b.allPolygons());
  610. a.invert();
  611. this._polygons = a.allPolygons();
  612. }
  613. /**
  614. * Return a new CSG solid with solid and empty space switched. This solid is
  615. * not modified.
  616. * @returns A new CSG solid with solid and empty space switched
  617. */
  618. inverse() {
  619. const csg = this.clone();
  620. csg.inverseInPlace();
  621. return csg;
  622. }
  623. /**
  624. * Inverses the CSG in place
  625. */
  626. inverseInPlace() {
  627. this._polygons.map((p) => {
  628. p.flip();
  629. });
  630. }
  631. /**
  632. * This is used to keep meshes transformations so they can be restored
  633. * when we build back a Babylon Mesh
  634. * NB : All CSG operations are performed in world coordinates
  635. * @param csg The CSG to copy the transform attributes from
  636. * @returns This CSG
  637. */
  638. copyTransformAttributes(csg) {
  639. this.matrix = csg.matrix;
  640. this.position = csg.position;
  641. this.rotation = csg.rotation;
  642. this.scaling = csg.scaling;
  643. this.rotationQuaternion = csg.rotationQuaternion;
  644. return this;
  645. }
  646. /**
  647. * Build vertex data from CSG
  648. * Coordinates here are in world space
  649. * @param onBeforePolygonProcessing called before each polygon is being processed
  650. * @param onAfterPolygonProcessing called after each polygon has been processed
  651. * @returns the final vertex data
  652. */
  653. toVertexData(onBeforePolygonProcessing = null, onAfterPolygonProcessing = null) {
  654. const matrix = this.matrix.clone();
  655. matrix.invert();
  656. const polygons = this._polygons;
  657. const vertices = [];
  658. const indices = [];
  659. const normals = [];
  660. let uvs = null;
  661. let vertColors = null;
  662. const vertex = Vector3.Zero();
  663. const normal = Vector3.Zero();
  664. const uv = Vector2.Zero();
  665. const vertColor = new Color4(0, 0, 0, 0);
  666. const polygonIndices = [0, 0, 0];
  667. const vertice_dict = {};
  668. let vertex_idx;
  669. for (let i = 0, il = polygons.length; i < il; i++) {
  670. const polygon = polygons[i];
  671. if (onBeforePolygonProcessing) {
  672. onBeforePolygonProcessing(polygon);
  673. }
  674. for (let j = 2, jl = polygon.vertices.length; j < jl; j++) {
  675. polygonIndices[0] = 0;
  676. polygonIndices[1] = j - 1;
  677. polygonIndices[2] = j;
  678. for (let k = 0; k < 3; k++) {
  679. vertex.copyFrom(polygon.vertices[polygonIndices[k]].pos);
  680. normal.copyFrom(polygon.vertices[polygonIndices[k]].normal);
  681. if (polygon.vertices[polygonIndices[k]].uv) {
  682. if (!uvs) {
  683. uvs = [];
  684. }
  685. uv.copyFrom(polygon.vertices[polygonIndices[k]].uv);
  686. }
  687. if (polygon.vertices[polygonIndices[k]].vertColor) {
  688. if (!vertColors) {
  689. vertColors = [];
  690. }
  691. vertColor.copyFrom(polygon.vertices[polygonIndices[k]].vertColor);
  692. }
  693. const localVertex = Vector3.TransformCoordinates(vertex, matrix);
  694. const localNormal = Vector3.TransformNormal(normal, matrix);
  695. vertex_idx = vertice_dict[localVertex.x + "," + localVertex.y + "," + localVertex.z];
  696. let areUvsDifferent = false;
  697. if (uvs && !(uvs[vertex_idx * 2] === uv.x || uvs[vertex_idx * 2 + 1] === uv.y)) {
  698. areUvsDifferent = true;
  699. }
  700. let areColorsDifferent = false;
  701. if (vertColors &&
  702. !(vertColors[vertex_idx * 4] === vertColor.r ||
  703. vertColors[vertex_idx * 4 + 1] === vertColor.g ||
  704. vertColors[vertex_idx * 4 + 2] === vertColor.b ||
  705. vertColors[vertex_idx * 4 + 3] === vertColor.a)) {
  706. areColorsDifferent = true;
  707. }
  708. // Check if 2 points can be merged
  709. if (!(typeof vertex_idx !== "undefined" &&
  710. normals[vertex_idx * 3] === localNormal.x &&
  711. normals[vertex_idx * 3 + 1] === localNormal.y &&
  712. normals[vertex_idx * 3 + 2] === localNormal.z) ||
  713. areUvsDifferent ||
  714. areColorsDifferent) {
  715. vertices.push(localVertex.x, localVertex.y, localVertex.z);
  716. if (uvs) {
  717. uvs.push(uv.x, uv.y);
  718. }
  719. normals.push(normal.x, normal.y, normal.z);
  720. if (vertColors) {
  721. vertColors.push(vertColor.r, vertColor.g, vertColor.b, vertColor.a);
  722. }
  723. vertex_idx = vertice_dict[localVertex.x + "," + localVertex.y + "," + localVertex.z] = vertices.length / 3 - 1;
  724. }
  725. indices.push(vertex_idx);
  726. if (onAfterPolygonProcessing) {
  727. onAfterPolygonProcessing();
  728. }
  729. }
  730. }
  731. }
  732. const result = new VertexData();
  733. result.positions = vertices;
  734. result.normals = normals;
  735. if (uvs) {
  736. result.uvs = uvs;
  737. }
  738. if (vertColors) {
  739. result.colors = vertColors;
  740. }
  741. result.indices = indices;
  742. return result;
  743. }
  744. /**
  745. * Build Raw mesh from CSG
  746. * Coordinates here are in world space
  747. * @param name The name of the mesh geometry
  748. * @param scene The Scene
  749. * @param keepSubMeshes Specifies if the submeshes should be kept
  750. * @returns A new Mesh
  751. */
  752. buildMeshGeometry(name, scene, keepSubMeshes) {
  753. const mesh = new Mesh(name, scene);
  754. const polygons = this._polygons;
  755. let currentIndex = 0;
  756. const subMeshDict = {};
  757. let subMeshObj;
  758. if (keepSubMeshes) {
  759. // Sort Polygons, since subMeshes are indices range
  760. polygons.sort((a, b) => {
  761. if (a.shared.meshId === b.shared.meshId) {
  762. return a.shared.subMeshId - b.shared.subMeshId;
  763. }
  764. else {
  765. return a.shared.meshId - b.shared.meshId;
  766. }
  767. });
  768. }
  769. const vertexData = this.toVertexData((polygon) => {
  770. // Building SubMeshes
  771. if (!subMeshDict[polygon.shared.meshId]) {
  772. subMeshDict[polygon.shared.meshId] = {};
  773. }
  774. if (!subMeshDict[polygon.shared.meshId][polygon.shared.subMeshId]) {
  775. subMeshDict[polygon.shared.meshId][polygon.shared.subMeshId] = {
  776. indexStart: +Infinity,
  777. indexEnd: -Infinity,
  778. materialIndex: polygon.shared.materialIndex,
  779. };
  780. }
  781. subMeshObj = subMeshDict[polygon.shared.meshId][polygon.shared.subMeshId];
  782. }, () => {
  783. subMeshObj.indexStart = Math.min(currentIndex, subMeshObj.indexStart);
  784. subMeshObj.indexEnd = Math.max(currentIndex, subMeshObj.indexEnd);
  785. currentIndex++;
  786. });
  787. vertexData.applyToMesh(mesh);
  788. if (keepSubMeshes) {
  789. // We offset the materialIndex by the previous number of materials in the CSG mixed meshes
  790. let materialIndexOffset = 0, materialMaxIndex;
  791. mesh.subMeshes = [];
  792. for (const m in subMeshDict) {
  793. materialMaxIndex = -1;
  794. for (const sm in subMeshDict[m]) {
  795. subMeshObj = subMeshDict[m][sm];
  796. SubMesh.CreateFromIndices(subMeshObj.materialIndex + materialIndexOffset, subMeshObj.indexStart, subMeshObj.indexEnd - subMeshObj.indexStart + 1, mesh);
  797. materialMaxIndex = Math.max(subMeshObj.materialIndex, materialMaxIndex);
  798. }
  799. materialIndexOffset += ++materialMaxIndex;
  800. }
  801. }
  802. return mesh;
  803. }
  804. /**
  805. * Build Mesh from CSG taking material and transforms into account
  806. * @param name The name of the Mesh
  807. * @param material The material of the Mesh
  808. * @param scene The Scene
  809. * @param keepSubMeshes Specifies if submeshes should be kept
  810. * @returns The new Mesh
  811. */
  812. toMesh(name, material = null, scene, keepSubMeshes) {
  813. const mesh = this.buildMeshGeometry(name, scene, keepSubMeshes);
  814. mesh.material = material;
  815. mesh.position.copyFrom(this.position);
  816. mesh.rotation.copyFrom(this.rotation);
  817. if (this.rotationQuaternion) {
  818. mesh.rotationQuaternion = this.rotationQuaternion.clone();
  819. }
  820. mesh.scaling.copyFrom(this.scaling);
  821. mesh.computeWorldMatrix(true);
  822. return mesh;
  823. }
  824. }
  825. //# sourceMappingURL=csg.js.map