color-smoothing.ts 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. /**
  2. * Copyright (c) 2021 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';
  7. import { createComputeRenderable, ComputeRenderable } from '../../../mol-gl/renderable';
  8. import { WebGLContext } from '../../../mol-gl/webgl/context';
  9. import { Texture } from '../../../mol-gl/webgl/texture';
  10. import { ShaderCode } from '../../../mol-gl/shader-code';
  11. import { createComputeRenderItem } from '../../../mol-gl/webgl/render-item';
  12. import { ValueSpec, AttributeSpec, UniformSpec, TextureSpec, Values, DefineSpec } from '../../../mol-gl/renderable/schema';
  13. import { quad_vert } from '../../../mol-gl/shader/quad.vert';
  14. import { normalize_frag } from '../../../mol-gl/shader/compute/color-smoothing/normalize.frag';
  15. import { QuadSchema, QuadValues } from '../../../mol-gl/compute/util';
  16. import { Vec2, Vec3, Vec4 } from '../../../mol-math/linear-algebra';
  17. import { Box3D, Sphere3D } from '../../../mol-math/geometry';
  18. import { accumulate_frag } from '../../../mol-gl/shader/compute/color-smoothing/accumulate.frag';
  19. import { accumulate_vert } from '../../../mol-gl/shader/compute/color-smoothing/accumulate.vert';
  20. import { isWebGL2 } from '../../../mol-gl/webgl/compat';
  21. import { TextureMeshValues } from '../../../mol-gl/renderable/texture-mesh';
  22. export const ColorAccumulateSchema = {
  23. drawCount: ValueSpec('number'),
  24. instanceCount: ValueSpec('number'),
  25. stride: ValueSpec('number'),
  26. uTotalCount: UniformSpec('i'),
  27. uInstanceCount: UniformSpec('i'),
  28. uGroupCount: UniformSpec('i'),
  29. aTransform: AttributeSpec('float32', 16, 1),
  30. aInstance: AttributeSpec('float32', 1, 1),
  31. aSample: AttributeSpec('float32', 1, 0),
  32. uGeoTexDim: UniformSpec('v2', 'buffered'),
  33. tPosition: TextureSpec('texture', 'rgba', 'float', 'nearest'),
  34. tGroup: TextureSpec('texture', 'rgba', 'float', 'nearest'),
  35. uColorTexDim: UniformSpec('v2'),
  36. tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
  37. dColorType: DefineSpec('string', ['group', 'groupInstance', 'vertex', 'vertexInstance']),
  38. uCurrentSlice: UniformSpec('f'),
  39. uCurrentX: UniformSpec('f'),
  40. uCurrentY: UniformSpec('f'),
  41. uBboxMin: UniformSpec('v3', 'material'),
  42. uBboxSize: UniformSpec('v3', 'material'),
  43. uResolution: UniformSpec('f', 'material'),
  44. };
  45. type ColorAccumulateValues = Values<typeof ColorAccumulateSchema>
  46. const ColorAccumulateName = 'color-accumulate';
  47. const ColorCountName = 'color-count';
  48. interface AccumulateInput {
  49. vertexCount: number
  50. instanceCount: number
  51. groupCount: number
  52. transformBuffer: Float32Array
  53. instanceBuffer: Float32Array
  54. positionTexture: Texture
  55. groupTexture: Texture
  56. colorData: Texture
  57. colorType: 'group' | 'groupInstance'
  58. }
  59. function getSampleBuffer(sampleCount: number, stride: number) {
  60. const sampleBuffer = new Float32Array(sampleCount);
  61. for (let i = 0; i < sampleCount; ++i) {
  62. sampleBuffer[i] = i * stride;
  63. }
  64. return sampleBuffer;
  65. }
  66. function getAccumulateRenderable(ctx: WebGLContext, input: AccumulateInput, box: Box3D, resolution: number, stride: number): ComputeRenderable<ColorAccumulateValues> {
  67. if (ctx.namedComputeRenderables[ColorAccumulateName]) {
  68. const extent = Vec3.sub(Vec3(), box.max, box.min);
  69. const v = ctx.namedComputeRenderables[ColorAccumulateName].values as ColorAccumulateValues;
  70. const sampleCount = Math.round(input.vertexCount / stride);
  71. if (sampleCount > v.drawCount.ref.value || stride !== v.stride.ref.value) {
  72. ValueCell.update(v.aSample, getSampleBuffer(sampleCount, stride));
  73. }
  74. ValueCell.updateIfChanged(v.drawCount, sampleCount);
  75. ValueCell.updateIfChanged(v.instanceCount, input.instanceCount);
  76. ValueCell.updateIfChanged(v.stride, stride);
  77. ValueCell.updateIfChanged(v.uTotalCount, input.vertexCount);
  78. ValueCell.updateIfChanged(v.uInstanceCount, input.instanceCount);
  79. ValueCell.updateIfChanged(v.uGroupCount, input.groupCount);
  80. ValueCell.update(v.aTransform, input.transformBuffer);
  81. ValueCell.update(v.aInstance, input.instanceBuffer);
  82. ValueCell.update(v.uGeoTexDim, Vec2.set(v.uGeoTexDim.ref.value, input.positionTexture.getWidth(), input.positionTexture.getHeight()));
  83. ValueCell.update(v.tPosition, input.positionTexture);
  84. ValueCell.update(v.tGroup, input.groupTexture);
  85. ValueCell.update(v.uColorTexDim, Vec2.set(v.uColorTexDim.ref.value, input.colorData.getWidth(), input.colorData.getHeight()));
  86. ValueCell.update(v.tColor, input.colorData);
  87. ValueCell.updateIfChanged(v.dColorType, input.colorType);
  88. ValueCell.updateIfChanged(v.uCurrentSlice, 0);
  89. ValueCell.updateIfChanged(v.uCurrentX, 0);
  90. ValueCell.updateIfChanged(v.uCurrentY, 0);
  91. ValueCell.update(v.uBboxMin, box.min);
  92. ValueCell.update(v.uBboxSize, extent);
  93. ValueCell.updateIfChanged(v.uResolution, resolution);
  94. ctx.namedComputeRenderables[ColorAccumulateName].update();
  95. } else {
  96. ctx.namedComputeRenderables[ColorAccumulateName] = createAccumulateRenderable(ctx, input, box, resolution, stride);
  97. }
  98. return ctx.namedComputeRenderables[ColorAccumulateName];
  99. }
  100. function createAccumulateRenderable(ctx: WebGLContext, input: AccumulateInput, box: Box3D, resolution: number, stride: number) {
  101. const extent = Vec3.sub(Vec3(), box.max, box.min);
  102. const sampleCount = Math.round(input.vertexCount / stride);
  103. const values: ColorAccumulateValues = {
  104. drawCount: ValueCell.create(sampleCount),
  105. instanceCount: ValueCell.create(input.instanceCount),
  106. stride: ValueCell.create(stride),
  107. uTotalCount: ValueCell.create(input.vertexCount),
  108. uInstanceCount: ValueCell.create(input.instanceCount),
  109. uGroupCount: ValueCell.create(input.groupCount),
  110. aTransform: ValueCell.create(input.transformBuffer),
  111. aInstance: ValueCell.create(input.instanceBuffer),
  112. aSample: ValueCell.create(getSampleBuffer(sampleCount, stride)),
  113. uGeoTexDim: ValueCell.create(Vec2.create(input.positionTexture.getWidth(), input.positionTexture.getHeight())),
  114. tPosition: ValueCell.create(input.positionTexture),
  115. tGroup: ValueCell.create(input.groupTexture),
  116. uColorTexDim: ValueCell.create(Vec2.create(input.colorData.getWidth(), input.colorData.getHeight())),
  117. tColor: ValueCell.create(input.colorData),
  118. dColorType: ValueCell.create(input.colorType),
  119. uCurrentSlice: ValueCell.create(0),
  120. uCurrentX: ValueCell.create(0),
  121. uCurrentY: ValueCell.create(0),
  122. uBboxMin: ValueCell.create(box.min),
  123. uBboxSize: ValueCell.create(extent),
  124. uResolution: ValueCell.create(resolution),
  125. };
  126. const schema = { ...ColorAccumulateSchema };
  127. const shaderCode = ShaderCode('accumulate', accumulate_vert, accumulate_frag, { drawBuffers: 'required' });
  128. const renderItem = createComputeRenderItem(ctx, 'points', shaderCode, schema, values);
  129. return createComputeRenderable(renderItem, values);
  130. }
  131. function setAccumulateDefaults(ctx: WebGLContext) {
  132. const { gl, state } = ctx;
  133. state.disable(gl.CULL_FACE);
  134. state.enable(gl.BLEND);
  135. state.disable(gl.DEPTH_TEST);
  136. state.enable(gl.SCISSOR_TEST);
  137. state.depthMask(false);
  138. state.clearColor(0, 0, 0, 0);
  139. state.blendFunc(gl.ONE, gl.ONE);
  140. state.blendEquation(gl.FUNC_ADD);
  141. }
  142. //
  143. export const ColorNormalizeSchema = {
  144. ...QuadSchema,
  145. tColor: TextureSpec('texture', 'rgba', 'float', 'nearest'),
  146. tCount: TextureSpec('texture', 'alpha', 'float', 'nearest'),
  147. uTexSize: UniformSpec('v2'),
  148. };
  149. type ColorNormalizeValues = Values<typeof ColorNormalizeSchema>
  150. const ColorNormalizeName = 'color-normalize';
  151. function getNormalizeRenderable(ctx: WebGLContext, color: Texture, count: Texture): ComputeRenderable<ColorNormalizeValues> {
  152. if (ctx.namedComputeRenderables[ColorNormalizeName]) {
  153. const v = ctx.namedComputeRenderables[ColorNormalizeName].values as ColorNormalizeValues;
  154. ValueCell.update(v.tColor, color);
  155. ValueCell.update(v.tCount, count);
  156. ValueCell.update(v.uTexSize, Vec2.set(v.uTexSize.ref.value, color.getWidth(), color.getHeight()));
  157. ctx.namedComputeRenderables[ColorNormalizeName].update();
  158. } else {
  159. ctx.namedComputeRenderables[ColorNormalizeName] = createColorNormalizeRenderable(ctx, color, count);
  160. }
  161. return ctx.namedComputeRenderables[ColorNormalizeName];
  162. }
  163. function createColorNormalizeRenderable(ctx: WebGLContext, color: Texture, count: Texture) {
  164. const values: ColorNormalizeValues = {
  165. ...QuadValues,
  166. tColor: ValueCell.create(color),
  167. tCount: ValueCell.create(count),
  168. uTexSize: ValueCell.create(Vec2.create(color.getWidth(), color.getHeight())),
  169. };
  170. const schema = { ...ColorNormalizeSchema };
  171. const shaderCode = ShaderCode('normalize', quad_vert, normalize_frag);
  172. const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values);
  173. return createComputeRenderable(renderItem, values);
  174. }
  175. function setNormalizeDefaults(ctx: WebGLContext) {
  176. const { gl, state } = ctx;
  177. state.disable(gl.CULL_FACE);
  178. state.enable(gl.BLEND);
  179. state.disable(gl.DEPTH_TEST);
  180. state.enable(gl.SCISSOR_TEST);
  181. state.depthMask(false);
  182. state.clearColor(0, 0, 0, 0);
  183. state.blendFunc(gl.ONE, gl.ONE);
  184. state.blendEquation(gl.FUNC_ADD);
  185. }
  186. //
  187. function getTexture2dSize(gridDim: Vec3) {
  188. const area = gridDim[0] * gridDim[1] * gridDim[2];
  189. const squareDim = Math.sqrt(area);
  190. const powerOfTwoSize = Math.pow(2, Math.ceil(Math.log(squareDim) / Math.log(2)));
  191. let texDimX = 0;
  192. let texDimY = gridDim[1];
  193. let texRows = 1;
  194. let texCols = gridDim[2];
  195. if (powerOfTwoSize < gridDim[0] * gridDim[2]) {
  196. texCols = Math.floor(powerOfTwoSize / gridDim[0]);
  197. texRows = Math.ceil(gridDim[2] / texCols);
  198. texDimX = texCols * gridDim[0];
  199. texDimY *= texRows;
  200. } else {
  201. texDimX = gridDim[0] * gridDim[2];
  202. }
  203. // console.log(texDimX, texDimY, texDimY < powerOfTwoSize ? powerOfTwoSize : powerOfTwoSize * 2);
  204. return { texDimX, texDimY, texRows, texCols, powerOfTwoSize: texDimY < powerOfTwoSize ? powerOfTwoSize : powerOfTwoSize * 2 };
  205. }
  206. interface ColorSmoothingInput extends AccumulateInput {
  207. boundingSphere: Sphere3D
  208. invariantBoundingSphere: Sphere3D
  209. }
  210. export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolution: number, stride: number, webgl: WebGLContext, texture?: Texture) {
  211. const { drawBuffers } = webgl.extensions;
  212. if (!drawBuffers) throw new Error('need WebGL draw buffers');
  213. const { gl, resources, state, extensions: { colorBufferHalfFloat, textureHalfFloat } } = webgl;
  214. const isInstanceType = input.colorType.endsWith('Instance');
  215. const box = Box3D.fromSphere3D(Box3D(), isInstanceType ? input.boundingSphere : input.invariantBoundingSphere);
  216. const scaleFactor = 1 / resolution;
  217. const scaledBox = Box3D.scale(Box3D(), box, scaleFactor);
  218. const gridDim = Box3D.size(Vec3(), scaledBox);
  219. Vec3.ceil(gridDim, gridDim);
  220. Vec3.add(gridDim, gridDim, Vec3.create(2, 2, 2));
  221. const { min } = box;
  222. const [dx, dy, dz] = gridDim;
  223. const { texDimX: width, texDimY: height, texCols } = getTexture2dSize(gridDim);
  224. // console.log({ width, height, texCols, dim, resolution });
  225. if (!webgl.namedFramebuffers[ColorAccumulateName]) {
  226. webgl.namedFramebuffers[ColorAccumulateName] = webgl.resources.framebuffer();
  227. }
  228. const framebuffer = webgl.namedFramebuffers[ColorAccumulateName];
  229. if (isWebGL2(gl)) {
  230. if (!webgl.namedTextures[ColorAccumulateName]) {
  231. webgl.namedTextures[ColorAccumulateName] = colorBufferHalfFloat && textureHalfFloat
  232. ? resources.texture('image-float16', 'rgba', 'fp16', 'nearest')
  233. : resources.texture('image-float32', 'rgba', 'float', 'nearest');
  234. }
  235. if (!webgl.namedTextures[ColorCountName]) {
  236. webgl.namedTextures[ColorCountName] = resources.texture('image-float32', 'alpha', 'float', 'nearest');
  237. }
  238. } else {
  239. // in webgl1 drawbuffers must be in the same format for some reason
  240. // this is quite wasteful but good enough for medium size meshes
  241. if (!webgl.namedTextures[ColorAccumulateName]) {
  242. webgl.namedTextures[ColorAccumulateName] = resources.texture('image-float32', 'rgba', 'float', 'nearest');
  243. }
  244. if (!webgl.namedTextures[ColorCountName]) {
  245. webgl.namedTextures[ColorCountName] = resources.texture('image-float32', 'rgba', 'float', 'nearest');
  246. }
  247. }
  248. const accumulateTexture = webgl.namedTextures[ColorAccumulateName];
  249. const countTexture = webgl.namedTextures[ColorCountName];
  250. accumulateTexture.define(width, height);
  251. countTexture.define(width, height);
  252. accumulateTexture.attachFramebuffer(framebuffer, 0);
  253. countTexture.attachFramebuffer(framebuffer, 1);
  254. const accumulateRenderable = getAccumulateRenderable(webgl, input, box, resolution, stride);
  255. state.currentRenderItemId = -1;
  256. framebuffer.bind();
  257. drawBuffers.drawBuffers([
  258. drawBuffers.COLOR_ATTACHMENT0,
  259. drawBuffers.COLOR_ATTACHMENT1,
  260. ]);
  261. const { uCurrentSlice, uCurrentX, uCurrentY } = accumulateRenderable.values;
  262. setAccumulateDefaults(webgl);
  263. gl.viewport(0, 0, width, height);
  264. gl.scissor(0, 0, width, height);
  265. gl.clear(gl.COLOR_BUFFER_BIT);
  266. ValueCell.update(uCurrentY, 0);
  267. let currCol = 0;
  268. let currY = 0;
  269. let currX = 0;
  270. for (let i = 0; i < dz; ++i) {
  271. if (currCol >= texCols) {
  272. currCol -= texCols;
  273. currY += dy;
  274. currX = 0;
  275. ValueCell.update(uCurrentY, currY);
  276. }
  277. // console.log({ i, currX, currY });
  278. ValueCell.update(uCurrentX, currX);
  279. ValueCell.update(uCurrentSlice, i);
  280. gl.viewport(currX, currY, dx, dy);
  281. gl.scissor(currX, currY, dx, dy);
  282. accumulateRenderable.render();
  283. ++currCol;
  284. currX += dx;
  285. }
  286. accumulateTexture.detachFramebuffer(framebuffer, 0);
  287. countTexture.detachFramebuffer(framebuffer, 1);
  288. drawBuffers.drawBuffers([gl.COLOR_ATTACHMENT0, gl.NONE]);
  289. // const accImage = new Float32Array(width * height * 4);
  290. // accumulateTexture.attachFramebuffer(framebuffer, 0);
  291. // webgl.readPixels(0, 0, width, height, accImage);
  292. // console.log(accImage);
  293. // printTextureImage({ array: accImage, width, height }, { scale: 1 });
  294. // const cntImage = new Float32Array(width * height * 4);
  295. // countTexture.attachFramebuffer(framebuffer, 0);
  296. // webgl.readPixels(0, 0, width, height, cntImage);
  297. // console.log(cntImage);
  298. // printTextureImage({ array: cntImage, width, height }, { scale: 1 });
  299. // normalize
  300. if (!texture) texture = resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
  301. texture.define(width, height);
  302. const normalizeRenderable = getNormalizeRenderable(webgl, accumulateTexture, countTexture);
  303. state.currentRenderItemId = -1;
  304. setNormalizeDefaults(webgl);
  305. texture.attachFramebuffer(framebuffer, 0);
  306. gl.viewport(0, 0, width, height);
  307. gl.scissor(0, 0, width, height);
  308. gl.clear(gl.COLOR_BUFFER_BIT);
  309. normalizeRenderable.render();
  310. // const normImage = new Uint8Array(width * height * 4);
  311. // texture.attachFramebuffer(framebuffer, 0);
  312. // webgl.readPixels(0, 0, width, height, normImage);
  313. // console.log(normImage);
  314. // printTextureImage({ array: normImage, width, height }, { scale: 1 });
  315. const gridTransform = Vec4.create(min[0], min[1], min[2], scaleFactor);
  316. const type = isInstanceType ? 'volumeInstance' : 'volume';
  317. return { texture, gridDim, gridTexDim: Vec2.create(width, height), gridTransform, type };
  318. }
  319. //
  320. const ColorSmoothingRgbName = 'color-smoothing-rgb';
  321. const ColorSmoothingRgbaName = 'color-smoothing-rgba';
  322. const ColorSmoothingAlphaName = 'color-smoothing-alpha';
  323. function isSupportedColorType(x: string): x is 'group' | 'groupInstance' {
  324. return x === 'group' || x === 'groupInstance';
  325. }
  326. export function applyTextureMeshColorSmoothing(values: TextureMeshValues, resolution: number, stride: number, webgl: WebGLContext, colorTexture?: Texture) {
  327. if (!isSupportedColorType(values.dColorType.ref.value)) return;
  328. stride *= 3; // triple because TextureMesh is never indexed (no elements buffer)
  329. if (!webgl.namedTextures[ColorSmoothingRgbName]) {
  330. webgl.namedTextures[ColorSmoothingRgbName] = webgl.resources.texture('image-uint8', 'rgb', 'ubyte', 'nearest');
  331. }
  332. const colorData = webgl.namedTextures[ColorSmoothingRgbName];
  333. colorData.load(values.tColor.ref.value);
  334. const smoothingData = calcTextureMeshColorSmoothing({
  335. vertexCount: values.uVertexCount.ref.value,
  336. instanceCount: values.uInstanceCount.ref.value,
  337. groupCount: values.uGroupCount.ref.value,
  338. transformBuffer: values.aTransform.ref.value,
  339. instanceBuffer: values.aInstance.ref.value,
  340. positionTexture: values.tPosition.ref.value,
  341. groupTexture: values.tGroup.ref.value,
  342. colorData,
  343. colorType: values.dColorType.ref.value,
  344. boundingSphere: values.boundingSphere.ref.value,
  345. invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
  346. }, resolution, stride, webgl, colorTexture);
  347. ValueCell.updateIfChanged(values.dColorType, smoothingData.type);
  348. ValueCell.update(values.tColorGrid, smoothingData.texture);
  349. ValueCell.update(values.uColorTexDim, smoothingData.gridTexDim);
  350. ValueCell.update(values.uColorGridDim, smoothingData.gridDim);
  351. ValueCell.update(values.uColorGridTransform, smoothingData.gridTransform);
  352. }
  353. function isSupportedOverpaintType(x: string): x is 'groupInstance' {
  354. return x === 'groupInstance';
  355. }
  356. export function applyTextureMeshOverpaintSmoothing(values: TextureMeshValues, resolution: number, stride: number, webgl: WebGLContext, colorTexture?: Texture) {
  357. if (!isSupportedOverpaintType(values.dOverpaintType.ref.value)) return;
  358. stride *= 3; // triple because TextureMesh is never indexed (no elements buffer)
  359. if (!webgl.namedTextures[ColorSmoothingRgbaName]) {
  360. webgl.namedTextures[ColorSmoothingRgbaName] = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
  361. }
  362. const colorData = webgl.namedTextures[ColorSmoothingRgbaName];
  363. colorData.load(values.tOverpaint.ref.value);
  364. const smoothingData = calcTextureMeshColorSmoothing({
  365. vertexCount: values.uVertexCount.ref.value,
  366. instanceCount: values.uInstanceCount.ref.value,
  367. groupCount: values.uGroupCount.ref.value,
  368. transformBuffer: values.aTransform.ref.value,
  369. instanceBuffer: values.aInstance.ref.value,
  370. positionTexture: values.tPosition.ref.value,
  371. groupTexture: values.tGroup.ref.value,
  372. colorData,
  373. colorType: values.dOverpaintType.ref.value,
  374. boundingSphere: values.boundingSphere.ref.value,
  375. invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
  376. }, resolution, stride, webgl, colorTexture);
  377. ValueCell.updateIfChanged(values.dOverpaintType, smoothingData.type);
  378. ValueCell.update(values.tOverpaintGrid, smoothingData.texture);
  379. ValueCell.update(values.uOverpaintTexDim, smoothingData.gridTexDim);
  380. ValueCell.update(values.uOverpaintGridDim, smoothingData.gridDim);
  381. ValueCell.update(values.uOverpaintGridTransform, smoothingData.gridTransform);
  382. }
  383. function isSupportedTransparencyType(x: string): x is 'groupInstance' {
  384. return x === 'groupInstance';
  385. }
  386. export function applyTextureMeshTransparencySmoothing(values: TextureMeshValues, resolution: number, stride: number, webgl: WebGLContext, colorTexture?: Texture) {
  387. if (!isSupportedTransparencyType(values.dTransparencyType.ref.value)) return;
  388. stride *= 3; // triple because TextureMesh is never indexed (no elements buffer)
  389. if (!webgl.namedTextures[ColorSmoothingAlphaName]) {
  390. webgl.namedTextures[ColorSmoothingAlphaName] = webgl.resources.texture('image-uint8', 'alpha', 'ubyte', 'nearest');
  391. }
  392. const colorData = webgl.namedTextures[ColorSmoothingAlphaName];
  393. colorData.load(values.tTransparency.ref.value);
  394. const smoothingData = calcTextureMeshColorSmoothing({
  395. vertexCount: values.uVertexCount.ref.value,
  396. instanceCount: values.uInstanceCount.ref.value,
  397. groupCount: values.uGroupCount.ref.value,
  398. transformBuffer: values.aTransform.ref.value,
  399. instanceBuffer: values.aInstance.ref.value,
  400. positionTexture: values.tPosition.ref.value,
  401. groupTexture: values.tGroup.ref.value,
  402. colorData,
  403. colorType: values.dTransparencyType.ref.value,
  404. boundingSphere: values.boundingSphere.ref.value,
  405. invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
  406. }, resolution, stride, webgl, colorTexture);
  407. ValueCell.updateIfChanged(values.dTransparencyType, smoothingData.type);
  408. ValueCell.update(values.tTransparencyGrid, smoothingData.texture);
  409. ValueCell.update(values.uTransparencyTexDim, smoothingData.gridTexDim);
  410. ValueCell.update(values.uTransparencyGridDim, smoothingData.gridDim);
  411. ValueCell.update(values.uTransparencyGridTransform, smoothingData.gridTransform);
  412. }