mesh.ts 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. /**
  2. * Copyright (c) 2018-2021 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 } 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. export interface Mesh {
  28. readonly kind: 'mesh',
  29. /** Number of vertices in the mesh */
  30. vertexCount: number,
  31. /** Number of triangles in the mesh */
  32. triangleCount: number,
  33. /** Vertex buffer as array of xyz values wrapped in a value cell */
  34. readonly vertexBuffer: ValueCell<Float32Array>,
  35. /** Index buffer as array of vertex index triplets wrapped in a value cell */
  36. readonly indexBuffer: ValueCell<Uint32Array>,
  37. /** Normal buffer as array of xyz values for each vertex wrapped in a value cell */
  38. readonly normalBuffer: ValueCell<Float32Array>,
  39. /** Group buffer as array of group ids for each vertex wrapped in a value cell */
  40. readonly groupBuffer: ValueCell<Float32Array>,
  41. /** Bounding sphere of the mesh */
  42. readonly boundingSphere: Sphere3D
  43. /** Maps group ids to vertex indices */
  44. readonly groupMapping: GroupMapping
  45. setBoundingSphere(boundingSphere: Sphere3D): void
  46. readonly meta: { [k: string]: unknown }
  47. }
  48. export namespace Mesh {
  49. export function create(vertices: Float32Array, indices: Uint32Array, normals: Float32Array, groups: Float32Array, vertexCount: number, triangleCount: number, mesh?: Mesh): Mesh {
  50. return mesh ?
  51. update(vertices, indices, normals, groups, vertexCount, triangleCount, mesh) :
  52. fromArrays(vertices, indices, normals, groups, vertexCount, triangleCount);
  53. }
  54. export function createEmpty(mesh?: Mesh): Mesh {
  55. const vb = mesh ? mesh.vertexBuffer.ref.value : new Float32Array(0);
  56. const ib = mesh ? mesh.indexBuffer.ref.value : new Uint32Array(0);
  57. const nb = mesh ? mesh.normalBuffer.ref.value : new Float32Array(0);
  58. const gb = mesh ? mesh.groupBuffer.ref.value : new Float32Array(0);
  59. return create(vb, ib, nb, gb, 0, 0, mesh);
  60. }
  61. function hashCode(mesh: Mesh) {
  62. return hashFnv32a([
  63. mesh.vertexCount, mesh.triangleCount,
  64. mesh.vertexBuffer.ref.version, mesh.indexBuffer.ref.version,
  65. mesh.normalBuffer.ref.version, mesh.groupBuffer.ref.version
  66. ]);
  67. }
  68. function fromArrays(vertices: Float32Array, indices: Uint32Array, normals: Float32Array, groups: Float32Array, vertexCount: number, triangleCount: number): Mesh {
  69. const boundingSphere = Sphere3D();
  70. let groupMapping: GroupMapping;
  71. let currentHash = -1;
  72. let currentGroup = -1;
  73. const mesh = {
  74. kind: 'mesh' as const,
  75. vertexCount,
  76. triangleCount,
  77. vertexBuffer: ValueCell.create(vertices),
  78. indexBuffer: ValueCell.create(indices),
  79. normalBuffer: ValueCell.create(normals),
  80. groupBuffer: ValueCell.create(groups),
  81. get boundingSphere() {
  82. const newHash = hashCode(mesh);
  83. if (newHash !== currentHash) {
  84. const b = calculateInvariantBoundingSphere(mesh.vertexBuffer.ref.value, mesh.vertexCount, 1);
  85. Sphere3D.copy(boundingSphere, b);
  86. currentHash = newHash;
  87. }
  88. return boundingSphere;
  89. },
  90. get groupMapping() {
  91. if (mesh.groupBuffer.ref.version !== currentGroup) {
  92. groupMapping = createGroupMapping(mesh.groupBuffer.ref.value, mesh.vertexCount);
  93. currentGroup = mesh.groupBuffer.ref.version;
  94. }
  95. return groupMapping;
  96. },
  97. setBoundingSphere(sphere: Sphere3D) {
  98. Sphere3D.copy(boundingSphere, sphere);
  99. currentHash = hashCode(mesh);
  100. },
  101. meta: {}
  102. };
  103. return mesh;
  104. }
  105. function update(vertices: Float32Array, indices: Uint32Array, normals: Float32Array, groups: Float32Array, vertexCount: number, triangleCount: number, mesh: Mesh) {
  106. mesh.vertexCount = vertexCount;
  107. mesh.triangleCount = triangleCount;
  108. ValueCell.update(mesh.vertexBuffer, vertices);
  109. ValueCell.update(mesh.indexBuffer, indices);
  110. ValueCell.update(mesh.normalBuffer, normals);
  111. ValueCell.update(mesh.groupBuffer, groups);
  112. return mesh;
  113. }
  114. export function computeNormals(mesh: Mesh) {
  115. const { vertexCount, triangleCount } = mesh;
  116. const vertices = mesh.vertexBuffer.ref.value;
  117. const indices = mesh.indexBuffer.ref.value;
  118. const normals = mesh.normalBuffer.ref.value.length >= vertexCount * 3
  119. ? mesh.normalBuffer.ref.value
  120. : new Float32Array(vertexCount * 3);
  121. if (normals === mesh.normalBuffer.ref.value) {
  122. normals.fill(0, 0, vertexCount * 3);
  123. }
  124. computeIndexedVertexNormals(vertices, indices, normals, vertexCount, triangleCount);
  125. ValueCell.update(mesh.normalBuffer, normals);
  126. }
  127. export function checkForDuplicateVertices(mesh: Mesh, fractionDigits = 3) {
  128. const v = mesh.vertexBuffer.ref.value;
  129. const map = new Map<string, number>();
  130. const hash = (v: Vec3, d: number) => `${v[0].toFixed(d)}|${v[1].toFixed(d)}|${v[2].toFixed(d)}`;
  131. let duplicates = 0;
  132. const a = Vec3();
  133. for (let i = 0, il = mesh.vertexCount; i < il; ++i) {
  134. Vec3.fromArray(a, v, i * 3);
  135. const k = hash(a, fractionDigits);
  136. const count = map.get(k);
  137. if (count !== undefined) {
  138. duplicates += 1;
  139. map.set(k, count + 1);
  140. } else {
  141. map.set(k, 1);
  142. }
  143. }
  144. return duplicates;
  145. }
  146. const tmpMat3 = Mat3();
  147. export function transform(mesh: Mesh, t: Mat4) {
  148. const v = mesh.vertexBuffer.ref.value;
  149. transformPositionArray(t, v, 0, mesh.vertexCount);
  150. if (!Mat4.isTranslationAndUniformScaling(t)) {
  151. const n = Mat3.directionTransform(tmpMat3, t);
  152. transformDirectionArray(n, mesh.normalBuffer.ref.value, 0, mesh.vertexCount);
  153. }
  154. ValueCell.update(mesh.vertexBuffer, v);
  155. }
  156. export type OriginalData = {
  157. indexBuffer: Uint32Array
  158. vertexCount: number
  159. triangleCount: number
  160. }
  161. /** Meshes may contain some original data in case any processing was done. */
  162. export function getOriginalData(x: Mesh | MeshValues) {
  163. const { originalData } = 'kind' in x ? x.meta : x.meta.ref.value as Mesh['meta'];
  164. return originalData as OriginalData | undefined;
  165. }
  166. /**
  167. * Ensure that each vertices of each triangle have the same group id.
  168. * Note that normals are copied over and can't be re-created from the new mesh.
  169. */
  170. export function uniformTriangleGroup(mesh: Mesh, splitTriangles = true) {
  171. const { indexBuffer, vertexBuffer, groupBuffer, normalBuffer, triangleCount, vertexCount } = mesh;
  172. const ib = indexBuffer.ref.value;
  173. const vb = vertexBuffer.ref.value;
  174. const gb = groupBuffer.ref.value;
  175. const nb = normalBuffer.ref.value;
  176. // new
  177. const index = ChunkedArray.create(Uint32Array, 3, 1024, triangleCount);
  178. // re-use
  179. const vertex = ChunkedArray.create(Float32Array, 3, 1024, vb);
  180. vertex.currentIndex = vertexCount * 3;
  181. vertex.elementCount = vertexCount;
  182. const normal = ChunkedArray.create(Float32Array, 3, 1024, nb);
  183. normal.currentIndex = vertexCount * 3;
  184. normal.elementCount = vertexCount;
  185. const group = ChunkedArray.create(Float32Array, 1, 1024, gb);
  186. group.currentIndex = vertexCount;
  187. group.elementCount = vertexCount;
  188. const vi = Vec3();
  189. const vj = Vec3();
  190. const vk = Vec3();
  191. const ni = Vec3();
  192. const nj = Vec3();
  193. const nk = Vec3();
  194. function add(i: number) {
  195. Vec3.fromArray(vi, vb, i * 3);
  196. Vec3.fromArray(ni, nb, i * 3);
  197. ChunkedArray.add3(vertex, vi[0], vi[1], vi[2]);
  198. ChunkedArray.add3(normal, ni[0], ni[1], ni[2]);
  199. }
  200. function addMid(i: number, j: number) {
  201. Vec3.fromArray(vi, vb, i * 3);
  202. Vec3.fromArray(vj, vb, j * 3);
  203. Vec3.scale(vi, Vec3.add(vi, vi, vj), 0.5);
  204. Vec3.fromArray(ni, nb, i * 3);
  205. Vec3.fromArray(nj, nb, j * 3);
  206. Vec3.scale(ni, Vec3.add(ni, ni, nj), 0.5);
  207. ChunkedArray.add3(vertex, vi[0], vi[1], vi[2]);
  208. ChunkedArray.add3(normal, ni[0], ni[1], ni[2]);
  209. }
  210. function addCenter(i: number, j: number, k: number) {
  211. Vec3.fromArray(vi, vb, i * 3);
  212. Vec3.fromArray(vj, vb, j * 3);
  213. Vec3.fromArray(vk, vb, k * 3);
  214. Vec3.scale(vi, Vec3.add(vi, Vec3.add(vi, vi, vj), vk), 1 / 3);
  215. Vec3.fromArray(ni, nb, i * 3);
  216. Vec3.fromArray(nj, nb, j * 3);
  217. Vec3.fromArray(nk, nb, k * 3);
  218. Vec3.scale(ni, Vec3.add(ni, Vec3.add(ni, ni, nj), nk), 1 / 3);
  219. ChunkedArray.add3(vertex, vi[0], vi[1], vi[2]);
  220. ChunkedArray.add3(normal, ni[0], ni[1], ni[2]);
  221. }
  222. function split2(i0: number, i1: number, i2: number, g0: number, g1: number) {
  223. ++newTriangleCount;
  224. add(i0); addMid(i0, i1); addMid(i0, i2);
  225. ChunkedArray.add3(index, newVertexCount, newVertexCount + 1, newVertexCount + 2);
  226. for (let j = 0; j < 3; ++j) ChunkedArray.add(group, g0);
  227. newVertexCount += 3;
  228. newTriangleCount += 2;
  229. add(i1); add(i2); addMid(i0, i1); addMid(i0, i2);
  230. ChunkedArray.add3(index, newVertexCount, newVertexCount + 1, newVertexCount + 3);
  231. ChunkedArray.add3(index, newVertexCount, newVertexCount + 3, newVertexCount + 2);
  232. for (let j = 0; j < 4; ++j) ChunkedArray.add(group, g1);
  233. newVertexCount += 4;
  234. }
  235. let newVertexCount = vertexCount;
  236. let newTriangleCount = 0;
  237. if (splitTriangles) {
  238. for (let i = 0, il = triangleCount; i < il; ++i) {
  239. const i0 = ib[i * 3], i1 = ib[i * 3 + 1], i2 = ib[i * 3 + 2];
  240. const g0 = gb[i0], g1 = gb[i1], g2 = gb[i2];
  241. if (g0 === g1 && g0 === g2) {
  242. ++newTriangleCount;
  243. ChunkedArray.add3(index, i0, i1, i2);
  244. } else if (g0 === g1) {
  245. split2(i2, i0, i1, g2, g0);
  246. } else if (g0 === g2) {
  247. split2(i1, i2, i0, g1, g2);
  248. } else if (g1 === g2) {
  249. split2(i0, i1, i2, g0, g1);
  250. } else {
  251. newTriangleCount += 2;
  252. add(i0); addMid(i0, i1); addMid(i0, i2); addCenter(i0, i1, i2);
  253. ChunkedArray.add3(index, newVertexCount, newVertexCount + 1, newVertexCount + 3);
  254. ChunkedArray.add3(index, newVertexCount, newVertexCount + 3, newVertexCount + 2);
  255. for (let j = 0; j < 4; ++j) ChunkedArray.add(group, g0);
  256. newVertexCount += 4;
  257. newTriangleCount += 2;
  258. add(i1); addMid(i1, i2); addMid(i1, i0); 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, g1);
  262. newVertexCount += 4;
  263. newTriangleCount += 2;
  264. add(i2); addMid(i2, i1); addMid(i2, i0); addCenter(i0, i1, i2);
  265. ChunkedArray.add3(index, newVertexCount + 3, newVertexCount + 1, newVertexCount);
  266. ChunkedArray.add3(index, newVertexCount + 2, newVertexCount + 3, newVertexCount);
  267. for (let j = 0; j < 4; ++j) ChunkedArray.add(group, g2);
  268. newVertexCount += 4;
  269. }
  270. }
  271. } else {
  272. for (let i = 0, il = triangleCount; i < il; ++i) {
  273. const i0 = ib[i * 3], i1 = ib[i * 3 + 1], i2 = ib[i * 3 + 2];
  274. const g0 = gb[i0], g1 = gb[i1], g2 = gb[i2];
  275. if (g0 !== g1 || g0 !== g2) {
  276. ++newTriangleCount;
  277. add(i0); add(i1); add(i2);
  278. ChunkedArray.add3(index, newVertexCount, newVertexCount + 1, newVertexCount + 2);
  279. const g = g1 === g2 ? g1 : g0;
  280. for (let j = 0; j < 3; ++j) ChunkedArray.add(group, g);
  281. newVertexCount += 3;
  282. } else {
  283. ++newTriangleCount;
  284. ChunkedArray.add3(index, i0, i1, i2);
  285. }
  286. }
  287. }
  288. const newIb = ChunkedArray.compact(index);
  289. const newVb = ChunkedArray.compact(vertex);
  290. const newNb = ChunkedArray.compact(normal);
  291. const newGb = ChunkedArray.compact(group);
  292. mesh.vertexCount = newVertexCount;
  293. mesh.triangleCount = newTriangleCount;
  294. ValueCell.update(vertexBuffer, newVb) as ValueCell<Float32Array>;
  295. ValueCell.update(groupBuffer, newGb) as ValueCell<Float32Array>;
  296. ValueCell.update(indexBuffer, newIb) as ValueCell<Uint32Array>;
  297. ValueCell.update(normalBuffer, newNb) as ValueCell<Float32Array>;
  298. // keep some original data, e.g., for geometry export
  299. (mesh.meta.originalData as OriginalData) = { indexBuffer: ib, vertexCount, triangleCount };
  300. return mesh;
  301. }
  302. //
  303. export const Params = {
  304. ...BaseGeometry.Params,
  305. doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
  306. flipSided: PD.Boolean(false, BaseGeometry.ShadingCategory),
  307. flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
  308. ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
  309. xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
  310. };
  311. export type Params = typeof Params
  312. export const Utils: GeometryUtils<Mesh, Params> = {
  313. Params,
  314. createEmpty,
  315. createValues,
  316. createValuesSimple,
  317. updateValues,
  318. updateBoundingSphere,
  319. createRenderableState,
  320. updateRenderableState,
  321. createPositionIterator
  322. };
  323. function createPositionIterator(mesh: Mesh, transform: TransformData): LocationIterator {
  324. const groupCount = mesh.vertexCount;
  325. const instanceCount = transform.instanceCount.ref.value;
  326. const location = PositionLocation();
  327. const p = location.position;
  328. const v = mesh.vertexBuffer.ref.value;
  329. const m = transform.aTransform.ref.value;
  330. const getLocation = (groupIndex: number, instanceIndex: number) => {
  331. if (instanceIndex < 0) {
  332. Vec3.fromArray(p, v, groupIndex * 3);
  333. } else {
  334. Vec3.transformMat4Offset(p, v, m, 0, groupIndex * 3, instanceIndex * 16);
  335. }
  336. return location;
  337. };
  338. return LocationIterator(groupCount, instanceCount, 1, getLocation);
  339. }
  340. function createValues(mesh: Mesh, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): MeshValues {
  341. const { instanceCount, groupCount } = locationIt;
  342. const positionIt = createPositionIterator(mesh, transform);
  343. const color = createColors(locationIt, positionIt, theme.color);
  344. const marker = createMarkers(instanceCount * groupCount);
  345. const overpaint = createEmptyOverpaint();
  346. const transparency = createEmptyTransparency();
  347. const clipping = createEmptyClipping();
  348. const counts = { drawCount: mesh.triangleCount * 3, vertexCount: mesh.vertexCount, groupCount, instanceCount };
  349. const invariantBoundingSphere = Sphere3D.clone(mesh.boundingSphere);
  350. const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
  351. return {
  352. aPosition: mesh.vertexBuffer,
  353. aNormal: mesh.normalBuffer,
  354. aGroup: mesh.groupBuffer,
  355. elements: mesh.indexBuffer,
  356. boundingSphere: ValueCell.create(boundingSphere),
  357. invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
  358. uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
  359. ...color,
  360. ...marker,
  361. ...overpaint,
  362. ...transparency,
  363. ...clipping,
  364. ...transform,
  365. ...BaseGeometry.createValues(props, counts),
  366. dDoubleSided: ValueCell.create(props.doubleSided),
  367. dFlatShaded: ValueCell.create(props.flatShaded),
  368. dFlipSided: ValueCell.create(props.flipSided),
  369. dIgnoreLight: ValueCell.create(props.ignoreLight),
  370. dXrayShaded: ValueCell.create(props.xrayShaded),
  371. meta: ValueCell.create(mesh.meta),
  372. };
  373. }
  374. function createValuesSimple(mesh: Mesh, props: Partial<PD.Values<Params>>, colorValue: Color, sizeValue: number, transform?: TransformData) {
  375. const s = BaseGeometry.createSimple(colorValue, sizeValue, transform);
  376. const p = { ...PD.getDefaultValues(Params), ...props };
  377. return createValues(mesh, s.transform, s.locationIterator, s.theme, p);
  378. }
  379. function updateValues(values: MeshValues, props: PD.Values<Params>) {
  380. BaseGeometry.updateValues(values, props);
  381. ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
  382. ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded);
  383. ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
  384. ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
  385. ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
  386. }
  387. function updateBoundingSphere(values: MeshValues, mesh: Mesh) {
  388. const invariantBoundingSphere = Sphere3D.clone(mesh.boundingSphere);
  389. const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value);
  390. if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
  391. ValueCell.update(values.boundingSphere, boundingSphere);
  392. }
  393. if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
  394. ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
  395. ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
  396. }
  397. }
  398. function createRenderableState(props: PD.Values<Params>): RenderableState {
  399. const state = BaseGeometry.createRenderableState(props);
  400. updateRenderableState(state, props);
  401. return state;
  402. }
  403. function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
  404. BaseGeometry.updateRenderableState(state, props);
  405. state.opaque = state.opaque && !props.xrayShaded;
  406. state.writeDepth = state.opaque;
  407. }
  408. }