mesh-builder.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. /**
  2. * Copyright (c) 2018 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 Box, { BoxProps } from '../primitive/box';
  10. import Cylinder, { CylinderProps } from '../primitive/cylinder';
  11. import Icosahedron, { IcosahedronProps } from '../primitive/icosahedron';
  12. import { Mesh } from './mesh';
  13. import { getNormalMatrix } from '../util';
  14. type Primitive = {
  15. vertices: Float32Array
  16. normals: Float32Array
  17. indices: Uint32Array
  18. }
  19. export interface MeshBuilder {
  20. add(t: Mat4, _vertices: Float32Array, _normals: Float32Array, _indices?: Uint32Array): number
  21. addBox(t: Mat4, props?: BoxProps): number
  22. addCylinder(t: Mat4, props?: CylinderProps): number
  23. addIcosahedron(t: Mat4, props?: IcosahedronProps): number
  24. setId(id: number): void
  25. getMesh(): Mesh
  26. }
  27. const tmpV = Vec3.zero()
  28. const tmpMat3 = Mat3.zero()
  29. // TODO cache primitives based on props
  30. export namespace MeshBuilder {
  31. export function create(initialCount = 2048, chunkSize = 1024, mesh?: Mesh): MeshBuilder {
  32. const vertices = ChunkedArray.create(Float32Array, 3, chunkSize, mesh ? mesh.vertexBuffer.ref.value : initialCount);
  33. const normals = ChunkedArray.create(Float32Array, 3, chunkSize, mesh ? mesh.normalBuffer.ref.value : initialCount);
  34. const indices = ChunkedArray.create(Uint32Array, 3, chunkSize * 3, mesh ? mesh.indexBuffer.ref.value : initialCount * 3);
  35. const ids = ChunkedArray.create(Float32Array, 1, chunkSize, mesh ? mesh.idBuffer.ref.value : initialCount);
  36. const offsets = ChunkedArray.create(Uint32Array, 1, chunkSize, mesh ? mesh.offsetBuffer.ref.value : initialCount);
  37. let currentId = -1
  38. const cylinderMap = new Map<string, Primitive>()
  39. const icosahedronMap = new Map<string, Primitive>()
  40. const add = (t: Mat4, _vertices: Float32Array, _normals: Float32Array, _indices: Uint32Array) => {
  41. const { elementCount, elementSize } = vertices
  42. const n = getNormalMatrix(tmpMat3, t)
  43. for (let i = 0, il = _vertices.length; i < il; i += 3) {
  44. // position
  45. Vec3.fromArray(tmpV, _vertices, i)
  46. Vec3.transformMat4(tmpV, tmpV, t)
  47. ChunkedArray.add3(vertices, tmpV[0], tmpV[1], tmpV[2]);
  48. // normal
  49. Vec3.fromArray(tmpV, _normals, i)
  50. Vec3.transformMat3(tmpV, tmpV, n)
  51. ChunkedArray.add3(normals, tmpV[0], tmpV[1], tmpV[2]);
  52. ChunkedArray.add(ids, currentId);
  53. }
  54. for (let i = 0, il = _indices.length; i < il; i += 3) {
  55. ChunkedArray.add3(indices, _indices[i] + elementCount, _indices[i + 1] + elementCount, _indices[i + 2] + elementCount);
  56. }
  57. return elementCount * elementSize
  58. }
  59. return {
  60. add,
  61. addBox: (t: Mat4, props?: BoxProps) => {
  62. const box = Box(props)
  63. return add(t, box.vertices, box.normals, box.indices)
  64. },
  65. addCylinder: (t: Mat4, props?: CylinderProps) => {
  66. const key = JSON.stringify(props)
  67. let cylinder = cylinderMap.get(key)
  68. if (cylinder === undefined) {
  69. cylinder = Cylinder(props)
  70. cylinderMap.set(key, cylinder)
  71. }
  72. return add(t, cylinder.vertices, cylinder.normals, cylinder.indices)
  73. },
  74. addIcosahedron: (t: Mat4, props: IcosahedronProps) => {
  75. const key = JSON.stringify(props)
  76. let icosahedron = icosahedronMap.get(key)
  77. if (icosahedron === undefined) {
  78. icosahedron = Icosahedron(props)
  79. icosahedronMap.set(key, icosahedron)
  80. }
  81. return add(t, icosahedron.vertices, icosahedron.normals, icosahedron.indices)
  82. },
  83. setId: (id: number) => {
  84. if (currentId !== id) {
  85. currentId = id
  86. ChunkedArray.add(offsets, vertices.elementCount)
  87. }
  88. },
  89. getMesh: () => {
  90. ChunkedArray.add(offsets, vertices.elementCount)
  91. const vb = ChunkedArray.compact(vertices, true) as Float32Array
  92. const ib = ChunkedArray.compact(indices, true) as Uint32Array
  93. const nb = ChunkedArray.compact(normals, true) as Float32Array
  94. const idb = ChunkedArray.compact(ids, true) as Float32Array
  95. const ob = ChunkedArray.compact(offsets, true) as Uint32Array
  96. return {
  97. vertexCount: vertices.elementCount,
  98. triangleCount: indices.elementCount,
  99. offsetCount: offsets.elementCount,
  100. vertexBuffer: mesh ? ValueCell.update(mesh.vertexBuffer, vb) : ValueCell.create(vb),
  101. indexBuffer: mesh ? ValueCell.update(mesh.indexBuffer, ib) : ValueCell.create(ib),
  102. normalBuffer: mesh ? ValueCell.update(mesh.normalBuffer, nb) : ValueCell.create(nb),
  103. idBuffer: mesh ? ValueCell.update(mesh.idBuffer, idb) : ValueCell.create(idb),
  104. offsetBuffer: mesh ? ValueCell.update(mesh.offsetBuffer, ob) : ValueCell.create(ob),
  105. normalsComputed: true,
  106. offsetsComputed: true,
  107. }
  108. }
  109. }
  110. }
  111. }