mesh-exporter.ts 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. /**
  2. * Copyright (c) 2021-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
  5. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  6. */
  7. import { sort, arraySwap } from '../../mol-data/util';
  8. import { GraphicsRenderObject } from '../../mol-gl/render-object';
  9. import { MeshValues } from '../../mol-gl/renderable/mesh';
  10. import { LinesValues } from '../../mol-gl/renderable/lines';
  11. import { PointsValues } from '../../mol-gl/renderable/points';
  12. import { SpheresValues } from '../../mol-gl/renderable/spheres';
  13. import { CylindersValues } from '../../mol-gl/renderable/cylinders';
  14. import { TextureMeshValues } from '../../mol-gl/renderable/texture-mesh';
  15. import { BaseValues, SizeValues } from '../../mol-gl/renderable/schema';
  16. import { TextureImage } from '../../mol-gl/renderable/util';
  17. import { WebGLContext } from '../../mol-gl/webgl/context';
  18. import { getTrilinearlyInterpolated } from '../../mol-geo/geometry/mesh/color-smoothing';
  19. import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
  20. import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
  21. import { addSphere } from '../../mol-geo/geometry/mesh/builder/sphere';
  22. import { addCylinder } from '../../mol-geo/geometry/mesh/builder/cylinder';
  23. import { sizeDataFactor } from '../../mol-geo/geometry/size-data';
  24. import { Vec3 } from '../../mol-math/linear-algebra';
  25. import { RuntimeContext } from '../../mol-task';
  26. import { Color } from '../../mol-util/color/color';
  27. import { unpackRGBToInt } from '../../mol-util/number-packing';
  28. import { RenderObjectExporter, RenderObjectExportData } from './render-object-exporter';
  29. import { readAlphaTexture, readTexture } from '../../mol-gl/compute/util';
  30. const GeoExportName = 'geo-export';
  31. // avoiding namespace lookup improved performance in Chrome (Aug 2020)
  32. const v3fromArray = Vec3.fromArray;
  33. type MeshMode = 'points' | 'lines' | 'triangles'
  34. export interface AddMeshInput {
  35. mesh: {
  36. vertices: Float32Array
  37. normals: Float32Array | undefined
  38. indices: Uint32Array | undefined
  39. groups: Float32Array | Uint8Array
  40. vertexCount: number
  41. drawCount: number
  42. } | undefined
  43. meshes: Mesh[] | undefined
  44. values: BaseValues
  45. isGeoTexture: boolean
  46. mode: MeshMode
  47. webgl: WebGLContext | undefined
  48. ctx: RuntimeContext
  49. }
  50. export type MeshGeoData = {
  51. values: BaseValues,
  52. groups: Float32Array | Uint8Array,
  53. vertexCount: number,
  54. instanceIndex: number,
  55. isGeoTexture: boolean,
  56. mode: MeshMode
  57. }
  58. export abstract class MeshExporter<D extends RenderObjectExportData> implements RenderObjectExporter<D> {
  59. abstract readonly fileExtension: string;
  60. private static getSizeFromTexture(tSize: TextureImage<Uint8Array>, i: number): number {
  61. const r = tSize.array[i * 3];
  62. const g = tSize.array[i * 3 + 1];
  63. const b = tSize.array[i * 3 + 2];
  64. return unpackRGBToInt(r, g, b) / sizeDataFactor;
  65. }
  66. private static getSize(values: BaseValues & SizeValues, instanceIndex: number, group: number): number {
  67. const tSize = values.tSize.ref.value;
  68. let size = 0;
  69. switch (values.dSizeType.ref.value) {
  70. case 'uniform':
  71. size = values.uSize.ref.value;
  72. break;
  73. case 'instance':
  74. size = MeshExporter.getSizeFromTexture(tSize, instanceIndex);
  75. break;
  76. case 'group':
  77. size = MeshExporter.getSizeFromTexture(tSize, group);
  78. break;
  79. case 'groupInstance':
  80. const groupCount = values.uGroupCount.ref.value;
  81. size = MeshExporter.getSizeFromTexture(tSize, instanceIndex * groupCount + group);
  82. break;
  83. }
  84. return size * values.uSizeFactor.ref.value;
  85. }
  86. protected static getGroup(groups: Float32Array | Uint8Array, i: number): number {
  87. const i4 = i * 4;
  88. const r = groups[i4];
  89. const g = groups[i4 + 1];
  90. const b = groups[i4 + 2];
  91. if (groups instanceof Float32Array) {
  92. return unpackRGBToInt(r * 255 + 0.5, g * 255 + 0.5, b * 255 + 0.5);
  93. }
  94. return unpackRGBToInt(r, g, b);
  95. }
  96. protected static getInterpolatedColors(webgl: WebGLContext, input: { vertices: Float32Array, vertexCount: number, values: BaseValues, stride: 3 | 4, colorType: 'volume' | 'volumeInstance' }) {
  97. const { values, vertexCount, vertices, colorType, stride } = input;
  98. const colorGridTransform = values.uColorGridTransform.ref.value;
  99. const colorGridDim = values.uColorGridDim.ref.value;
  100. const colorTexDim = values.uColorTexDim.ref.value;
  101. const aTransform = values.aTransform.ref.value;
  102. const instanceCount = values.uInstanceCount.ref.value;
  103. const colorGrid = readTexture(webgl, values.tColorGrid.ref.value).array;
  104. const interpolated = getTrilinearlyInterpolated({ vertexCount, instanceCount, transformBuffer: aTransform, positionBuffer: vertices, colorType, grid: colorGrid, gridDim: colorGridDim, gridTexDim: colorTexDim, gridTransform: colorGridTransform, vertexStride: stride, colorStride: 4, outputStride: 3 });
  105. return interpolated.array;
  106. }
  107. protected static getInterpolatedOverpaint(webgl: WebGLContext, input: { vertices: Float32Array, vertexCount: number, values: BaseValues, stride: 3 | 4, colorType: 'volumeInstance' }) {
  108. const { values, vertexCount, vertices, colorType, stride } = input;
  109. const overpaintGridTransform = values.uOverpaintGridTransform.ref.value;
  110. const overpaintGridDim = values.uOverpaintGridDim.ref.value;
  111. const overpaintTexDim = values.uOverpaintTexDim.ref.value;
  112. const aTransform = values.aTransform.ref.value;
  113. const instanceCount = values.uInstanceCount.ref.value;
  114. const overpaintGrid = readTexture(webgl, values.tOverpaintGrid.ref.value).array;
  115. const interpolated = getTrilinearlyInterpolated({ vertexCount, instanceCount, transformBuffer: aTransform, positionBuffer: vertices, colorType, grid: overpaintGrid, gridDim: overpaintGridDim, gridTexDim: overpaintTexDim, gridTransform: overpaintGridTransform, vertexStride: stride, colorStride: 4, outputStride: 4 });
  116. return interpolated.array;
  117. }
  118. protected static getInterpolatedTransparency(webgl: WebGLContext, input: { vertices: Float32Array, vertexCount: number, values: BaseValues, stride: 3 | 4, colorType: 'volumeInstance' }) {
  119. const { values, vertexCount, vertices, colorType, stride } = input;
  120. const transparencyGridTransform = values.uTransparencyGridTransform.ref.value;
  121. const transparencyGridDim = values.uTransparencyGridDim.ref.value;
  122. const transparencyTexDim = values.uTransparencyTexDim.ref.value;
  123. const aTransform = values.aTransform.ref.value;
  124. const instanceCount = values.uInstanceCount.ref.value;
  125. const transparencyGrid = readAlphaTexture(webgl, values.tTransparencyGrid.ref.value).array;
  126. const interpolated = getTrilinearlyInterpolated({ vertexCount, instanceCount, transformBuffer: aTransform, positionBuffer: vertices, colorType, grid: transparencyGrid, gridDim: transparencyGridDim, gridTexDim: transparencyTexDim, gridTransform: transparencyGridTransform, vertexStride: stride, colorStride: 4, outputStride: 1, itemOffset: 3 });
  127. return interpolated.array;
  128. }
  129. protected static quantizeColors(colorArray: Uint8Array, vertexCount: number) {
  130. if (vertexCount <= 1024) return;
  131. const rgb = Vec3();
  132. const min = Vec3();
  133. const max = Vec3();
  134. const sum = Vec3();
  135. const colorMap = new Map<Color, Color>();
  136. const colorComparers = [
  137. (colors: Color[], i: number, j: number) => (Color.toVec3(rgb, colors[i])[0] - Color.toVec3(rgb, colors[j])[0]),
  138. (colors: Color[], i: number, j: number) => (Color.toVec3(rgb, colors[i])[1] - Color.toVec3(rgb, colors[j])[1]),
  139. (colors: Color[], i: number, j: number) => (Color.toVec3(rgb, colors[i])[2] - Color.toVec3(rgb, colors[j])[2]),
  140. ];
  141. const medianCut = (colors: Color[], l: number, r: number, depth: number) => {
  142. if (l > r) return;
  143. if (l === r || depth >= 10) {
  144. // Find the average color.
  145. Vec3.set(sum, 0, 0, 0);
  146. for (let i = l; i <= r; ++i) {
  147. Color.toVec3(rgb, colors[i]);
  148. Vec3.add(sum, sum, rgb);
  149. }
  150. Vec3.round(rgb, Vec3.scale(rgb, sum, 1 / (r - l + 1)));
  151. const averageColor = Color.fromArray(rgb, 0);
  152. for (let i = l; i <= r; ++i) colorMap.set(colors[i], averageColor);
  153. return;
  154. }
  155. // Find the color channel with the greatest range.
  156. Vec3.set(min, 255, 255, 255);
  157. Vec3.set(max, 0, 0, 0);
  158. for (let i = l; i <= r; ++i) {
  159. Color.toVec3(rgb, colors[i]);
  160. for (let j = 0; j < 3; ++j) {
  161. Vec3.min(min, min, rgb);
  162. Vec3.max(max, max, rgb);
  163. }
  164. }
  165. let k = 0;
  166. if (max[1] - min[1] > max[k] - min[k]) k = 1;
  167. if (max[2] - min[2] > max[k] - min[k]) k = 2;
  168. sort(colors, l, r + 1, colorComparers[k], arraySwap);
  169. const m = (l + r) >> 1;
  170. medianCut(colors, l, m, depth + 1);
  171. medianCut(colors, m + 1, r, depth + 1);
  172. };
  173. // Create an array of unique colors and use the median cut algorithm.
  174. const colorSet = new Set<Color>();
  175. for (let i = 0; i < vertexCount; ++i) {
  176. colorSet.add(Color.fromArray(colorArray, i * 3));
  177. }
  178. const colors = Array.from(colorSet);
  179. medianCut(colors, 0, colors.length - 1, 0);
  180. // Map actual colors to quantized colors.
  181. for (let i = 0; i < vertexCount; ++i) {
  182. const color = colorMap.get(Color.fromArray(colorArray, i * 3));
  183. Color.toArray(color!, colorArray, i * 3);
  184. }
  185. }
  186. protected static getInstance(input: AddMeshInput, instanceIndex: number) {
  187. const { mesh, meshes } = input;
  188. if (mesh !== undefined) {
  189. return mesh;
  190. } else {
  191. const mesh = meshes![instanceIndex];
  192. return {
  193. vertices: mesh.vertexBuffer.ref.value,
  194. normals: mesh.normalBuffer.ref.value,
  195. indices: mesh.indexBuffer.ref.value,
  196. groups: mesh.groupBuffer.ref.value,
  197. vertexCount: mesh.vertexCount,
  198. drawCount: mesh.triangleCount * 3
  199. };
  200. }
  201. }
  202. protected static getColor(vertexIndex: number, geoData: MeshGeoData, interpolatedColors?: Uint8Array, interpolatedOverpaint?: Uint8Array): Color {
  203. const { values, instanceIndex, isGeoTexture, mode, groups } = geoData;
  204. const groupCount = values.uGroupCount.ref.value;
  205. const colorType = values.dColorType.ref.value;
  206. const uColor = values.uColor.ref.value;
  207. const tColor = values.tColor.ref.value.array;
  208. const overpaintType = values.dOverpaintType.ref.value;
  209. const dOverpaint = values.dOverpaint.ref.value;
  210. const tOverpaint = values.tOverpaint.ref.value.array;
  211. let vertexCount = geoData.vertexCount;
  212. if (mode === 'lines') {
  213. vertexIndex *= 2;
  214. vertexCount *= 2;
  215. }
  216. let color: Color;
  217. switch (colorType) {
  218. case 'uniform':
  219. color = Color.fromNormalizedArray(uColor, 0);
  220. break;
  221. case 'instance':
  222. color = Color.fromArray(tColor, instanceIndex * 3);
  223. break;
  224. case 'group': {
  225. const group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex];
  226. color = Color.fromArray(tColor, group * 3);
  227. break;
  228. }
  229. case 'groupInstance': {
  230. const group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex];
  231. color = Color.fromArray(tColor, (instanceIndex * groupCount + group) * 3);
  232. break;
  233. }
  234. case 'vertex':
  235. color = Color.fromArray(tColor, vertexIndex * 3);
  236. break;
  237. case 'vertexInstance':
  238. color = Color.fromArray(tColor, (instanceIndex * vertexCount + vertexIndex) * 3);
  239. break;
  240. case 'volume':
  241. color = Color.fromArray(interpolatedColors!, vertexIndex * 3);
  242. break;
  243. case 'volumeInstance':
  244. color = Color.fromArray(interpolatedColors!, (instanceIndex * vertexCount + vertexIndex) * 3);
  245. break;
  246. default: throw new Error('Unsupported color type.');
  247. }
  248. if (dOverpaint) {
  249. let overpaintColor: Color;
  250. let overpaintAlpha: number;
  251. switch (overpaintType) {
  252. case 'groupInstance': {
  253. const group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex];
  254. const idx = (instanceIndex * groupCount + group) * 4;
  255. overpaintColor = Color.fromArray(tOverpaint, idx);
  256. overpaintAlpha = tOverpaint[idx + 3] / 255;
  257. break;
  258. }
  259. case 'vertexInstance': {
  260. const idx = (instanceIndex * vertexCount + vertexIndex) * 4;
  261. overpaintColor = Color.fromArray(tOverpaint, idx);
  262. overpaintAlpha = tOverpaint[idx + 3] / 255;
  263. break;
  264. }
  265. case 'volumeInstance': {
  266. const idx = (instanceIndex * vertexCount + vertexIndex) * 4;
  267. overpaintColor = Color.fromArray(interpolatedOverpaint!, idx);
  268. overpaintAlpha = interpolatedOverpaint![idx + 3] / 255;
  269. break;
  270. }
  271. default: throw new Error('Unsupported overpaint type.');
  272. }
  273. // interpolate twice to avoid darkening due to empty overpaint
  274. overpaintColor = Color.interpolate(color, overpaintColor, overpaintAlpha);
  275. color = Color.interpolate(color, overpaintColor, overpaintAlpha);
  276. }
  277. return color;
  278. }
  279. protected static getTransparency(vertexIndex: number, geoData: MeshGeoData, interpolatedTransparency?: Uint8Array): number {
  280. const { values, instanceIndex, isGeoTexture, mode, groups } = geoData;
  281. const groupCount = values.uGroupCount.ref.value;
  282. const dTransparency = values.dTransparency.ref.value;
  283. const tTransparency = values.tTransparency.ref.value.array;
  284. const transparencyType = values.dTransparencyType.ref.value;
  285. let vertexCount = geoData.vertexCount;
  286. if (mode === 'lines') {
  287. vertexIndex *= 2;
  288. vertexCount *= 2;
  289. }
  290. let transparency: number = 0;
  291. if (dTransparency) {
  292. switch (transparencyType) {
  293. case 'groupInstance': {
  294. const group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex];
  295. const idx = (instanceIndex * groupCount + group);
  296. transparency = tTransparency[idx] / 255;
  297. break;
  298. }
  299. case 'vertexInstance': {
  300. const idx = (instanceIndex * vertexCount + vertexIndex);
  301. transparency = tTransparency[idx] / 255;
  302. break;
  303. }
  304. case 'volumeInstance': {
  305. const idx = (instanceIndex * vertexCount + vertexIndex);
  306. transparency = interpolatedTransparency![idx] / 255;
  307. break;
  308. }
  309. default: throw new Error('Unsupported transparency type.');
  310. }
  311. }
  312. return transparency;
  313. }
  314. protected abstract addMeshWithColors(input: AddMeshInput): Promise<void>;
  315. private async addMesh(values: MeshValues, webgl: WebGLContext, ctx: RuntimeContext) {
  316. const aPosition = values.aPosition.ref.value;
  317. const aNormal = values.aNormal.ref.value;
  318. const aGroup = values.aGroup.ref.value;
  319. const originalData = Mesh.getOriginalData(values);
  320. let indices: Uint32Array;
  321. let vertexCount: number;
  322. let drawCount: number;
  323. if (originalData) {
  324. indices = originalData.indexBuffer;
  325. vertexCount = originalData.vertexCount;
  326. drawCount = originalData.triangleCount * 3;
  327. } else {
  328. indices = values.elements.ref.value;
  329. vertexCount = values.uVertexCount.ref.value;
  330. drawCount = values.drawCount.ref.value;
  331. }
  332. await this.addMeshWithColors({ mesh: { vertices: aPosition, normals: aNormal, indices, groups: aGroup, vertexCount, drawCount }, meshes: undefined, values, isGeoTexture: false, mode: 'triangles', webgl, ctx });
  333. }
  334. private async addLines(values: LinesValues, webgl: WebGLContext, ctx: RuntimeContext) {
  335. const aStart = values.aStart.ref.value;
  336. const aEnd = values.aEnd.ref.value;
  337. const aGroup = values.aGroup.ref.value;
  338. const vertexCount = (values.uVertexCount.ref.value / 4) * 2;
  339. const drawCount = values.drawCount.ref.value / (2 * 3);
  340. const n = (vertexCount / 2);
  341. const vertices = new Float32Array(n * 2 * 3);
  342. for (let i = 0; i < n; ++i) {
  343. vertices[i * 6] = aStart[i * 4 * 3];
  344. vertices[i * 6 + 1] = aStart[i * 4 * 3 + 1];
  345. vertices[i * 6 + 2] = aStart[i * 4 * 3 + 2];
  346. vertices[i * 6 + 3] = aEnd[i * 4 * 3];
  347. vertices[i * 6 + 4] = aEnd[i * 4 * 3 + 1];
  348. vertices[i * 6 + 5] = aEnd[i * 4 * 3 + 2];
  349. }
  350. await this.addMeshWithColors({ mesh: { vertices, normals: undefined, indices: undefined, groups: aGroup, vertexCount, drawCount }, meshes: undefined, values, isGeoTexture: false, mode: 'lines', webgl, ctx });
  351. }
  352. private async addPoints(values: PointsValues, webgl: WebGLContext, ctx: RuntimeContext) {
  353. const aPosition = values.aPosition.ref.value;
  354. const aGroup = values.aGroup.ref.value;
  355. const vertexCount = values.uVertexCount.ref.value;
  356. const drawCount = values.drawCount.ref.value;
  357. await this.addMeshWithColors({ mesh: { vertices: aPosition, normals: undefined, indices: undefined, groups: aGroup, vertexCount, drawCount }, meshes: undefined, values, isGeoTexture: false, mode: 'points', webgl, ctx });
  358. }
  359. private async addSpheres(values: SpheresValues, webgl: WebGLContext, ctx: RuntimeContext) {
  360. const center = Vec3();
  361. const aPosition = values.aPosition.ref.value;
  362. const aGroup = values.aGroup.ref.value;
  363. const instanceCount = values.instanceCount.ref.value;
  364. const vertexCount = values.uVertexCount.ref.value;
  365. const meshes: Mesh[] = [];
  366. const sphereCount = vertexCount / 4 * instanceCount;
  367. let detail: number;
  368. if (sphereCount < 2000) detail = 3;
  369. else if (sphereCount < 20000) detail = 2;
  370. else detail = 1;
  371. for (let instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
  372. const state = MeshBuilder.createState(512, 256);
  373. for (let i = 0; i < vertexCount; i += 4) {
  374. v3fromArray(center, aPosition, i * 3);
  375. const group = aGroup[i];
  376. const radius = MeshExporter.getSize(values, instanceIndex, group);
  377. state.currentGroup = group;
  378. addSphere(state, center, radius, detail);
  379. }
  380. meshes.push(MeshBuilder.getMesh(state));
  381. }
  382. await this.addMeshWithColors({ mesh: undefined, meshes, values, isGeoTexture: false, mode: 'triangles', webgl, ctx });
  383. }
  384. private async addCylinders(values: CylindersValues, webgl: WebGLContext, ctx: RuntimeContext) {
  385. const start = Vec3();
  386. const end = Vec3();
  387. const aStart = values.aStart.ref.value;
  388. const aEnd = values.aEnd.ref.value;
  389. const aScale = values.aScale.ref.value;
  390. const aCap = values.aCap.ref.value;
  391. const aGroup = values.aGroup.ref.value;
  392. const instanceCount = values.instanceCount.ref.value;
  393. const vertexCount = values.uVertexCount.ref.value;
  394. const meshes: Mesh[] = [];
  395. const cylinderCount = vertexCount / 6 * instanceCount;
  396. let radialSegments: number;
  397. if (cylinderCount < 2000) radialSegments = 36;
  398. else if (cylinderCount < 20000) radialSegments = 24;
  399. else radialSegments = 12;
  400. for (let instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
  401. const state = MeshBuilder.createState(512, 256);
  402. for (let i = 0; i < vertexCount; i += 6) {
  403. v3fromArray(start, aStart, i * 3);
  404. v3fromArray(end, aEnd, i * 3);
  405. const group = aGroup[i];
  406. const radius = MeshExporter.getSize(values, instanceIndex, group) * aScale[i];
  407. const cap = aCap[i];
  408. const topCap = cap === 1 || cap === 3;
  409. const bottomCap = cap >= 2;
  410. const cylinderProps = { radiusTop: radius, radiusBottom: radius, topCap, bottomCap, radialSegments };
  411. state.currentGroup = aGroup[i];
  412. addCylinder(state, start, end, 1, cylinderProps);
  413. }
  414. meshes.push(MeshBuilder.getMesh(state));
  415. }
  416. await this.addMeshWithColors({ mesh: undefined, meshes, values, isGeoTexture: false, mode: 'triangles', webgl, ctx });
  417. }
  418. private async addTextureMesh(values: TextureMeshValues, webgl: WebGLContext, ctx: RuntimeContext) {
  419. if (!webgl.namedFramebuffers[GeoExportName]) {
  420. webgl.namedFramebuffers[GeoExportName] = webgl.resources.framebuffer();
  421. }
  422. const framebuffer = webgl.namedFramebuffers[GeoExportName];
  423. const [width, height] = values.uGeoTexDim.ref.value;
  424. const vertices = new Float32Array(width * height * 4);
  425. const normals = new Float32Array(width * height * 4);
  426. const groups = webgl.isWebGL2 ? new Uint8Array(width * height * 4) : new Float32Array(width * height * 4);
  427. framebuffer.bind();
  428. values.tPosition.ref.value.attachFramebuffer(framebuffer, 0);
  429. webgl.readPixels(0, 0, width, height, vertices);
  430. values.tNormal.ref.value.attachFramebuffer(framebuffer, 0);
  431. webgl.readPixels(0, 0, width, height, normals);
  432. values.tGroup.ref.value.attachFramebuffer(framebuffer, 0);
  433. webgl.readPixels(0, 0, width, height, groups);
  434. const vertexCount = values.uVertexCount.ref.value;
  435. const drawCount = values.drawCount.ref.value;
  436. await this.addMeshWithColors({ mesh: { vertices, normals, indices: undefined, groups, vertexCount, drawCount }, meshes: undefined, values, isGeoTexture: true, mode: 'triangles', webgl, ctx });
  437. }
  438. add(renderObject: GraphicsRenderObject, webgl: WebGLContext, ctx: RuntimeContext) {
  439. if (!renderObject.state.visible) return;
  440. if (renderObject.values.drawCount.ref.value === 0) return;
  441. if (renderObject.values.instanceCount.ref.value === 0) return;
  442. switch (renderObject.type) {
  443. case 'mesh':
  444. return this.addMesh(renderObject.values as MeshValues, webgl, ctx);
  445. case 'lines':
  446. return this.addLines(renderObject.values as LinesValues, webgl, ctx);
  447. case 'points':
  448. return this.addPoints(renderObject.values as PointsValues, webgl, ctx);
  449. case 'spheres':
  450. return this.addSpheres(renderObject.values as SpheresValues, webgl, ctx);
  451. case 'cylinders':
  452. return this.addCylinders(renderObject.values as CylindersValues, webgl, ctx);
  453. case 'texture-mesh':
  454. return this.addTextureMesh(renderObject.values as TextureMeshValues, webgl, ctx);
  455. }
  456. }
  457. abstract getData(ctx: RuntimeContext): Promise<D>;
  458. abstract getBlob(ctx: RuntimeContext): Promise<Blob>;
  459. }