mesh.ts 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  1. /**
  2. * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. * @author David Sehnal <david.sehnal@gmail.com>
  6. */
  7. import { ValueCell } from '../../../mol-util';
  8. import { Vec3, Mat4, Mat3, Vec4 } from '../../../mol-math/linear-algebra';
  9. import { Sphere3D } from '../../../mol-math/geometry';
  10. import { transformPositionArray, transformDirectionArray, computeIndexedVertexNormals, GroupMapping, createGroupMapping } from '../../util';
  11. import { GeometryUtils } from '../geometry';
  12. import { createMarkers } from '../marker-data';
  13. import { TransformData } from '../transform-data';
  14. import { LocationIterator, PositionLocation } from '../../util/location-iterator';
  15. import { createColors } from '../color-data';
  16. import { ChunkedArray, hashFnv32a, invertCantorPairing, sortedCantorPairing } from '../../../mol-data/util';
  17. import { ParamDefinition as PD } from '../../../mol-util/param-definition';
  18. import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
  19. import { Theme } from '../../../mol-theme/theme';
  20. import { MeshValues } from '../../../mol-gl/renderable/mesh';
  21. import { Color } from '../../../mol-util/color';
  22. import { BaseGeometry } from '../base';
  23. import { createEmptyOverpaint } from '../overpaint-data';
  24. import { createEmptyTransparency } from '../transparency-data';
  25. import { createEmptyClipping } from '../clipping-data';
  26. import { RenderableState } from '../../../mol-gl/renderable';
  27. import { arraySetAdd } from '../../../mol-util/array';
  28. import { degToRad } from '../../../mol-math/misc';
  29. import { createEmptySubstance } from '../substance-data';
  30. export interface Mesh {
  31. readonly kind: 'mesh',
  32. /** Number of vertices in the mesh */
  33. vertexCount: number,
  34. /** Number of triangles in the mesh */
  35. triangleCount: number,
  36. /** Vertex buffer as array of xyz values wrapped in a value cell */
  37. readonly vertexBuffer: ValueCell<Float32Array>,
  38. /** Index buffer as array of vertex index triplets wrapped in a value cell */
  39. readonly indexBuffer: ValueCell<Uint32Array>,
  40. /** Normal buffer as array of xyz values for each vertex wrapped in a value cell */
  41. readonly normalBuffer: ValueCell<Float32Array>,
  42. /** Group buffer as array of group ids for each vertex wrapped in a value cell */
  43. readonly groupBuffer: ValueCell<Float32Array>,
  44. /** Bounding sphere of the mesh */
  45. readonly boundingSphere: Sphere3D
  46. /** Maps group ids to vertex indices */
  47. readonly groupMapping: GroupMapping
  48. setBoundingSphere(boundingSphere: Sphere3D): void
  49. readonly meta: { [k: string]: unknown }
  50. }
  51. export namespace Mesh {
  52. export function create(vertices: Float32Array, indices: Uint32Array, normals: Float32Array, groups: Float32Array, vertexCount: number, triangleCount: number, mesh?: Mesh): Mesh {
  53. return mesh ?
  54. update(vertices, indices, normals, groups, vertexCount, triangleCount, mesh) :
  55. fromArrays(vertices, indices, normals, groups, vertexCount, triangleCount);
  56. }
  57. export function createEmpty(mesh?: Mesh): Mesh {
  58. const vb = mesh ? mesh.vertexBuffer.ref.value : new Float32Array(0);
  59. const ib = mesh ? mesh.indexBuffer.ref.value : new Uint32Array(0);
  60. const nb = mesh ? mesh.normalBuffer.ref.value : new Float32Array(0);
  61. const gb = mesh ? mesh.groupBuffer.ref.value : new Float32Array(0);
  62. return create(vb, ib, nb, gb, 0, 0, mesh);
  63. }
  64. function hashCode(mesh: Mesh) {
  65. return hashFnv32a([
  66. mesh.vertexCount, mesh.triangleCount,
  67. mesh.vertexBuffer.ref.version, mesh.indexBuffer.ref.version,
  68. mesh.normalBuffer.ref.version, mesh.groupBuffer.ref.version
  69. ]);
  70. }
  71. function fromArrays(vertices: Float32Array, indices: Uint32Array, normals: Float32Array, groups: Float32Array, vertexCount: number, triangleCount: number): Mesh {
  72. const boundingSphere = Sphere3D();
  73. let groupMapping: GroupMapping;
  74. let currentHash = -1;
  75. let currentGroup = -1;
  76. const mesh = {
  77. kind: 'mesh' as const,
  78. vertexCount,
  79. triangleCount,
  80. vertexBuffer: ValueCell.create(vertices),
  81. indexBuffer: ValueCell.create(indices),
  82. normalBuffer: ValueCell.create(normals),
  83. groupBuffer: ValueCell.create(groups),
  84. get boundingSphere() {
  85. const newHash = hashCode(mesh);
  86. if (newHash !== currentHash) {
  87. const b = calculateInvariantBoundingSphere(mesh.vertexBuffer.ref.value, mesh.vertexCount, 1);
  88. Sphere3D.copy(boundingSphere, b);
  89. currentHash = newHash;
  90. }
  91. return boundingSphere;
  92. },
  93. get groupMapping() {
  94. if (mesh.groupBuffer.ref.version !== currentGroup) {
  95. groupMapping = createGroupMapping(mesh.groupBuffer.ref.value, mesh.vertexCount);
  96. currentGroup = mesh.groupBuffer.ref.version;
  97. }
  98. return groupMapping;
  99. },
  100. setBoundingSphere(sphere: Sphere3D) {
  101. Sphere3D.copy(boundingSphere, sphere);
  102. currentHash = hashCode(mesh);
  103. },
  104. meta: {}
  105. };
  106. return mesh;
  107. }
  108. function update(vertices: Float32Array, indices: Uint32Array, normals: Float32Array, groups: Float32Array, vertexCount: number, triangleCount: number, mesh: Mesh) {
  109. mesh.vertexCount = vertexCount;
  110. mesh.triangleCount = triangleCount;
  111. ValueCell.update(mesh.vertexBuffer, vertices);
  112. ValueCell.update(mesh.indexBuffer, indices);
  113. ValueCell.update(mesh.normalBuffer, normals);
  114. ValueCell.update(mesh.groupBuffer, groups);
  115. return mesh;
  116. }
  117. export function computeNormals(mesh: Mesh) {
  118. const { vertexCount, triangleCount } = mesh;
  119. const vertices = mesh.vertexBuffer.ref.value;
  120. const indices = mesh.indexBuffer.ref.value;
  121. const normals = mesh.normalBuffer.ref.value.length >= vertexCount * 3
  122. ? mesh.normalBuffer.ref.value
  123. : new Float32Array(vertexCount * 3);
  124. if (normals === mesh.normalBuffer.ref.value) {
  125. normals.fill(0, 0, vertexCount * 3);
  126. }
  127. computeIndexedVertexNormals(vertices, indices, normals, vertexCount, triangleCount);
  128. ValueCell.update(mesh.normalBuffer, normals);
  129. }
  130. export function checkForDuplicateVertices(mesh: Mesh, fractionDigits = 3) {
  131. const v = mesh.vertexBuffer.ref.value;
  132. const map = new Map<string, number>();
  133. const hash = (v: Vec3, d: number) => `${v[0].toFixed(d)}|${v[1].toFixed(d)}|${v[2].toFixed(d)}`;
  134. let duplicates = 0;
  135. const a = Vec3();
  136. for (let i = 0, il = mesh.vertexCount; i < il; ++i) {
  137. Vec3.fromArray(a, v, i * 3);
  138. const k = hash(a, fractionDigits);
  139. const count = map.get(k);
  140. if (count !== undefined) {
  141. duplicates += 1;
  142. map.set(k, count + 1);
  143. } else {
  144. map.set(k, 1);
  145. }
  146. }
  147. return duplicates;
  148. }
  149. const tmpMat3 = Mat3();
  150. export function transform(mesh: Mesh, t: Mat4) {
  151. const v = mesh.vertexBuffer.ref.value;
  152. transformPositionArray(t, v, 0, mesh.vertexCount);
  153. if (!Mat4.isTranslationAndUniformScaling(t)) {
  154. const n = Mat3.directionTransform(tmpMat3, t);
  155. transformDirectionArray(n, mesh.normalBuffer.ref.value, 0, mesh.vertexCount);
  156. }
  157. ValueCell.update(mesh.vertexBuffer, v);
  158. }
  159. export type OriginalData = {
  160. indexBuffer: Uint32Array
  161. vertexCount: number
  162. triangleCount: number
  163. }
  164. /** Meshes may contain some original data in case any processing was done. */
  165. export function getOriginalData(x: Mesh | MeshValues) {
  166. const { originalData } = 'kind' in x ? x.meta : x.meta.ref.value as Mesh['meta'];
  167. return originalData as OriginalData | undefined;
  168. }
  169. /**
  170. * Ensure that each vertices of each triangle have the same group id.
  171. * Note that normals are copied over and can't be re-created from the new mesh.
  172. */
  173. export function uniformTriangleGroup(mesh: Mesh, splitTriangles = true) {
  174. const { indexBuffer, vertexBuffer, groupBuffer, normalBuffer, triangleCount, vertexCount } = mesh;
  175. const ib = indexBuffer.ref.value;
  176. const vb = vertexBuffer.ref.value;
  177. const gb = groupBuffer.ref.value;
  178. const nb = normalBuffer.ref.value;
  179. // new
  180. const index = ChunkedArray.create(Uint32Array, 3, 1024, triangleCount);
  181. // re-use
  182. const vertex = ChunkedArray.create(Float32Array, 3, 1024, vb);
  183. vertex.currentIndex = vertexCount * 3;
  184. vertex.elementCount = vertexCount;
  185. const normal = ChunkedArray.create(Float32Array, 3, 1024, nb);
  186. normal.currentIndex = vertexCount * 3;
  187. normal.elementCount = vertexCount;
  188. const group = ChunkedArray.create(Float32Array, 1, 1024, gb);
  189. group.currentIndex = vertexCount;
  190. group.elementCount = vertexCount;
  191. const vi = Vec3();
  192. const vj = Vec3();
  193. const vk = Vec3();
  194. const ni = Vec3();
  195. const nj = Vec3();
  196. const nk = Vec3();
  197. function add(i: number) {
  198. Vec3.fromArray(vi, vb, i * 3);
  199. Vec3.fromArray(ni, nb, i * 3);
  200. ChunkedArray.add3(vertex, vi[0], vi[1], vi[2]);
  201. ChunkedArray.add3(normal, ni[0], ni[1], ni[2]);
  202. }
  203. function addMid(i: number, j: number) {
  204. Vec3.fromArray(vi, vb, i * 3);
  205. Vec3.fromArray(vj, vb, j * 3);
  206. Vec3.scale(vi, Vec3.add(vi, vi, vj), 0.5);
  207. Vec3.fromArray(ni, nb, i * 3);
  208. Vec3.fromArray(nj, nb, j * 3);
  209. Vec3.scale(ni, Vec3.add(ni, ni, nj), 0.5);
  210. ChunkedArray.add3(vertex, vi[0], vi[1], vi[2]);
  211. ChunkedArray.add3(normal, ni[0], ni[1], ni[2]);
  212. }
  213. function addCenter(i: number, j: number, k: number) {
  214. Vec3.fromArray(vi, vb, i * 3);
  215. Vec3.fromArray(vj, vb, j * 3);
  216. Vec3.fromArray(vk, vb, k * 3);
  217. Vec3.scale(vi, Vec3.add(vi, Vec3.add(vi, vi, vj), vk), 1 / 3);
  218. Vec3.fromArray(ni, nb, i * 3);
  219. Vec3.fromArray(nj, nb, j * 3);
  220. Vec3.fromArray(nk, nb, k * 3);
  221. Vec3.scale(ni, Vec3.add(ni, Vec3.add(ni, ni, nj), nk), 1 / 3);
  222. ChunkedArray.add3(vertex, vi[0], vi[1], vi[2]);
  223. ChunkedArray.add3(normal, ni[0], ni[1], ni[2]);
  224. }
  225. function split2(i0: number, i1: number, i2: number, g0: number, g1: number) {
  226. ++newTriangleCount;
  227. add(i0); addMid(i0, i1); addMid(i0, i2);
  228. ChunkedArray.add3(index, newVertexCount, newVertexCount + 1, newVertexCount + 2);
  229. for (let j = 0; j < 3; ++j) ChunkedArray.add(group, g0);
  230. newVertexCount += 3;
  231. newTriangleCount += 2;
  232. add(i1); add(i2); addMid(i0, i1); addMid(i0, i2);
  233. ChunkedArray.add3(index, newVertexCount, newVertexCount + 1, newVertexCount + 3);
  234. ChunkedArray.add3(index, newVertexCount, newVertexCount + 3, newVertexCount + 2);
  235. for (let j = 0; j < 4; ++j) ChunkedArray.add(group, g1);
  236. newVertexCount += 4;
  237. }
  238. let newVertexCount = vertexCount;
  239. let newTriangleCount = 0;
  240. if (splitTriangles) {
  241. for (let i = 0, il = triangleCount; i < il; ++i) {
  242. const i0 = ib[i * 3], i1 = ib[i * 3 + 1], i2 = ib[i * 3 + 2];
  243. const g0 = gb[i0], g1 = gb[i1], g2 = gb[i2];
  244. if (g0 === g1 && g0 === g2) {
  245. ++newTriangleCount;
  246. ChunkedArray.add3(index, i0, i1, i2);
  247. } else if (g0 === g1) {
  248. split2(i2, i0, i1, g2, g0);
  249. } else if (g0 === g2) {
  250. split2(i1, i2, i0, g1, g2);
  251. } else if (g1 === g2) {
  252. split2(i0, i1, i2, g0, g1);
  253. } else {
  254. newTriangleCount += 2;
  255. add(i0); addMid(i0, i1); addMid(i0, i2); addCenter(i0, i1, i2);
  256. ChunkedArray.add3(index, newVertexCount, newVertexCount + 1, newVertexCount + 3);
  257. ChunkedArray.add3(index, newVertexCount, newVertexCount + 3, newVertexCount + 2);
  258. for (let j = 0; j < 4; ++j) ChunkedArray.add(group, g0);
  259. newVertexCount += 4;
  260. newTriangleCount += 2;
  261. add(i1); addMid(i1, i2); addMid(i1, i0); addCenter(i0, i1, i2);
  262. ChunkedArray.add3(index, newVertexCount, newVertexCount + 1, newVertexCount + 3);
  263. ChunkedArray.add3(index, newVertexCount, newVertexCount + 3, newVertexCount + 2);
  264. for (let j = 0; j < 4; ++j) ChunkedArray.add(group, g1);
  265. newVertexCount += 4;
  266. newTriangleCount += 2;
  267. add(i2); addMid(i2, i1); addMid(i2, i0); addCenter(i0, i1, i2);
  268. ChunkedArray.add3(index, newVertexCount + 3, newVertexCount + 1, newVertexCount);
  269. ChunkedArray.add3(index, newVertexCount + 2, newVertexCount + 3, newVertexCount);
  270. for (let j = 0; j < 4; ++j) ChunkedArray.add(group, g2);
  271. newVertexCount += 4;
  272. }
  273. }
  274. } else {
  275. for (let i = 0, il = triangleCount; i < il; ++i) {
  276. const i0 = ib[i * 3], i1 = ib[i * 3 + 1], i2 = ib[i * 3 + 2];
  277. const g0 = gb[i0], g1 = gb[i1], g2 = gb[i2];
  278. if (g0 !== g1 || g0 !== g2) {
  279. ++newTriangleCount;
  280. add(i0); add(i1); add(i2);
  281. ChunkedArray.add3(index, newVertexCount, newVertexCount + 1, newVertexCount + 2);
  282. const g = g1 === g2 ? g1 : g0;
  283. for (let j = 0; j < 3; ++j) ChunkedArray.add(group, g);
  284. newVertexCount += 3;
  285. } else {
  286. ++newTriangleCount;
  287. ChunkedArray.add3(index, i0, i1, i2);
  288. }
  289. }
  290. }
  291. const newIb = ChunkedArray.compact(index);
  292. const newVb = ChunkedArray.compact(vertex);
  293. const newNb = ChunkedArray.compact(normal);
  294. const newGb = ChunkedArray.compact(group);
  295. mesh.vertexCount = newVertexCount;
  296. mesh.triangleCount = newTriangleCount;
  297. ValueCell.update(vertexBuffer, newVb);
  298. ValueCell.update(groupBuffer, newGb);
  299. ValueCell.update(indexBuffer, newIb);
  300. ValueCell.update(normalBuffer, newNb);
  301. // keep some original data, e.g., for geometry export
  302. (mesh.meta.originalData as OriginalData) = { indexBuffer: ib, vertexCount, triangleCount };
  303. return mesh;
  304. }
  305. //
  306. function getNeighboursMap(mesh: Mesh) {
  307. const { vertexCount, triangleCount } = mesh;
  308. const elements = mesh.indexBuffer.ref.value;
  309. const neighboursMap: number[][] = [];
  310. for (let i = 0; i < vertexCount; ++i) {
  311. neighboursMap[i] = [];
  312. }
  313. for (let i = 0; i < triangleCount; ++i) {
  314. const v1 = elements[i * 3];
  315. const v2 = elements[i * 3 + 1];
  316. const v3 = elements[i * 3 + 2];
  317. arraySetAdd(neighboursMap[v1], v2);
  318. arraySetAdd(neighboursMap[v1], v3);
  319. arraySetAdd(neighboursMap[v2], v1);
  320. arraySetAdd(neighboursMap[v2], v3);
  321. arraySetAdd(neighboursMap[v3], v1);
  322. arraySetAdd(neighboursMap[v3], v2);
  323. }
  324. return neighboursMap;
  325. }
  326. function getEdgeCounts(mesh: Mesh) {
  327. const { triangleCount } = mesh;
  328. const elements = mesh.indexBuffer.ref.value;
  329. const edgeCounts = new Map<number, number>();
  330. const add = (a: number, b: number) => {
  331. const z = sortedCantorPairing(a, b);
  332. const c = edgeCounts.get(z) || 0;
  333. edgeCounts.set(z, c + 1);
  334. };
  335. for (let i = 0; i < triangleCount; ++i) {
  336. const a = elements[i * 3];
  337. const b = elements[i * 3 + 1];
  338. const c = elements[i * 3 + 2];
  339. add(a, b); add(a, c); add(b, c);
  340. }
  341. return edgeCounts;
  342. }
  343. function getBorderVertices(edgeCounts: Map<number, number>) {
  344. const borderVertices = new Set<number>();
  345. const pair: [number, number] = [0, 0];
  346. edgeCounts.forEach((c, z) => {
  347. if (c === 1) {
  348. invertCantorPairing(pair, z);
  349. borderVertices.add(pair[0]);
  350. borderVertices.add(pair[1]);
  351. }
  352. });
  353. return borderVertices;
  354. }
  355. function getBorderNeighboursMap(neighboursMap: number[][], borderVertices: Set<number>, edgeCounts: Map<number, number>) {
  356. const borderNeighboursMap = new Map<number, number[]>();
  357. const add = (v: number, nb: number) => {
  358. if (borderNeighboursMap.has(v)) arraySetAdd(borderNeighboursMap.get(v)!, nb);
  359. else borderNeighboursMap.set(v, [nb]);
  360. };
  361. borderVertices.forEach(v => {
  362. const neighbours = neighboursMap[v];
  363. for (const nb of neighbours) {
  364. if (borderVertices.has(nb) && edgeCounts.get(sortedCantorPairing(v, nb)) === 1) {
  365. add(v, nb);
  366. }
  367. }
  368. });
  369. return borderNeighboursMap;
  370. }
  371. function trimEdges(mesh: Mesh, neighboursMap: number[][]) {
  372. const { indexBuffer, triangleCount } = mesh;
  373. const ib = indexBuffer.ref.value;
  374. // new
  375. const index = ChunkedArray.create(Uint32Array, 3, 1024, triangleCount);
  376. let newTriangleCount = 0;
  377. for (let i = 0; i < triangleCount; ++i) {
  378. const a = ib[i * 3];
  379. const b = ib[i * 3 + 1];
  380. const c = ib[i * 3 + 2];
  381. if (neighboursMap[a].length === 2 ||
  382. neighboursMap[b].length === 2 ||
  383. neighboursMap[c].length === 2) continue;
  384. ChunkedArray.add3(index, a, b, c);
  385. newTriangleCount += 1;
  386. }
  387. const newIb = ChunkedArray.compact(index);
  388. mesh.triangleCount = newTriangleCount;
  389. ValueCell.update(indexBuffer, newIb);
  390. return mesh;
  391. }
  392. function fillEdges(mesh: Mesh, neighboursMap: number[][], borderNeighboursMap: Map<number, number[]>, maxLengthSquared: number) {
  393. const { vertexBuffer, indexBuffer, normalBuffer, triangleCount } = mesh;
  394. const vb = vertexBuffer.ref.value;
  395. const ib = indexBuffer.ref.value;
  396. const nb = normalBuffer.ref.value;
  397. // new
  398. const index = ChunkedArray.create(Uint32Array, 3, 1024, triangleCount);
  399. let newTriangleCount = 0;
  400. for (let i = 0; i < triangleCount; ++i) {
  401. ChunkedArray.add3(index, ib[i * 3], ib[i * 3 + 1], ib[i * 3 + 2]);
  402. newTriangleCount += 1;
  403. }
  404. const vA = Vec3();
  405. const vB = Vec3();
  406. const vC = Vec3();
  407. const vD = Vec3();
  408. const vAB = Vec3();
  409. const vAC = Vec3();
  410. const vAD = Vec3();
  411. const vABC = Vec3();
  412. const vAN = Vec3();
  413. const vN = Vec3();
  414. const AngleThreshold = degToRad(120);
  415. const added = new Set<number>();
  416. const indices = Array.from(borderNeighboursMap.keys())
  417. .filter(v => borderNeighboursMap.get(v)!.length < 2)
  418. .map(v => {
  419. const bnd = borderNeighboursMap.get(v)!;
  420. Vec3.fromArray(vA, vb, v * 3);
  421. Vec3.fromArray(vB, vb, bnd[0] * 3);
  422. Vec3.fromArray(vC, vb, bnd[1] * 3);
  423. Vec3.sub(vAB, vB, vA);
  424. Vec3.sub(vAC, vC, vA);
  425. return [v, Vec3.angle(vAB, vAC)];
  426. });
  427. // start with the smallest angle
  428. indices.sort(([, a], [, b]) => a - b);
  429. for (const [v, angle] of indices) {
  430. if (added.has(v) || angle > AngleThreshold) continue;
  431. const nbs = borderNeighboursMap.get(v)!;
  432. if (neighboursMap[nbs[0]].includes(nbs[1]) &&
  433. !borderNeighboursMap.get(nbs[0])?.includes(nbs[1])
  434. ) continue;
  435. Vec3.fromArray(vA, vb, v * 3);
  436. Vec3.fromArray(vB, vb, nbs[0] * 3);
  437. Vec3.fromArray(vC, vb, nbs[1] * 3);
  438. Vec3.sub(vAB, vB, vA);
  439. Vec3.sub(vAC, vC, vA);
  440. Vec3.add(vABC, vAB, vAC);
  441. if (Vec3.squaredDistance(vA, vB) >= maxLengthSquared) continue;
  442. let add = false;
  443. for (const nb of neighboursMap[v]) {
  444. if (nbs.includes(nb)) continue;
  445. Vec3.fromArray(vD, vb, nb * 3);
  446. Vec3.sub(vAD, vD, vA);
  447. if (Vec3.dot(vABC, vAD) < 0) {
  448. add = true;
  449. break;
  450. }
  451. }
  452. if (!add) continue;
  453. Vec3.fromArray(vAN, nb, v * 3);
  454. Vec3.triangleNormal(vN, vA, vB, vC);
  455. if (Vec3.dot(vN, vAN) > 0) {
  456. ChunkedArray.add3(index, v, nbs[0], nbs[1]);
  457. } else {
  458. ChunkedArray.add3(index, nbs[1], nbs[0], v);
  459. }
  460. added.add(v); added.add(nbs[0]); added.add(nbs[1]);
  461. newTriangleCount += 1;
  462. }
  463. const newIb = ChunkedArray.compact(index);
  464. mesh.triangleCount = newTriangleCount;
  465. ValueCell.update(indexBuffer, newIb);
  466. return mesh;
  467. }
  468. function laplacianEdgeSmoothing(mesh: Mesh, borderNeighboursMap: Map<number, number[]>, options: { iterations: number, lambda: number }) {
  469. const { iterations, lambda } = options;
  470. const a = Vec3();
  471. const b = Vec3();
  472. const c = Vec3();
  473. const t = Vec3();
  474. const mu = -lambda;
  475. let dst = new Float32Array(mesh.vertexBuffer.ref.value.length);
  476. const step = (f: number) => {
  477. const pos = mesh.vertexBuffer.ref.value;
  478. dst.set(pos);
  479. borderNeighboursMap.forEach((nbs, v) => {
  480. if (nbs.length !== 2) return;
  481. Vec3.fromArray(a, pos, v * 3);
  482. Vec3.fromArray(b, pos, nbs[0] * 3);
  483. Vec3.fromArray(c, pos, nbs[1] * 3);
  484. const wab = 1 / Vec3.distance(a, b);
  485. const wac = 1 / Vec3.distance(a, c);
  486. Vec3.scale(b, b, wab);
  487. Vec3.scale(c, c, wac);
  488. Vec3.add(t, b, c);
  489. Vec3.scale(t, t, 1 / (wab + wac));
  490. Vec3.sub(t, t, a);
  491. Vec3.scale(t, t, f);
  492. Vec3.add(t, a, t);
  493. Vec3.toArray(t, dst, v * 3);
  494. });
  495. const tmp = mesh.vertexBuffer.ref.value;
  496. ValueCell.update(mesh.vertexBuffer, dst);
  497. dst = tmp;
  498. };
  499. for (let k = 0; k < iterations; ++k) {
  500. step(lambda);
  501. step(mu);
  502. }
  503. }
  504. export function smoothEdges(mesh: Mesh, options: { iterations: number, maxNewEdgeLength: number }) {
  505. trimEdges(mesh, getNeighboursMap(mesh));
  506. for (let k = 0; k < 10; ++k) {
  507. const oldTriangleCount = mesh.triangleCount;
  508. const edgeCounts = getEdgeCounts(mesh);
  509. const neighboursMap = getNeighboursMap(mesh);
  510. const borderVertices = getBorderVertices(edgeCounts);
  511. const borderNeighboursMap = getBorderNeighboursMap(neighboursMap, borderVertices, edgeCounts);
  512. fillEdges(mesh, neighboursMap, borderNeighboursMap, options.maxNewEdgeLength * options.maxNewEdgeLength);
  513. if (mesh.triangleCount === oldTriangleCount) break;
  514. }
  515. const edgeCounts = getEdgeCounts(mesh);
  516. const neighboursMap = getNeighboursMap(mesh);
  517. const borderVertices = getBorderVertices(edgeCounts);
  518. const borderNeighboursMap = getBorderNeighboursMap(neighboursMap, borderVertices, edgeCounts);
  519. laplacianEdgeSmoothing(mesh, borderNeighboursMap, { iterations: options.iterations, lambda: 0.5 });
  520. return mesh;
  521. }
  522. //
  523. export const Params = {
  524. ...BaseGeometry.Params,
  525. doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
  526. flipSided: PD.Boolean(false, BaseGeometry.ShadingCategory),
  527. flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
  528. ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
  529. xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
  530. transparentBackfaces: PD.Select('off', PD.arrayToOptions(['off', 'on', 'opaque']), BaseGeometry.ShadingCategory),
  531. bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
  532. bumpAmplitude: PD.Numeric(1, { min: 0, max: 5, step: 0.1 }, BaseGeometry.ShadingCategory),
  533. };
  534. export type Params = typeof Params
  535. export const Utils: GeometryUtils<Mesh, Params> = {
  536. Params,
  537. createEmpty,
  538. createValues,
  539. createValuesSimple,
  540. updateValues,
  541. updateBoundingSphere,
  542. createRenderableState,
  543. updateRenderableState,
  544. createPositionIterator
  545. };
  546. function createPositionIterator(mesh: Mesh, transform: TransformData): LocationIterator {
  547. const groupCount = mesh.vertexCount;
  548. const instanceCount = transform.instanceCount.ref.value;
  549. const location = PositionLocation();
  550. const p = location.position;
  551. const v = mesh.vertexBuffer.ref.value;
  552. const m = transform.aTransform.ref.value;
  553. const getLocation = (groupIndex: number, instanceIndex: number) => {
  554. if (instanceIndex < 0) {
  555. Vec3.fromArray(p, v, groupIndex * 3);
  556. } else {
  557. Vec3.transformMat4Offset(p, v, m, 0, groupIndex * 3, instanceIndex * 16);
  558. }
  559. return location;
  560. };
  561. return LocationIterator(groupCount, instanceCount, 1, getLocation);
  562. }
  563. function createValues(mesh: Mesh, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): MeshValues {
  564. const { instanceCount, groupCount } = locationIt;
  565. const positionIt = createPositionIterator(mesh, transform);
  566. const color = createColors(locationIt, positionIt, theme.color);
  567. const marker = createMarkers(instanceCount * groupCount);
  568. const overpaint = createEmptyOverpaint();
  569. const transparency = createEmptyTransparency();
  570. const material = createEmptySubstance();
  571. const clipping = createEmptyClipping();
  572. const counts = { drawCount: mesh.triangleCount * 3, vertexCount: mesh.vertexCount, groupCount, instanceCount };
  573. const invariantBoundingSphere = Sphere3D.clone(mesh.boundingSphere);
  574. const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
  575. return {
  576. dGeometryType: ValueCell.create('mesh'),
  577. aPosition: mesh.vertexBuffer,
  578. aNormal: mesh.normalBuffer,
  579. aGroup: mesh.groupBuffer,
  580. elements: mesh.indexBuffer,
  581. boundingSphere: ValueCell.create(boundingSphere),
  582. invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
  583. uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
  584. ...color,
  585. ...marker,
  586. ...overpaint,
  587. ...transparency,
  588. ...material,
  589. ...clipping,
  590. ...transform,
  591. ...BaseGeometry.createValues(props, counts),
  592. uDoubleSided: ValueCell.create(props.doubleSided),
  593. dFlatShaded: ValueCell.create(props.flatShaded),
  594. dFlipSided: ValueCell.create(props.flipSided),
  595. dIgnoreLight: ValueCell.create(props.ignoreLight),
  596. dXrayShaded: ValueCell.create(props.xrayShaded),
  597. dTransparentBackfaces: ValueCell.create(props.transparentBackfaces),
  598. uBumpFrequency: ValueCell.create(props.bumpFrequency),
  599. uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
  600. meta: ValueCell.create(mesh.meta),
  601. };
  602. }
  603. function createValuesSimple(mesh: Mesh, props: Partial<PD.Values<Params>>, colorValue: Color, sizeValue: number, transform?: TransformData) {
  604. const s = BaseGeometry.createSimple(colorValue, sizeValue, transform);
  605. const p = { ...PD.getDefaultValues(Params), ...props };
  606. return createValues(mesh, s.transform, s.locationIterator, s.theme, p);
  607. }
  608. function updateValues(values: MeshValues, props: PD.Values<Params>) {
  609. BaseGeometry.updateValues(values, props);
  610. ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided);
  611. ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded);
  612. ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
  613. ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
  614. ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
  615. ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces);
  616. ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
  617. ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
  618. }
  619. function updateBoundingSphere(values: MeshValues, mesh: Mesh) {
  620. const invariantBoundingSphere = Sphere3D.clone(mesh.boundingSphere);
  621. const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value);
  622. if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
  623. ValueCell.update(values.boundingSphere, boundingSphere);
  624. }
  625. if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
  626. ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
  627. ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
  628. }
  629. }
  630. function createRenderableState(props: PD.Values<Params>): RenderableState {
  631. const state = BaseGeometry.createRenderableState(props);
  632. updateRenderableState(state, props);
  633. return state;
  634. }
  635. function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
  636. BaseGeometry.updateRenderableState(state, props);
  637. state.opaque = state.opaque && !props.xrayShaded;
  638. state.writeDepth = state.opaque;
  639. }
  640. }