mesh.ts 31 KB

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