mesh-builder.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. /**
  2. * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { ValueCell } from '../../../mol-util/value-cell'
  7. import { Vec3, Mat4, Mat3 } from '../../../mol-math/linear-algebra';
  8. import { ChunkedArray } from '../../../mol-data/util';
  9. import { Mesh } from './mesh';
  10. import { getNormalMatrix } from '../../util';
  11. import { Primitive } from '../../primitive/primitive';
  12. import { Cage } from '../../../mol-geo/primitive/cage';
  13. import { addSphere } from './builder/sphere';
  14. import { addCylinder } from './builder/cylinder';
  15. const tmpV = Vec3.zero()
  16. const tmpMat3 = Mat3.zero()
  17. const tmpVecA = Vec3.zero()
  18. const tmpVecB = Vec3.zero()
  19. const tmpVecC = Vec3.zero()
  20. const tmpVecD = Vec3.zero()
  21. export namespace MeshBuilder {
  22. export interface State {
  23. currentGroup: number
  24. readonly vertices: ChunkedArray<number, 3>
  25. readonly normals: ChunkedArray<number, 3>
  26. readonly indices: ChunkedArray<number, 3>
  27. readonly groups: ChunkedArray<number, 1>
  28. readonly mesh?: Mesh
  29. }
  30. export function createState(initialCount = 2048, chunkSize = 1024, mesh?: Mesh): State {
  31. return {
  32. currentGroup: -1,
  33. vertices: ChunkedArray.create(Float32Array, 3, chunkSize, mesh ? mesh.vertexBuffer.ref.value : initialCount),
  34. normals: ChunkedArray.create(Float32Array, 3, chunkSize, mesh ? mesh.normalBuffer.ref.value : initialCount),
  35. indices: ChunkedArray.create(Uint32Array, 3, chunkSize * 3, mesh ? mesh.indexBuffer.ref.value : initialCount * 3),
  36. groups: ChunkedArray.create(Float32Array, 1, chunkSize, mesh ? mesh.groupBuffer.ref.value : initialCount),
  37. mesh
  38. }
  39. }
  40. export function addTriangle(state: State, a: Vec3, b: Vec3, c: Vec3) {
  41. const { vertices, normals, indices, groups, currentGroup } = state
  42. const offset = vertices.elementCount
  43. // positions
  44. ChunkedArray.add3(vertices, a[0], a[1], a[2]);
  45. ChunkedArray.add3(vertices, b[0], b[1], b[2]);
  46. ChunkedArray.add3(vertices, c[0], c[1], c[2]);
  47. Vec3.triangleNormal(tmpV, a, b, c)
  48. for (let i = 0; i < 3; ++i) {
  49. ChunkedArray.add3(normals, tmpV[0], tmpV[1], tmpV[2]); // normal
  50. ChunkedArray.add(groups, currentGroup); // group
  51. }
  52. ChunkedArray.add3(indices, offset, offset + 1, offset + 2);
  53. }
  54. export function addTriangleStrip(state: State, vertices: ArrayLike<number>, indices: ArrayLike<number>) {
  55. Vec3.fromArray(tmpVecC, vertices, indices[0] * 3)
  56. Vec3.fromArray(tmpVecD, vertices, indices[1] * 3)
  57. for (let i = 2, il = indices.length; i < il; i += 2) {
  58. Vec3.copy(tmpVecA, tmpVecC)
  59. Vec3.copy(tmpVecB, tmpVecD)
  60. Vec3.fromArray(tmpVecC, vertices, indices[i] * 3)
  61. Vec3.fromArray(tmpVecD, vertices, indices[i + 1] * 3)
  62. addTriangle(state, tmpVecA, tmpVecB, tmpVecC)
  63. addTriangle(state, tmpVecB, tmpVecD, tmpVecC)
  64. }
  65. }
  66. export function addTriangleFan(state: State, vertices: ArrayLike<number>, indices: ArrayLike<number>) {
  67. Vec3.fromArray(tmpVecA, vertices, indices[0] * 3)
  68. for (let i = 2, il = indices.length; i < il; ++i) {
  69. Vec3.fromArray(tmpVecB, vertices, indices[i - 1] * 3)
  70. Vec3.fromArray(tmpVecC, vertices, indices[i] * 3)
  71. addTriangle(state, tmpVecA, tmpVecC, tmpVecB)
  72. }
  73. }
  74. export function addPrimitive(state: State, t: Mat4, primitive: Primitive) {
  75. const { vertices: va, normals: na, indices: ia } = primitive
  76. const { vertices, normals, indices, groups, currentGroup } = state
  77. const offset = vertices.elementCount
  78. const n = getNormalMatrix(tmpMat3, t)
  79. for (let i = 0, il = va.length; i < il; i += 3) {
  80. // position
  81. Vec3.transformMat4(tmpV, Vec3.fromArray(tmpV, va, i), t)
  82. ChunkedArray.add3(vertices, tmpV[0], tmpV[1], tmpV[2]);
  83. // normal
  84. Vec3.transformMat3(tmpV, Vec3.fromArray(tmpV, na, i), n)
  85. ChunkedArray.add3(normals, tmpV[0], tmpV[1], tmpV[2]);
  86. // group
  87. ChunkedArray.add(groups, currentGroup);
  88. }
  89. for (let i = 0, il = ia.length; i < il; i += 3) {
  90. ChunkedArray.add3(indices, ia[i] + offset, ia[i + 1] + offset, ia[i + 2] + offset);
  91. }
  92. }
  93. export function addCage(state: State, t: Mat4, cage: Cage, radius: number, detail: number, radialSegments: number) {
  94. const { vertices: va, edges: ea } = cage
  95. const cylinderProps = { radiusTop: radius, radiusBottom: radius, radialSegments }
  96. for (let i = 0, il = ea.length; i < il; i += 2) {
  97. Vec3.fromArray(tmpVecA, va, ea[i] * 3)
  98. Vec3.fromArray(tmpVecB, va, ea[i + 1] * 3)
  99. Vec3.transformMat4(tmpVecA, tmpVecA, t)
  100. Vec3.transformMat4(tmpVecB, tmpVecB, t)
  101. addSphere(state, tmpVecA, radius, detail)
  102. addSphere(state, tmpVecB, radius, detail)
  103. addCylinder(state, tmpVecA, tmpVecB, 1, cylinderProps)
  104. }
  105. }
  106. export function getMesh (state: State): Mesh {
  107. const { vertices, normals, indices, groups, mesh } = state
  108. const vb = ChunkedArray.compact(vertices, true) as Float32Array
  109. const ib = ChunkedArray.compact(indices, true) as Uint32Array
  110. const nb = ChunkedArray.compact(normals, true) as Float32Array
  111. const gb = ChunkedArray.compact(groups, true) as Float32Array
  112. return {
  113. kind: 'mesh',
  114. vertexCount: state.vertices.elementCount,
  115. triangleCount: state.indices.elementCount,
  116. vertexBuffer: mesh ? ValueCell.update(mesh.vertexBuffer, vb) : ValueCell.create(vb),
  117. indexBuffer: mesh ? ValueCell.update(mesh.indexBuffer, ib) : ValueCell.create(ib),
  118. normalBuffer: mesh ? ValueCell.update(mesh.normalBuffer, nb) : ValueCell.create(nb),
  119. groupBuffer: mesh ? ValueCell.update(mesh.groupBuffer, gb) : ValueCell.create(gb),
  120. normalsComputed: true,
  121. }
  122. }
  123. }