meshSimplification.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  1. import { Vector3 } 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 { AsyncLoop } from "../Misc/tools.js";
  6. import { Epsilon } from "../Maths/math.constants.js";
  7. /**
  8. * Class used to specify simplification options
  9. * @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/simplifyingMeshes
  10. */
  11. export class SimplificationSettings {
  12. /**
  13. * Creates a SimplificationSettings
  14. * @param quality expected quality
  15. * @param distance distance when this optimized version should be used
  16. * @param optimizeMesh already optimized mesh
  17. */
  18. constructor(
  19. /** expected quality */
  20. quality,
  21. /** distance when this optimized version should be used */
  22. distance,
  23. /** already optimized mesh */
  24. optimizeMesh) {
  25. this.quality = quality;
  26. this.distance = distance;
  27. this.optimizeMesh = optimizeMesh;
  28. }
  29. }
  30. /**
  31. * Queue used to order the simplification tasks
  32. * @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/simplifyingMeshes
  33. */
  34. export class SimplificationQueue {
  35. /**
  36. * Creates a new queue
  37. */
  38. constructor() {
  39. this.running = false;
  40. this._simplificationArray = [];
  41. }
  42. /**
  43. * Adds a new simplification task
  44. * @param task defines a task to add
  45. */
  46. addTask(task) {
  47. this._simplificationArray.push(task);
  48. }
  49. /**
  50. * Execute next task
  51. */
  52. executeNext() {
  53. const task = this._simplificationArray.pop();
  54. if (task) {
  55. this.running = true;
  56. this.runSimplification(task);
  57. }
  58. else {
  59. this.running = false;
  60. }
  61. }
  62. /**
  63. * Execute a simplification task
  64. * @param task defines the task to run
  65. */
  66. runSimplification(task) {
  67. if (task.parallelProcessing) {
  68. //parallel simplifier
  69. task.settings.forEach((setting) => {
  70. const simplifier = this._getSimplifier(task);
  71. simplifier.simplify(setting, (newMesh) => {
  72. if (setting.distance !== undefined) {
  73. task.mesh.addLODLevel(setting.distance, newMesh);
  74. }
  75. newMesh.isVisible = true;
  76. //check if it is the last
  77. if (setting.quality === task.settings[task.settings.length - 1].quality && task.successCallback) {
  78. //all done, run the success callback.
  79. task.successCallback();
  80. }
  81. this.executeNext();
  82. });
  83. });
  84. }
  85. else {
  86. //single simplifier.
  87. const simplifier = this._getSimplifier(task);
  88. const runDecimation = (setting, callback) => {
  89. simplifier.simplify(setting, (newMesh) => {
  90. if (setting.distance !== undefined) {
  91. task.mesh.addLODLevel(setting.distance, newMesh);
  92. }
  93. newMesh.isVisible = true;
  94. //run the next quality level
  95. callback();
  96. });
  97. };
  98. AsyncLoop.Run(task.settings.length, (loop) => {
  99. runDecimation(task.settings[loop.index], () => {
  100. loop.executeNext();
  101. });
  102. }, () => {
  103. //execution ended, run the success callback.
  104. if (task.successCallback) {
  105. task.successCallback();
  106. }
  107. this.executeNext();
  108. });
  109. }
  110. }
  111. _getSimplifier(task) {
  112. switch (task.simplificationType) {
  113. case SimplificationType.QUADRATIC:
  114. default:
  115. return new QuadraticErrorSimplification(task.mesh);
  116. }
  117. }
  118. }
  119. /**
  120. * The implemented types of simplification
  121. * At the moment only Quadratic Error Decimation is implemented
  122. * @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/simplifyingMeshes
  123. */
  124. export var SimplificationType;
  125. (function (SimplificationType) {
  126. /** Quadratic error decimation */
  127. SimplificationType[SimplificationType["QUADRATIC"] = 0] = "QUADRATIC";
  128. })(SimplificationType || (SimplificationType = {}));
  129. class DecimationTriangle {
  130. constructor(_vertices) {
  131. this._vertices = _vertices;
  132. this.error = new Array(4);
  133. this.deleted = false;
  134. this.isDirty = false;
  135. this.deletePending = false;
  136. this.borderFactor = 0;
  137. }
  138. }
  139. class DecimationVertex {
  140. constructor(position, id) {
  141. this.position = position;
  142. this.id = id;
  143. this.isBorder = true;
  144. this.q = new QuadraticMatrix();
  145. this.triangleCount = 0;
  146. this.triangleStart = 0;
  147. this.originalOffsets = [];
  148. }
  149. updatePosition(newPosition) {
  150. this.position.copyFrom(newPosition);
  151. }
  152. }
  153. class QuadraticMatrix {
  154. constructor(data) {
  155. this.data = new Array(10);
  156. for (let i = 0; i < 10; ++i) {
  157. if (data && data[i]) {
  158. this.data[i] = data[i];
  159. }
  160. else {
  161. this.data[i] = 0;
  162. }
  163. }
  164. }
  165. det(a11, a12, a13, a21, a22, a23, a31, a32, a33) {
  166. const det = this.data[a11] * this.data[a22] * this.data[a33] +
  167. this.data[a13] * this.data[a21] * this.data[a32] +
  168. this.data[a12] * this.data[a23] * this.data[a31] -
  169. this.data[a13] * this.data[a22] * this.data[a31] -
  170. this.data[a11] * this.data[a23] * this.data[a32] -
  171. this.data[a12] * this.data[a21] * this.data[a33];
  172. return det;
  173. }
  174. addInPlace(matrix) {
  175. for (let i = 0; i < 10; ++i) {
  176. this.data[i] += matrix.data[i];
  177. }
  178. }
  179. addArrayInPlace(data) {
  180. for (let i = 0; i < 10; ++i) {
  181. this.data[i] += data[i];
  182. }
  183. }
  184. add(matrix) {
  185. const m = new QuadraticMatrix();
  186. for (let i = 0; i < 10; ++i) {
  187. m.data[i] = this.data[i] + matrix.data[i];
  188. }
  189. return m;
  190. }
  191. static FromData(a, b, c, d) {
  192. return new QuadraticMatrix(QuadraticMatrix.DataFromNumbers(a, b, c, d));
  193. }
  194. //returning an array to avoid garbage collection
  195. static DataFromNumbers(a, b, c, d) {
  196. return [a * a, a * b, a * c, a * d, b * b, b * c, b * d, c * c, c * d, d * d];
  197. }
  198. }
  199. class Reference {
  200. constructor(vertexId, triangleId) {
  201. this.vertexId = vertexId;
  202. this.triangleId = triangleId;
  203. }
  204. }
  205. /**
  206. * An implementation of the Quadratic Error simplification algorithm.
  207. * Original paper : http://www1.cs.columbia.edu/~cs4162/html05s/garland97.pdf
  208. * Ported mostly from QSlim and http://voxels.blogspot.de/2014/05/quadric-mesh-simplification-with-source.html to babylon JS
  209. * @author RaananW
  210. * @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/simplifyingMeshes
  211. */
  212. export class QuadraticErrorSimplification {
  213. /**
  214. * Creates a new QuadraticErrorSimplification
  215. * @param _mesh defines the target mesh
  216. */
  217. constructor(_mesh) {
  218. this._mesh = _mesh;
  219. /** Gets or sets the number pf sync iterations */
  220. this.syncIterations = 5000;
  221. this.aggressiveness = 7;
  222. this.decimationIterations = 100;
  223. this.boundingBoxEpsilon = Epsilon;
  224. }
  225. /**
  226. * Simplification of a given mesh according to the given settings.
  227. * Since this requires computation, it is assumed that the function runs async.
  228. * @param settings The settings of the simplification, including quality and distance
  229. * @param successCallback A callback that will be called after the mesh was simplified.
  230. */
  231. simplify(settings, successCallback) {
  232. this._initDecimatedMesh();
  233. //iterating through the submeshes array, one after the other.
  234. AsyncLoop.Run(this._mesh.subMeshes.length, (loop) => {
  235. this._initWithMesh(loop.index, () => {
  236. this._runDecimation(settings, loop.index, () => {
  237. loop.executeNext();
  238. });
  239. }, settings.optimizeMesh);
  240. }, () => {
  241. setTimeout(() => {
  242. successCallback(this._reconstructedMesh);
  243. }, 0);
  244. });
  245. }
  246. _runDecimation(settings, submeshIndex, successCallback) {
  247. const targetCount = ~~(this._triangles.length * settings.quality);
  248. let deletedTriangles = 0;
  249. const triangleCount = this._triangles.length;
  250. const iterationFunction = (iteration, callback) => {
  251. setTimeout(() => {
  252. if (iteration % 5 === 0) {
  253. this._updateMesh(iteration === 0);
  254. }
  255. for (let i = 0; i < this._triangles.length; ++i) {
  256. this._triangles[i].isDirty = false;
  257. }
  258. const threshold = 0.000000001 * Math.pow(iteration + 3, this.aggressiveness);
  259. const trianglesIterator = (i) => {
  260. const tIdx = ~~((this._triangles.length / 2 + i) % this._triangles.length);
  261. const t = this._triangles[tIdx];
  262. if (!t) {
  263. return;
  264. }
  265. if (t.error[3] > threshold || t.deleted || t.isDirty) {
  266. return;
  267. }
  268. for (let j = 0; j < 3; ++j) {
  269. if (t.error[j] < threshold) {
  270. const deleted0 = [];
  271. const deleted1 = [];
  272. const v0 = t._vertices[j];
  273. const v1 = t._vertices[(j + 1) % 3];
  274. if (v0.isBorder || v1.isBorder) {
  275. continue;
  276. }
  277. const p = Vector3.Zero();
  278. // var n = Vector3.Zero();
  279. // var uv = Vector2.Zero();
  280. // var color = new Color4(0, 0, 0, 1);
  281. this._calculateError(v0, v1, p);
  282. const delTr = [];
  283. if (this._isFlipped(v0, v1, p, deleted0, delTr)) {
  284. continue;
  285. }
  286. if (this._isFlipped(v1, v0, p, deleted1, delTr)) {
  287. continue;
  288. }
  289. if (deleted0.indexOf(true) < 0 || deleted1.indexOf(true) < 0) {
  290. continue;
  291. }
  292. const uniqueArray = [];
  293. delTr.forEach((deletedT) => {
  294. if (uniqueArray.indexOf(deletedT) === -1) {
  295. deletedT.deletePending = true;
  296. uniqueArray.push(deletedT);
  297. }
  298. });
  299. if (uniqueArray.length % 2 !== 0) {
  300. continue;
  301. }
  302. v0.q = v1.q.add(v0.q);
  303. v0.updatePosition(p);
  304. const tStart = this._references.length;
  305. deletedTriangles = this._updateTriangles(v0, v0, deleted0, deletedTriangles);
  306. deletedTriangles = this._updateTriangles(v0, v1, deleted1, deletedTriangles);
  307. const tCount = this._references.length - tStart;
  308. if (tCount <= v0.triangleCount) {
  309. if (tCount) {
  310. for (let c = 0; c < tCount; c++) {
  311. this._references[v0.triangleStart + c] = this._references[tStart + c];
  312. }
  313. }
  314. }
  315. else {
  316. v0.triangleStart = tStart;
  317. }
  318. v0.triangleCount = tCount;
  319. break;
  320. }
  321. }
  322. };
  323. AsyncLoop.SyncAsyncForLoop(this._triangles.length, this.syncIterations, trianglesIterator, callback, () => {
  324. return triangleCount - deletedTriangles <= targetCount;
  325. });
  326. }, 0);
  327. };
  328. AsyncLoop.Run(this.decimationIterations, (loop) => {
  329. if (triangleCount - deletedTriangles <= targetCount) {
  330. loop.breakLoop();
  331. }
  332. else {
  333. iterationFunction(loop.index, () => {
  334. loop.executeNext();
  335. });
  336. }
  337. }, () => {
  338. setTimeout(() => {
  339. //reconstruct this part of the mesh
  340. this._reconstructMesh(submeshIndex);
  341. successCallback();
  342. }, 0);
  343. });
  344. }
  345. _initWithMesh(submeshIndex, callback, optimizeMesh) {
  346. this._vertices = [];
  347. this._triangles = [];
  348. const positionData = this._mesh.getVerticesData(VertexBuffer.PositionKind);
  349. const indices = this._mesh.getIndices();
  350. const submesh = this._mesh.subMeshes[submeshIndex];
  351. const findInVertices = (positionToSearch) => {
  352. if (optimizeMesh) {
  353. for (let ii = 0; ii < this._vertices.length; ++ii) {
  354. if (this._vertices[ii].position.equalsWithEpsilon(positionToSearch, 0.0001)) {
  355. return this._vertices[ii];
  356. }
  357. }
  358. }
  359. return null;
  360. };
  361. const vertexReferences = [];
  362. const vertexInit = (i) => {
  363. if (!positionData) {
  364. return;
  365. }
  366. const offset = i + submesh.verticesStart;
  367. const position = Vector3.FromArray(positionData, offset * 3);
  368. const vertex = findInVertices(position) || new DecimationVertex(position, this._vertices.length);
  369. vertex.originalOffsets.push(offset);
  370. if (vertex.id === this._vertices.length) {
  371. this._vertices.push(vertex);
  372. }
  373. vertexReferences.push(vertex.id);
  374. };
  375. //var totalVertices = mesh.getTotalVertices();
  376. const totalVertices = submesh.verticesCount;
  377. AsyncLoop.SyncAsyncForLoop(totalVertices, (this.syncIterations / 4) >> 0, vertexInit, () => {
  378. const indicesInit = (i) => {
  379. if (!indices) {
  380. return;
  381. }
  382. const offset = submesh.indexStart / 3 + i;
  383. const pos = offset * 3;
  384. const i0 = indices[pos + 0];
  385. const i1 = indices[pos + 1];
  386. const i2 = indices[pos + 2];
  387. const v0 = this._vertices[vertexReferences[i0 - submesh.verticesStart]];
  388. const v1 = this._vertices[vertexReferences[i1 - submesh.verticesStart]];
  389. const v2 = this._vertices[vertexReferences[i2 - submesh.verticesStart]];
  390. const triangle = new DecimationTriangle([v0, v1, v2]);
  391. triangle.originalOffset = pos;
  392. this._triangles.push(triangle);
  393. };
  394. AsyncLoop.SyncAsyncForLoop(submesh.indexCount / 3, this.syncIterations, indicesInit, () => {
  395. this._init(callback);
  396. });
  397. });
  398. }
  399. _init(callback) {
  400. const triangleInit1 = (i) => {
  401. const t = this._triangles[i];
  402. t.normal = Vector3.Cross(t._vertices[1].position.subtract(t._vertices[0].position), t._vertices[2].position.subtract(t._vertices[0].position)).normalize();
  403. for (let j = 0; j < 3; j++) {
  404. t._vertices[j].q.addArrayInPlace(QuadraticMatrix.DataFromNumbers(t.normal.x, t.normal.y, t.normal.z, -Vector3.Dot(t.normal, t._vertices[0].position)));
  405. }
  406. };
  407. AsyncLoop.SyncAsyncForLoop(this._triangles.length, this.syncIterations, triangleInit1, () => {
  408. const triangleInit2 = (i) => {
  409. const t = this._triangles[i];
  410. for (let j = 0; j < 3; ++j) {
  411. t.error[j] = this._calculateError(t._vertices[j], t._vertices[(j + 1) % 3]);
  412. }
  413. t.error[3] = Math.min(t.error[0], t.error[1], t.error[2]);
  414. };
  415. AsyncLoop.SyncAsyncForLoop(this._triangles.length, this.syncIterations, triangleInit2, () => {
  416. callback();
  417. });
  418. });
  419. }
  420. _reconstructMesh(submeshIndex) {
  421. const newTriangles = [];
  422. let i;
  423. for (i = 0; i < this._vertices.length; ++i) {
  424. this._vertices[i].triangleCount = 0;
  425. }
  426. let t;
  427. let j;
  428. for (i = 0; i < this._triangles.length; ++i) {
  429. if (!this._triangles[i].deleted) {
  430. t = this._triangles[i];
  431. for (j = 0; j < 3; ++j) {
  432. t._vertices[j].triangleCount = 1;
  433. }
  434. newTriangles.push(t);
  435. }
  436. }
  437. const newPositionData = (this._reconstructedMesh.getVerticesData(VertexBuffer.PositionKind) || []);
  438. const newNormalData = (this._reconstructedMesh.getVerticesData(VertexBuffer.NormalKind) || []);
  439. const newUVsData = (this._reconstructedMesh.getVerticesData(VertexBuffer.UVKind) || []);
  440. const newColorsData = (this._reconstructedMesh.getVerticesData(VertexBuffer.ColorKind) || []);
  441. const normalData = this._mesh.getVerticesData(VertexBuffer.NormalKind);
  442. const uvs = this._mesh.getVerticesData(VertexBuffer.UVKind);
  443. const colorsData = this._mesh.getVerticesData(VertexBuffer.ColorKind);
  444. let vertexCount = 0;
  445. for (i = 0; i < this._vertices.length; ++i) {
  446. const vertex = this._vertices[i];
  447. vertex.id = vertexCount;
  448. if (vertex.triangleCount) {
  449. vertex.originalOffsets.forEach((originalOffset) => {
  450. newPositionData.push(vertex.position.x);
  451. newPositionData.push(vertex.position.y);
  452. newPositionData.push(vertex.position.z);
  453. if (normalData && normalData.length) {
  454. newNormalData.push(normalData[originalOffset * 3]);
  455. newNormalData.push(normalData[originalOffset * 3 + 1]);
  456. newNormalData.push(normalData[originalOffset * 3 + 2]);
  457. }
  458. if (uvs && uvs.length) {
  459. newUVsData.push(uvs[originalOffset * 2]);
  460. newUVsData.push(uvs[originalOffset * 2 + 1]);
  461. }
  462. if (colorsData && colorsData.length) {
  463. newColorsData.push(colorsData[originalOffset * 4]);
  464. newColorsData.push(colorsData[originalOffset * 4 + 1]);
  465. newColorsData.push(colorsData[originalOffset * 4 + 2]);
  466. newColorsData.push(colorsData[originalOffset * 4 + 3]);
  467. }
  468. ++vertexCount;
  469. });
  470. }
  471. }
  472. const startingIndex = this._reconstructedMesh.getTotalIndices();
  473. const startingVertex = this._reconstructedMesh.getTotalVertices();
  474. const submeshesArray = this._reconstructedMesh.subMeshes;
  475. this._reconstructedMesh.subMeshes = [];
  476. const newIndicesArray = this._reconstructedMesh.getIndices(); //[];
  477. const originalIndices = this._mesh.getIndices();
  478. for (i = 0; i < newTriangles.length; ++i) {
  479. t = newTriangles[i]; //now get the new referencing point for each vertex
  480. [0, 1, 2].forEach((idx) => {
  481. const id = originalIndices[t.originalOffset + idx];
  482. let offset = t._vertices[idx].originalOffsets.indexOf(id);
  483. if (offset < 0) {
  484. offset = 0;
  485. }
  486. newIndicesArray.push(t._vertices[idx].id + offset + startingVertex);
  487. });
  488. }
  489. //overwriting the old vertex buffers and indices.
  490. this._reconstructedMesh.setIndices(newIndicesArray);
  491. this._reconstructedMesh.setVerticesData(VertexBuffer.PositionKind, newPositionData);
  492. if (newNormalData.length > 0) {
  493. this._reconstructedMesh.setVerticesData(VertexBuffer.NormalKind, newNormalData);
  494. }
  495. if (newUVsData.length > 0) {
  496. this._reconstructedMesh.setVerticesData(VertexBuffer.UVKind, newUVsData);
  497. }
  498. if (newColorsData.length > 0) {
  499. this._reconstructedMesh.setVerticesData(VertexBuffer.ColorKind, newColorsData);
  500. }
  501. //create submesh
  502. const originalSubmesh = this._mesh.subMeshes[submeshIndex];
  503. if (submeshIndex > 0) {
  504. this._reconstructedMesh.subMeshes = [];
  505. submeshesArray.forEach((submesh) => {
  506. SubMesh.AddToMesh(submesh.materialIndex, submesh.verticesStart, submesh.verticesCount,
  507. /* 0, newPositionData.length/3, */ submesh.indexStart, submesh.indexCount, submesh.getMesh());
  508. });
  509. SubMesh.AddToMesh(originalSubmesh.materialIndex, startingVertex, vertexCount,
  510. /* 0, newPositionData.length / 3, */ startingIndex, newTriangles.length * 3, this._reconstructedMesh);
  511. }
  512. }
  513. _initDecimatedMesh() {
  514. this._reconstructedMesh = new Mesh(this._mesh.name + "Decimated", this._mesh.getScene());
  515. this._reconstructedMesh.material = this._mesh.material;
  516. this._reconstructedMesh.parent = this._mesh.parent;
  517. this._reconstructedMesh.isVisible = false;
  518. this._reconstructedMesh.renderingGroupId = this._mesh.renderingGroupId;
  519. }
  520. _isFlipped(vertex1, vertex2, point, deletedArray, delTr) {
  521. for (let i = 0; i < vertex1.triangleCount; ++i) {
  522. const t = this._triangles[this._references[vertex1.triangleStart + i].triangleId];
  523. if (t.deleted) {
  524. continue;
  525. }
  526. const s = this._references[vertex1.triangleStart + i].vertexId;
  527. const v1 = t._vertices[(s + 1) % 3];
  528. const v2 = t._vertices[(s + 2) % 3];
  529. if (v1 === vertex2 || v2 === vertex2) {
  530. deletedArray[i] = true;
  531. delTr.push(t);
  532. continue;
  533. }
  534. let d1 = v1.position.subtract(point);
  535. d1 = d1.normalize();
  536. let d2 = v2.position.subtract(point);
  537. d2 = d2.normalize();
  538. if (Math.abs(Vector3.Dot(d1, d2)) > 0.999) {
  539. return true;
  540. }
  541. const normal = Vector3.Cross(d1, d2).normalize();
  542. deletedArray[i] = false;
  543. if (Vector3.Dot(normal, t.normal) < 0.2) {
  544. return true;
  545. }
  546. }
  547. return false;
  548. }
  549. _updateTriangles(origVertex, vertex, deletedArray, deletedTriangles) {
  550. let newDeleted = deletedTriangles;
  551. for (let i = 0; i < vertex.triangleCount; ++i) {
  552. const ref = this._references[vertex.triangleStart + i];
  553. const t = this._triangles[ref.triangleId];
  554. if (t.deleted) {
  555. continue;
  556. }
  557. if (deletedArray[i] && t.deletePending) {
  558. t.deleted = true;
  559. newDeleted++;
  560. continue;
  561. }
  562. t._vertices[ref.vertexId] = origVertex;
  563. t.isDirty = true;
  564. t.error[0] = this._calculateError(t._vertices[0], t._vertices[1]) + t.borderFactor / 2;
  565. t.error[1] = this._calculateError(t._vertices[1], t._vertices[2]) + t.borderFactor / 2;
  566. t.error[2] = this._calculateError(t._vertices[2], t._vertices[0]) + t.borderFactor / 2;
  567. t.error[3] = Math.min(t.error[0], t.error[1], t.error[2]);
  568. this._references.push(ref);
  569. }
  570. return newDeleted;
  571. }
  572. _identifyBorder() {
  573. for (let i = 0; i < this._vertices.length; ++i) {
  574. const vCount = [];
  575. const vId = [];
  576. const v = this._vertices[i];
  577. let j;
  578. for (j = 0; j < v.triangleCount; ++j) {
  579. const triangle = this._triangles[this._references[v.triangleStart + j].triangleId];
  580. for (let ii = 0; ii < 3; ii++) {
  581. let ofs = 0;
  582. const vv = triangle._vertices[ii];
  583. while (ofs < vCount.length) {
  584. if (vId[ofs] === vv.id) {
  585. break;
  586. }
  587. ++ofs;
  588. }
  589. if (ofs === vCount.length) {
  590. vCount.push(1);
  591. vId.push(vv.id);
  592. }
  593. else {
  594. vCount[ofs]++;
  595. }
  596. }
  597. }
  598. for (j = 0; j < vCount.length; ++j) {
  599. if (vCount[j] === 1) {
  600. this._vertices[vId[j]].isBorder = true;
  601. }
  602. else {
  603. this._vertices[vId[j]].isBorder = false;
  604. }
  605. }
  606. }
  607. }
  608. _updateMesh(identifyBorders = false) {
  609. let i;
  610. if (!identifyBorders) {
  611. const newTrianglesVector = [];
  612. for (i = 0; i < this._triangles.length; ++i) {
  613. if (!this._triangles[i].deleted) {
  614. newTrianglesVector.push(this._triangles[i]);
  615. }
  616. }
  617. this._triangles = newTrianglesVector;
  618. }
  619. for (i = 0; i < this._vertices.length; ++i) {
  620. this._vertices[i].triangleCount = 0;
  621. this._vertices[i].triangleStart = 0;
  622. }
  623. let t;
  624. let j;
  625. let v;
  626. for (i = 0; i < this._triangles.length; ++i) {
  627. t = this._triangles[i];
  628. for (j = 0; j < 3; ++j) {
  629. v = t._vertices[j];
  630. v.triangleCount++;
  631. }
  632. }
  633. let tStart = 0;
  634. for (i = 0; i < this._vertices.length; ++i) {
  635. this._vertices[i].triangleStart = tStart;
  636. tStart += this._vertices[i].triangleCount;
  637. this._vertices[i].triangleCount = 0;
  638. }
  639. const newReferences = new Array(this._triangles.length * 3);
  640. for (i = 0; i < this._triangles.length; ++i) {
  641. t = this._triangles[i];
  642. for (j = 0; j < 3; ++j) {
  643. v = t._vertices[j];
  644. newReferences[v.triangleStart + v.triangleCount] = new Reference(j, i);
  645. v.triangleCount++;
  646. }
  647. }
  648. this._references = newReferences;
  649. if (identifyBorders) {
  650. this._identifyBorder();
  651. }
  652. }
  653. _vertexError(q, point) {
  654. const x = point.x;
  655. const y = point.y;
  656. const z = point.z;
  657. return (q.data[0] * x * x +
  658. 2 * q.data[1] * x * y +
  659. 2 * q.data[2] * x * z +
  660. 2 * q.data[3] * x +
  661. q.data[4] * y * y +
  662. 2 * q.data[5] * y * z +
  663. 2 * q.data[6] * y +
  664. q.data[7] * z * z +
  665. 2 * q.data[8] * z +
  666. q.data[9]);
  667. }
  668. _calculateError(vertex1, vertex2, pointResult) {
  669. const q = vertex1.q.add(vertex2.q);
  670. const border = vertex1.isBorder && vertex2.isBorder;
  671. let error = 0;
  672. const qDet = q.det(0, 1, 2, 1, 4, 5, 2, 5, 7);
  673. if (qDet !== 0 && !border) {
  674. if (!pointResult) {
  675. pointResult = Vector3.Zero();
  676. }
  677. pointResult.x = (-1 / qDet) * q.det(1, 2, 3, 4, 5, 6, 5, 7, 8);
  678. pointResult.y = (1 / qDet) * q.det(0, 2, 3, 1, 5, 6, 2, 7, 8);
  679. pointResult.z = (-1 / qDet) * q.det(0, 1, 3, 1, 4, 6, 2, 5, 8);
  680. error = this._vertexError(q, pointResult);
  681. }
  682. else {
  683. const p3 = vertex1.position.add(vertex2.position).divide(new Vector3(2, 2, 2));
  684. //var norm3 = (vertex1.normal.add(vertex2.normal)).divide(new Vector3(2, 2, 2)).normalize();
  685. const error1 = this._vertexError(q, vertex1.position);
  686. const error2 = this._vertexError(q, vertex2.position);
  687. const error3 = this._vertexError(q, p3);
  688. error = Math.min(error1, error2, error3);
  689. if (error === error1) {
  690. if (pointResult) {
  691. pointResult.copyFrom(vertex1.position);
  692. }
  693. }
  694. else if (error === error2) {
  695. if (pointResult) {
  696. pointResult.copyFrom(vertex2.position);
  697. }
  698. }
  699. else {
  700. if (pointResult) {
  701. pointResult.copyFrom(p3);
  702. }
  703. }
  704. }
  705. return error;
  706. }
  707. }
  708. //# sourceMappingURL=meshSimplification.js.map