gaussian-surface-mesh.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  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. */
  6. import { ParamDefinition as PD } from '../../../mol-util/param-definition';
  7. import { UnitsMeshParams, UnitsTextureMeshParams, UnitsVisual, UnitsMeshVisual, UnitsTextureMeshVisual } from '../units-visual';
  8. import { GaussianDensityParams, computeUnitGaussianDensity, computeUnitGaussianDensityTexture2d, GaussianDensityProps, computeStructureGaussianDensity, computeStructureGaussianDensityTexture2d } from './util/gaussian';
  9. import { VisualContext } from '../../visual';
  10. import { Unit, Structure } from '../../../mol-model/structure';
  11. import { Theme } from '../../../mol-theme/theme';
  12. import { Mesh } from '../../../mol-geo/geometry/mesh/mesh';
  13. import { computeMarchingCubesMesh } from '../../../mol-geo/util/marching-cubes/algorithm';
  14. import { ElementIterator, getElementLoci, eachElement, getSerialElementLoci, eachSerialElement } from './util/element';
  15. import { VisualUpdateState } from '../../util';
  16. import { TextureMesh } from '../../../mol-geo/geometry/texture-mesh/texture-mesh';
  17. import { calcActiveVoxels } from '../../../mol-gl/compute/marching-cubes/active-voxels';
  18. import { createHistogramPyramid } from '../../../mol-gl/compute/histogram-pyramid/reduction';
  19. import { createIsosurfaceBuffers } from '../../../mol-gl/compute/marching-cubes/isosurface';
  20. import { Sphere3D } from '../../../mol-math/geometry';
  21. import { ComplexVisual, ComplexMeshParams, ComplexMeshVisual, ComplexTextureMeshVisual, ComplexTextureMeshParams } from '../complex-visual';
  22. import { getUnitExtraRadius, getStructureExtraRadius } from './util/common';
  23. import { WebGLContext } from '../../../mol-gl/webgl/context';
  24. const SharedParams = {
  25. ...GaussianDensityParams,
  26. ignoreHydrogens: PD.Boolean(false),
  27. useGpu: PD.Boolean(false),
  28. };
  29. export const GaussianSurfaceMeshParams = {
  30. ...UnitsMeshParams,
  31. ...UnitsTextureMeshParams,
  32. ...SharedParams,
  33. };
  34. export type GaussianSurfaceMeshParams = typeof GaussianSurfaceMeshParams
  35. export const StructureGaussianSurfaceMeshParams = {
  36. ...ComplexMeshParams,
  37. ...ComplexTextureMeshParams,
  38. ...SharedParams,
  39. };
  40. export type StructureGaussianSurfaceMeshParams = typeof StructureGaussianSurfaceMeshParams
  41. function gpuSupport(webgl: WebGLContext) {
  42. return webgl.extensions.colorBufferFloat && webgl.extensions.textureFloat && webgl.extensions.blendMinMax && webgl.extensions.drawBuffers;
  43. }
  44. export function GaussianSurfaceVisual(materialId: number, props?: PD.Values<GaussianSurfaceMeshParams>, webgl?: WebGLContext) {
  45. return props?.useGpu && webgl && gpuSupport(webgl)
  46. ? GaussianSurfaceTextureMeshVisual(materialId)
  47. : GaussianSurfaceMeshVisual(materialId);
  48. }
  49. export function StructureGaussianSurfaceVisual(materialId: number, props?: PD.Values<StructureGaussianSurfaceMeshParams>, webgl?: WebGLContext) {
  50. return props?.useGpu && webgl && gpuSupport(webgl)
  51. ? StructureGaussianSurfaceTextureMeshVisual(materialId)
  52. : StructureGaussianSurfaceMeshVisual(materialId);
  53. }
  54. //
  55. async function createGaussianSurfaceMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityProps, mesh?: Mesh): Promise<Mesh> {
  56. const { smoothness } = props;
  57. const { transform, field, idField, radiusFactor } = await computeUnitGaussianDensity(structure, unit, props, ctx.webgl).runInContext(ctx.runtime);
  58. const params = {
  59. isoLevel: Math.exp(-smoothness) / radiusFactor,
  60. scalarField: field,
  61. idField
  62. };
  63. const surface = await computeMarchingCubesMesh(params, mesh).runAsChild(ctx.runtime);
  64. Mesh.transform(surface, transform);
  65. if (ctx.webgl && !ctx.webgl.isWebGL2) Mesh.uniformTriangleGroup(surface);
  66. const sphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, props.radiusOffset + getUnitExtraRadius(unit));
  67. surface.setBoundingSphere(sphere);
  68. return surface;
  69. }
  70. export function GaussianSurfaceMeshVisual(materialId: number): UnitsVisual<GaussianSurfaceMeshParams> {
  71. return UnitsMeshVisual<GaussianSurfaceMeshParams>({
  72. defaultProps: PD.getDefaultValues(GaussianSurfaceMeshParams),
  73. createGeometry: createGaussianSurfaceMesh,
  74. createLocationIterator: ElementIterator.fromGroup,
  75. getLoci: getElementLoci,
  76. eachLocation: eachElement,
  77. setUpdateState: (state: VisualUpdateState, newProps: PD.Values<GaussianSurfaceMeshParams>, currentProps: PD.Values<GaussianSurfaceMeshParams>) => {
  78. if (newProps.resolution !== currentProps.resolution) state.createGeometry = true;
  79. if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true;
  80. if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true;
  81. if (newProps.ignoreHydrogens !== currentProps.ignoreHydrogens) state.createGeometry = true;
  82. if (newProps.traceOnly !== currentProps.traceOnly) state.createGeometry = true;
  83. if (newProps.includeParent !== currentProps.includeParent) state.createGeometry = true;
  84. },
  85. mustRecreate: (props: PD.Values<GaussianSurfaceMeshParams>, webgl?: WebGLContext) => {
  86. return props.useGpu && !!webgl;
  87. }
  88. }, materialId);
  89. }
  90. //
  91. async function createStructureGaussianSurfaceMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: GaussianDensityProps, mesh?: Mesh): Promise<Mesh> {
  92. const { smoothness } = props;
  93. const { transform, field, idField, radiusFactor } = await computeStructureGaussianDensity(structure, props, ctx.webgl).runInContext(ctx.runtime);
  94. const params = {
  95. isoLevel: Math.exp(-smoothness) / radiusFactor,
  96. scalarField: field,
  97. idField
  98. };
  99. const surface = await computeMarchingCubesMesh(params, mesh).runAsChild(ctx.runtime);
  100. Mesh.transform(surface, transform);
  101. if (ctx.webgl && !ctx.webgl.isWebGL2) Mesh.uniformTriangleGroup(surface);
  102. const sphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, props.radiusOffset + getStructureExtraRadius(structure));
  103. surface.setBoundingSphere(sphere);
  104. return surface;
  105. }
  106. export function StructureGaussianSurfaceMeshVisual(materialId: number): ComplexVisual<StructureGaussianSurfaceMeshParams> {
  107. return ComplexMeshVisual<StructureGaussianSurfaceMeshParams>({
  108. defaultProps: PD.getDefaultValues(StructureGaussianSurfaceMeshParams),
  109. createGeometry: createStructureGaussianSurfaceMesh,
  110. createLocationIterator: ElementIterator.fromStructure,
  111. getLoci: getSerialElementLoci,
  112. eachLocation: eachSerialElement,
  113. setUpdateState: (state: VisualUpdateState, newProps: PD.Values<GaussianSurfaceMeshParams>, currentProps: PD.Values<GaussianSurfaceMeshParams>) => {
  114. if (newProps.resolution !== currentProps.resolution) state.createGeometry = true;
  115. if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true;
  116. if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true;
  117. if (newProps.ignoreHydrogens !== currentProps.ignoreHydrogens) state.createGeometry = true;
  118. if (newProps.traceOnly !== currentProps.traceOnly) state.createGeometry = true;
  119. },
  120. mustRecreate: (props: PD.Values<StructureGaussianSurfaceMeshParams>, webgl?: WebGLContext) => {
  121. return props.useGpu && !!webgl;
  122. }
  123. }, materialId);
  124. }
  125. //
  126. const GaussianSurfaceName = 'gaussian-surface';
  127. async function createGaussianSurfaceTextureMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityProps, textureMesh?: TextureMesh): Promise<TextureMesh> {
  128. if (!ctx.webgl) throw new Error('webgl context required to create gaussian surface texture-mesh');
  129. const { namedTextures, resources, extensions: { colorBufferFloat, textureFloat, colorBufferHalfFloat, textureHalfFloat } } = ctx.webgl;
  130. if (!namedTextures[GaussianSurfaceName]) {
  131. namedTextures[GaussianSurfaceName] = colorBufferHalfFloat && textureHalfFloat
  132. ? resources.texture('image-float16', 'rgba', 'fp16', 'linear')
  133. : colorBufferFloat && textureFloat
  134. ? resources.texture('image-float32', 'rgba', 'float', 'linear')
  135. : resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
  136. }
  137. // console.time('computeUnitGaussianDensityTexture2d');
  138. const densityTextureData = await computeUnitGaussianDensityTexture2d(structure, unit, true, props, ctx.webgl, namedTextures[GaussianSurfaceName]).runInContext(ctx.runtime);
  139. // console.log(densityTextureData);
  140. // console.log('vertexGroupTexture', readTexture(ctx.webgl, densityTextureData.texture));
  141. // ctx.webgl.waitForGpuCommandsCompleteSync();
  142. // console.timeEnd('computeUnitGaussianDensityTexture2d');
  143. const isoLevel = Math.exp(-props.smoothness) / densityTextureData.radiusFactor;
  144. // console.time('calcActiveVoxels');
  145. const activeVoxelsTex = calcActiveVoxels(ctx.webgl, densityTextureData.texture, densityTextureData.gridDim, densityTextureData.gridTexDim, isoLevel, densityTextureData.gridTexScale);
  146. // ctx.webgl.waitForGpuCommandsCompleteSync();
  147. // console.timeEnd('calcActiveVoxels');
  148. // console.time('createHistogramPyramid');
  149. const compacted = createHistogramPyramid(ctx.webgl, activeVoxelsTex, densityTextureData.gridTexScale, densityTextureData.gridTexDim);
  150. // ctx.webgl.waitForGpuCommandsCompleteSync();
  151. // console.timeEnd('createHistogramPyramid');
  152. // console.time('createIsosurfaceBuffers');
  153. const gv = createIsosurfaceBuffers(ctx.webgl, activeVoxelsTex, densityTextureData.texture, compacted, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.transform, isoLevel, textureMesh ? textureMesh.vertexGroupTexture.ref.value : undefined, textureMesh ? textureMesh.normalTexture.ref.value : undefined);
  154. // ctx.webgl.waitForGpuCommandsCompleteSync();
  155. // console.timeEnd('createIsosurfaceBuffers');
  156. const boundingSphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, props.radiusOffset + getStructureExtraRadius(structure));
  157. const surface = TextureMesh.create(gv.vertexCount, 1, gv.vertexGroupTexture, gv.normalTexture, boundingSphere, textureMesh);
  158. // console.log({
  159. // renderables: ctx.webgl.namedComputeRenderables,
  160. // framebuffers: ctx.webgl.namedFramebuffers,
  161. // textures: ctx.webgl.namedTextures,
  162. // });
  163. // ctx.webgl.waitForGpuCommandsCompleteSync();
  164. return surface;
  165. }
  166. export function GaussianSurfaceTextureMeshVisual(materialId: number): UnitsVisual<GaussianSurfaceMeshParams> {
  167. return UnitsTextureMeshVisual<GaussianSurfaceMeshParams>({
  168. defaultProps: PD.getDefaultValues(GaussianSurfaceMeshParams),
  169. createGeometry: createGaussianSurfaceTextureMesh,
  170. createLocationIterator: ElementIterator.fromGroup,
  171. getLoci: getElementLoci,
  172. eachLocation: eachElement,
  173. setUpdateState: (state: VisualUpdateState, newProps: PD.Values<GaussianSurfaceMeshParams>, currentProps: PD.Values<GaussianSurfaceMeshParams>) => {
  174. if (newProps.resolution !== currentProps.resolution) state.createGeometry = true;
  175. if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true;
  176. if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true;
  177. if (newProps.ignoreHydrogens !== currentProps.ignoreHydrogens) state.createGeometry = true;
  178. if (newProps.traceOnly !== currentProps.traceOnly) state.createGeometry = true;
  179. if (newProps.includeParent !== currentProps.includeParent) state.createGeometry = true;
  180. },
  181. mustRecreate: (props: PD.Values<GaussianSurfaceMeshParams>, webgl?: WebGLContext) => {
  182. return !props.useGpu || !webgl;
  183. },
  184. dispose: (geometry: TextureMesh) => {
  185. geometry.normalTexture.ref.value.destroy();
  186. geometry.vertexGroupTexture.ref.value.destroy();
  187. }
  188. }, materialId);
  189. }
  190. //
  191. async function createStructureGaussianSurfaceTextureMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: GaussianDensityProps, textureMesh?: TextureMesh): Promise<TextureMesh> {
  192. if (!ctx.webgl) throw new Error('webgl context required to create structure gaussian surface texture-mesh');
  193. const { namedTextures, resources, extensions: { colorBufferFloat, textureFloat, colorBufferHalfFloat, textureHalfFloat } } = ctx.webgl;
  194. if (!namedTextures[GaussianSurfaceName]) {
  195. namedTextures[GaussianSurfaceName] = colorBufferHalfFloat && textureHalfFloat
  196. ? resources.texture('image-float16', 'rgba', 'fp16', 'linear')
  197. : colorBufferFloat && textureFloat
  198. ? resources.texture('image-float32', 'rgba', 'float', 'linear')
  199. : resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
  200. }
  201. // console.time('computeUnitGaussianDensityTexture2d');
  202. const densityTextureData = await computeStructureGaussianDensityTexture2d(structure, true, props, ctx.webgl, namedTextures[GaussianSurfaceName]).runInContext(ctx.runtime);
  203. // console.log(densityTextureData);
  204. // console.log('vertexGroupTexture', readTexture(ctx.webgl, densityTextureData.texture));
  205. // ctx.webgl.waitForGpuCommandsCompleteSync();
  206. // console.timeEnd('computeUnitGaussianDensityTexture2d');
  207. const isoLevel = Math.exp(-props.smoothness) / densityTextureData.radiusFactor;
  208. // console.time('calcActiveVoxels');
  209. const activeVoxelsTex = calcActiveVoxels(ctx.webgl, densityTextureData.texture, densityTextureData.gridDim, densityTextureData.gridTexDim, isoLevel, densityTextureData.gridTexScale);
  210. // ctx.webgl.waitForGpuCommandsCompleteSync();
  211. // console.timeEnd('calcActiveVoxels');
  212. // console.time('createHistogramPyramid');
  213. const compacted = createHistogramPyramid(ctx.webgl, activeVoxelsTex, densityTextureData.gridTexScale, densityTextureData.gridTexDim);
  214. // ctx.webgl.waitForGpuCommandsCompleteSync();
  215. // console.timeEnd('createHistogramPyramid');
  216. // console.time('createIsosurfaceBuffers');
  217. const gv = createIsosurfaceBuffers(ctx.webgl, activeVoxelsTex, densityTextureData.texture, compacted, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.transform, isoLevel, textureMesh ? textureMesh.vertexGroupTexture.ref.value : undefined, textureMesh ? textureMesh.normalTexture.ref.value : undefined);
  218. // ctx.webgl.waitForGpuCommandsCompleteSync();
  219. // console.timeEnd('createIsosurfaceBuffers');
  220. const boundingSphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, props.radiusOffset + getStructureExtraRadius(structure));
  221. const surface = TextureMesh.create(gv.vertexCount, 1, gv.vertexGroupTexture, gv.normalTexture, boundingSphere, textureMesh);
  222. // console.log({
  223. // renderables: ctx.webgl.namedComputeRenderables,
  224. // framebuffers: ctx.webgl.namedFramebuffers,
  225. // textures: ctx.webgl.namedTextures,
  226. // });
  227. // ctx.webgl.waitForGpuCommandsCompleteSync();
  228. return surface;
  229. }
  230. export function StructureGaussianSurfaceTextureMeshVisual(materialId: number): ComplexVisual<StructureGaussianSurfaceMeshParams> {
  231. return ComplexTextureMeshVisual<StructureGaussianSurfaceMeshParams>({
  232. defaultProps: PD.getDefaultValues(StructureGaussianSurfaceMeshParams),
  233. createGeometry: createStructureGaussianSurfaceTextureMesh,
  234. createLocationIterator: ElementIterator.fromStructure,
  235. getLoci: getSerialElementLoci,
  236. eachLocation: eachSerialElement,
  237. setUpdateState: (state: VisualUpdateState, newProps: PD.Values<StructureGaussianSurfaceMeshParams>, currentProps: PD.Values<StructureGaussianSurfaceMeshParams>) => {
  238. if (newProps.resolution !== currentProps.resolution) state.createGeometry = true;
  239. if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true;
  240. if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true;
  241. if (newProps.ignoreHydrogens !== currentProps.ignoreHydrogens) state.createGeometry = true;
  242. if (newProps.traceOnly !== currentProps.traceOnly) state.createGeometry = true;
  243. },
  244. mustRecreate: (props: PD.Values<StructureGaussianSurfaceMeshParams>, webgl?: WebGLContext) => {
  245. return !props.useGpu || !webgl;
  246. },
  247. dispose: (geometry: TextureMesh) => {
  248. geometry.normalTexture.ref.value.destroy();
  249. geometry.vertexGroupTexture.ref.value.destroy();
  250. }
  251. }, materialId);
  252. }