color-data.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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 { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
  9. import { Color } from '../../mol-util/color';
  10. import { Vec2, Vec3, Vec4 } from '../../mol-math/linear-algebra';
  11. import { LocationIterator } from '../util/location-iterator';
  12. import { NullLocation } from '../../mol-model/location';
  13. import { LocationColor, ColorTheme } from '../../mol-theme/color';
  14. import { Geometry } from './geometry';
  15. import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
  16. export type ColorType = 'uniform' | 'instance' | 'group' | 'groupInstance' | 'vertex' | 'vertexInstance' | 'volume' | 'volumeInstance' | 'direct'
  17. export type ColorData = {
  18. uColor: ValueCell<Vec3>,
  19. tColor: ValueCell<TextureImage<Uint8Array>>,
  20. tColorGrid: ValueCell<Texture>,
  21. tPalette: ValueCell<TextureImage<Uint8Array>>,
  22. uColorTexDim: ValueCell<Vec2>,
  23. uColorGridDim: ValueCell<Vec3>,
  24. uColorGridTransform: ValueCell<Vec4>,
  25. dColorType: ValueCell<string>,
  26. dUsePalette: ValueCell<boolean>,
  27. }
  28. export function createColors(locationIt: LocationIterator, positionIt: LocationIterator, colorTheme: ColorTheme<any>, colorData?: ColorData): ColorData {
  29. const data = _createColors(locationIt, positionIt, colorTheme, colorData);
  30. if (colorTheme.palette) {
  31. ValueCell.updateIfChanged(data.dUsePalette, true);
  32. updatePaletteTexture(colorTheme.palette, data.tPalette);
  33. } else {
  34. ValueCell.updateIfChanged(data.dUsePalette, false);
  35. }
  36. return data;
  37. }
  38. function _createColors(locationIt: LocationIterator, positionIt: LocationIterator, colorTheme: ColorTheme<any>, colorData?: ColorData): ColorData {
  39. switch (Geometry.getGranularity(locationIt, colorTheme.granularity)) {
  40. case 'uniform': return createUniformColor(locationIt, colorTheme.color, colorData);
  41. case 'instance': return createInstanceColor(locationIt, colorTheme.color, colorData);
  42. case 'group': return createGroupColor(locationIt, colorTheme.color, colorData);
  43. case 'groupInstance': return createGroupInstanceColor(locationIt, colorTheme.color, colorData);
  44. case 'vertex': return createVertexColor(positionIt, colorTheme.color, colorData);
  45. case 'vertexInstance': return createVertexInstanceColor(positionIt, colorTheme.color, colorData);
  46. case 'volume': return createGridColor((colorTheme as any).grid, 'volume', colorData);
  47. case 'volumeInstance': return createGridColor((colorTheme as any).grid, 'volumeInstance', colorData);
  48. case 'direct': return createDirectColor(colorData);
  49. }
  50. }
  51. function updatePaletteTexture(palette: ColorTheme.Palette, cell: ValueCell<TextureImage<Uint8Array>>) {
  52. let isSynced = true;
  53. const texture = cell.ref.value;
  54. if (palette.colors.length !== texture.width || texture.filter !== palette.filter) {
  55. isSynced = false;
  56. } else {
  57. const data = texture.array;
  58. let o = 0;
  59. for (const c of palette.colors) {
  60. const [r, g, b] = Color.toRgb(c);
  61. if (data[o++] !== r || data[o++] !== g || data[o++] !== b) {
  62. isSynced = false;
  63. break;
  64. }
  65. }
  66. }
  67. if (isSynced) return;
  68. const array = new Uint8Array(palette.colors.length * 3);
  69. let o = 0;
  70. for (const c of palette.colors) {
  71. const [r, g, b] = Color.toRgb(c);
  72. array[o++] = r;
  73. array[o++] = g;
  74. array[o++] = b;
  75. }
  76. ValueCell.update(cell, { array, height: 1, width: palette.colors.length, filter: palette.filter });
  77. }
  78. //
  79. export function createValueColor(value: Color, colorData?: ColorData): ColorData {
  80. if (colorData) {
  81. ValueCell.update(colorData.uColor, Color.toVec3Normalized(colorData.uColor.ref.value, value));
  82. ValueCell.updateIfChanged(colorData.dColorType, 'uniform');
  83. return colorData;
  84. } else {
  85. return {
  86. uColor: ValueCell.create(Color.toVec3Normalized(Vec3(), value)),
  87. tColor: ValueCell.create({ array: new Uint8Array(3), width: 1, height: 1 }),
  88. tColorGrid: ValueCell.create(createNullTexture()),
  89. tPalette: ValueCell.create({ array: new Uint8Array(3), width: 1, height: 1 }),
  90. uColorTexDim: ValueCell.create(Vec2.create(1, 1)),
  91. uColorGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
  92. uColorGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
  93. dColorType: ValueCell.create('uniform'),
  94. dUsePalette: ValueCell.create(false),
  95. };
  96. }
  97. }
  98. /** Creates color uniform */
  99. function createUniformColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
  100. return createValueColor(color(NullLocation, false), colorData);
  101. }
  102. //
  103. export function createTextureColor(colors: TextureImage<Uint8Array>, type: ColorType, colorData?: ColorData): ColorData {
  104. if (colorData) {
  105. ValueCell.update(colorData.tColor, colors);
  106. ValueCell.update(colorData.uColorTexDim, Vec2.create(colors.width, colors.height));
  107. ValueCell.updateIfChanged(colorData.dColorType, type);
  108. return colorData;
  109. } else {
  110. return {
  111. uColor: ValueCell.create(Vec3()),
  112. tColor: ValueCell.create(colors),
  113. tColorGrid: ValueCell.create(createNullTexture()),
  114. tPalette: ValueCell.create({ array: new Uint8Array(3), width: 1, height: 1 }),
  115. uColorTexDim: ValueCell.create(Vec2.create(colors.width, colors.height)),
  116. uColorGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
  117. uColorGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
  118. dColorType: ValueCell.create(type),
  119. dUsePalette: ValueCell.create(false),
  120. };
  121. }
  122. }
  123. /** Creates color texture with color for each instance */
  124. function createInstanceColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
  125. const { instanceCount } = locationIt;
  126. const colors = createTextureImage(Math.max(1, instanceCount), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
  127. locationIt.reset();
  128. while (locationIt.hasNext) {
  129. const { location, isSecondary, instanceIndex } = locationIt.move();
  130. Color.toArray(color(location, isSecondary), colors.array, instanceIndex * 3);
  131. locationIt.skipInstance();
  132. }
  133. return createTextureColor(colors, 'instance', colorData);
  134. }
  135. /** Creates color texture with color for each group (i.e. shared across instances) */
  136. function createGroupColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
  137. const { groupCount } = locationIt;
  138. const colors = createTextureImage(Math.max(1, groupCount), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
  139. locationIt.reset();
  140. while (locationIt.hasNext && !locationIt.isNextNewInstance) {
  141. const { location, isSecondary, groupIndex } = locationIt.move();
  142. Color.toArray(color(location, isSecondary), colors.array, groupIndex * 3);
  143. }
  144. return createTextureColor(colors, 'group', colorData);
  145. }
  146. /** Creates color texture with color for each group in each instance */
  147. function createGroupInstanceColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
  148. const { groupCount, instanceCount } = locationIt;
  149. const count = instanceCount * groupCount;
  150. const colors = createTextureImage(Math.max(1, count), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
  151. locationIt.reset();
  152. while (locationIt.hasNext) {
  153. const { location, isSecondary, index } = locationIt.move();
  154. Color.toArray(color(location, isSecondary), colors.array, index * 3);
  155. }
  156. return createTextureColor(colors, 'groupInstance', colorData);
  157. }
  158. /** Creates color texture with color for each vertex (i.e. shared across instances) */
  159. function createVertexColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
  160. const { groupCount, stride } = locationIt;
  161. const colors = createTextureImage(Math.max(1, groupCount), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
  162. locationIt.reset();
  163. locationIt.voidInstances();
  164. while (locationIt.hasNext && !locationIt.isNextNewInstance) {
  165. const { location, isSecondary, groupIndex } = locationIt.move();
  166. const c = color(location, isSecondary);
  167. for (let i = 0; i < stride; ++i) {
  168. Color.toArray(c, colors.array, (groupIndex + i) * 3);
  169. }
  170. }
  171. return createTextureColor(colors, 'vertex', colorData);
  172. }
  173. /** Creates color texture with color for each vertex in each instance */
  174. function createVertexInstanceColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
  175. const { groupCount, instanceCount, stride } = locationIt;
  176. const count = instanceCount * groupCount;
  177. const colors = createTextureImage(Math.max(1, count), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
  178. locationIt.reset();
  179. while (locationIt.hasNext) {
  180. const { location, isSecondary, index } = locationIt.move();
  181. const c = color(location, isSecondary);
  182. for (let i = 0; i < stride; ++i) {
  183. Color.toArray(c, colors.array, (index + i) * 3);
  184. }
  185. }
  186. return createTextureColor(colors, 'vertexInstance', colorData);
  187. }
  188. //
  189. interface ColorVolume {
  190. colors: Texture
  191. dimension: Vec3
  192. transform: Vec4
  193. }
  194. export function createGridColor(grid: ColorVolume, type: ColorType, colorData?: ColorData): ColorData {
  195. const { colors, dimension, transform } = grid;
  196. const width = colors.getWidth();
  197. const height = colors.getHeight();
  198. if (colorData) {
  199. ValueCell.update(colorData.tColorGrid, colors);
  200. ValueCell.update(colorData.uColorTexDim, Vec2.create(width, height));
  201. ValueCell.update(colorData.uColorGridDim, Vec3.clone(dimension));
  202. ValueCell.update(colorData.uColorGridTransform, Vec4.clone(transform));
  203. ValueCell.updateIfChanged(colorData.dColorType, type);
  204. return colorData;
  205. } else {
  206. return {
  207. uColor: ValueCell.create(Vec3()),
  208. tColor: ValueCell.create({ array: new Uint8Array(3), width: 1, height: 1 }),
  209. tColorGrid: ValueCell.create(colors),
  210. tPalette: ValueCell.create({ array: new Uint8Array(3), width: 1, height: 1 }),
  211. uColorTexDim: ValueCell.create(Vec2.create(width, height)),
  212. uColorGridDim: ValueCell.create(Vec3.clone(dimension)),
  213. uColorGridTransform: ValueCell.create(Vec4.clone(transform)),
  214. dColorType: ValueCell.create(type),
  215. dUsePalette: ValueCell.create(false),
  216. };
  217. }
  218. }
  219. //
  220. /** Creates direct color */
  221. function createDirectColor(colorData?: ColorData): ColorData {
  222. if (colorData) {
  223. ValueCell.updateIfChanged(colorData.dColorType, 'direct');
  224. return colorData;
  225. } else {
  226. return {
  227. uColor: ValueCell.create(Vec3()),
  228. tColor: ValueCell.create({ array: new Uint8Array(3), width: 1, height: 1 }),
  229. tColorGrid: ValueCell.create(createNullTexture()),
  230. tPalette: ValueCell.create({ array: new Uint8Array(3), width: 1, height: 1 }),
  231. uColorTexDim: ValueCell.create(Vec2.create(1, 1)),
  232. uColorGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
  233. uColorGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
  234. dColorType: ValueCell.create('direct'),
  235. dUsePalette: ValueCell.create(false),
  236. };
  237. }
  238. }