diff --git a/src/mol-geo/geometry/direct-volume/direct-volume.ts b/src/mol-geo/geometry/direct-volume/direct-volume.ts index de1aa2f03b5f72340389534ed9a92c39eb2762e3..cdfdccc303b35c54a3c064985ef842b5703264b1 100644 --- a/src/mol-geo/geometry/direct-volume/direct-volume.ts +++ b/src/mol-geo/geometry/direct-volume/direct-volume.ts @@ -6,13 +6,13 @@ import { RuntimeContext } from 'mol-task' import { ValueCell } from 'mol-util' -import { Sphere3D } from 'mol-math/geometry' +import { Sphere3D, Box3D } from 'mol-math/geometry' import { paramDefaultValues, RangeParam, BooleanParam, SelectParam, TextParam } from 'mol-view/parameter'; import { DirectVolume2dValues, DirectVolumeBaseValues, DirectVolume3dValues } from 'mol-gl/renderable/direct-volume'; -import { TextureImage, TextureVolume } from 'mol-gl/renderable/util'; import { Vec3, Vec2, Mat4 } from 'mol-math/linear-algebra'; import { Box } from '../../primitive/box'; import { getControlPointsFromString, createTransferFunctionTexture } from './transfer-function'; +import { Texture } from 'mol-gl/webgl/texture'; const VolumeBox = Box() const RenderModeOptions = [['isosurface', 'Isosurface'], ['volume', 'Volume']] as [string, string][] @@ -87,11 +87,34 @@ function updateBaseValues(values: DirectVolumeBaseValues, props: BaseProps) { export interface DirectVolume2d extends DirectVolumeBase { readonly kind: 'direct-volume-2d', - readonly gridTexture: ValueCell<TextureImage<any>>, + readonly gridTexture: ValueCell<Texture>, readonly gridTextureDim: ValueCell<Vec2>, } export namespace DirectVolume2d { + export function create(bbox: Box3D, gridDimension: Vec3, transform: Mat4, texture: Texture, directVolume?: DirectVolume2d): DirectVolume2d { + if (directVolume) { + ValueCell.update(directVolume.gridDimension, gridDimension) + ValueCell.update(directVolume.gridTextureDim, Vec2.set(directVolume.gridTextureDim.ref.value, texture.width, texture.height)) + ValueCell.update(directVolume.bboxMin, bbox.min) + ValueCell.update(directVolume.bboxMax, bbox.max) + ValueCell.update(directVolume.bboxSize, Vec3.sub(directVolume.bboxSize.ref.value, bbox.max, bbox.min)) + ValueCell.update(directVolume.transform, transform) + return directVolume + } else { + return { + kind: 'direct-volume-2d' as 'direct-volume-2d', + gridDimension: ValueCell.create(gridDimension), + gridTexture: ValueCell.create(texture), + gridTextureDim: ValueCell.create(Vec2.create(texture.width, texture.height)), + bboxMin: ValueCell.create(bbox.min), + bboxMax: ValueCell.create(bbox.max), + bboxSize: ValueCell.create(Vec3.sub(Vec3.zero(), bbox.max, bbox.min)), + transform: ValueCell.create(transform), + } + } + } + export function createEmpty(directVolume?: DirectVolume2d): DirectVolume2d { return {} as DirectVolume2d // TODO } @@ -120,10 +143,31 @@ export namespace DirectVolume2d { export interface DirectVolume3d extends DirectVolumeBase { readonly kind: 'direct-volume-3d', - readonly gridTexture: ValueCell<TextureVolume<any>>, + readonly gridTexture: ValueCell<Texture>, } export namespace DirectVolume3d { + export function create(bbox: Box3D, gridDimension: Vec3, transform: Mat4, texture: Texture, directVolume?: DirectVolume3d): DirectVolume3d { + if (directVolume) { + ValueCell.update(directVolume.gridDimension, gridDimension) + ValueCell.update(directVolume.bboxMin, bbox.min) + ValueCell.update(directVolume.bboxMax, bbox.max) + ValueCell.update(directVolume.bboxSize, Vec3.sub(directVolume.bboxSize.ref.value, bbox.max, bbox.min)) + ValueCell.update(directVolume.transform, transform) + return directVolume + } else { + return { + kind: 'direct-volume-3d' as 'direct-volume-3d', + gridDimension: ValueCell.create(gridDimension), + gridTexture: ValueCell.create(texture), + bboxMin: ValueCell.create(bbox.min), + bboxMax: ValueCell.create(bbox.max), + bboxSize: ValueCell.create(Vec3.sub(Vec3.zero(), bbox.max, bbox.min)), + transform: ValueCell.create(transform), + } + } + } + export function createEmpty(directVolume?: DirectVolume3d): DirectVolume3d { return {} as DirectVolume3d // TODO } diff --git a/src/mol-geo/geometry/geometry.ts b/src/mol-geo/geometry/geometry.ts index 4ad88a210ae595fe25cffbc0185c100307eba054..b8920b17a75cd1b065fe7d909d9cf592c6a955d9 100644 --- a/src/mol-geo/geometry/geometry.ts +++ b/src/mol-geo/geometry/geometry.ts @@ -18,7 +18,6 @@ import { Lines } from './lines/lines'; import { paramDefaultValues, RangeParam, BooleanParam, SelectParam, ColorParam, StructureParam, ValueParam } from 'mol-view/parameter' import { Structure } from 'mol-model/structure'; import { DirectVolume2d, DirectVolume3d } from './direct-volume/direct-volume'; -import { GLRenderingContext } from 'mol-gl/webgl/compat'; import { Context } from 'mol-gl/webgl/context'; // diff --git a/src/mol-geo/representation/structure/index.ts b/src/mol-geo/representation/structure/index.ts index 5c1cae6f6840b9ee845365deeb5f5ade64586b51..f1b8d592bac06a5b8e403e4b82be4e2410c53467 100644 --- a/src/mol-geo/representation/structure/index.ts +++ b/src/mol-geo/representation/structure/index.ts @@ -14,7 +14,7 @@ import { Mesh } from '../../geometry/mesh/mesh'; import { Points } from '../../geometry/points/points'; import { Lines } from '../../geometry/lines/lines'; import { SelectParam, paramDefaultValues } from 'mol-view/parameter'; -import { DirectVolume2d } from '../../geometry/direct-volume/direct-volume'; +import { DirectVolume2d, DirectVolume3d } from '../../geometry/direct-volume/direct-volume'; export interface StructureRepresentation<P extends RepresentationProps = {}> extends Representation<Structure, P> { } @@ -49,6 +49,7 @@ export type StructureLinesProps = typeof DefaultStructureLinesProps export const StructureDirectVolumeParams = { ...DirectVolume2d.Params, + ...DirectVolume3d.Params, ...StructureParams, } export const DefaultStructureDirectVolumeProps = paramDefaultValues(StructureDirectVolumeParams) diff --git a/src/mol-geo/representation/structure/units-visual.ts b/src/mol-geo/representation/structure/units-visual.ts index d645473bf2e40edd3f9bda9c4d1a69a5fcbab21d..785b9f9794e8d6582301256a0dd71dfb7c90dcca 100644 --- a/src/mol-geo/representation/structure/units-visual.ts +++ b/src/mol-geo/representation/structure/units-visual.ts @@ -15,7 +15,7 @@ import { LocationIterator } from '../../util/location-iterator'; import { Mesh } from '../../geometry/mesh/mesh'; import { MarkerAction, applyMarkerAction, createMarkers } from '../../geometry/marker-data'; import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci'; -import { MeshRenderObject, PointsRenderObject, LinesRenderObject, DirectVolume2dRenderObject } from 'mol-gl/render-object'; +import { MeshRenderObject, PointsRenderObject, LinesRenderObject, DirectVolume2dRenderObject, DirectVolume3dRenderObject } from 'mol-gl/render-object'; import { createUnitsMeshRenderObject, createUnitsPointsRenderObject, createUnitsTransform, createUnitsLinesRenderObject, createUnitsDirectVolumeRenderObject } from './visual/util/common'; import { deepEqual, ValueCell, UUID } from 'mol-util'; import { Interval } from 'mol-data/int'; @@ -25,7 +25,7 @@ import { createColors, ColorProps } from '../../geometry/color-data'; import { createSizes, SizeProps } from '../../geometry/size-data'; import { Lines } from '../../geometry/lines/lines'; import { MultiSelectParam, paramDefaultValues } from 'mol-view/parameter'; -import { DirectVolume2d } from '../../geometry/direct-volume/direct-volume'; +import { DirectVolume2d, DirectVolume3d } from '../../geometry/direct-volume/direct-volume'; export const UnitKindInfo = { 'atomic': {}, @@ -526,15 +526,15 @@ export const UnitsDirectVolumeParams = { } export const DefaultUnitsDirectVolumeProps = paramDefaultValues(UnitsDirectVolumeParams) export type UnitsDirectVolumeProps = typeof DefaultUnitsDirectVolumeProps -export interface UnitsDirectVolumeVisualBuilder<P extends UnitsDirectVolumeProps> extends UnitsVisualBuilder<P, DirectVolume2d> { } +export interface UnitsDirectVolumeVisualBuilder<P extends UnitsDirectVolumeProps> extends UnitsVisualBuilder<P, DirectVolume2d | DirectVolume3d> { } export function UnitsDirectVolumeVisual<P extends UnitsDirectVolumeProps>(builder: UnitsDirectVolumeVisualBuilder<P>): UnitsVisual<P> { const { defaultProps, createGeometry, createLocationIterator, getLoci, setUpdateState } = builder const updateState = VisualUpdateState.create() - let renderObject: DirectVolume2dRenderObject | undefined + let renderObject: DirectVolume2dRenderObject | DirectVolume3dRenderObject | undefined let currentProps: P - let directVolume: DirectVolume2d + let directVolume: DirectVolume2d | DirectVolume3d let currentGroup: Unit.SymmetryGroup let currentStructure: Structure let locationIt: LocationIterator @@ -551,7 +551,9 @@ export function UnitsDirectVolumeVisual<P extends UnitsDirectVolumeProps>(builde currentConformationId = Unit.conformationId(unit) directVolume = includesUnitKind(currentProps.unitKinds, unit) ? await createGeometry(ctx, unit, currentStructure, currentProps, directVolume) - : DirectVolume2d.createEmpty(directVolume) + : (webgl.isWebGL2 ? + DirectVolume2d.createEmpty(directVolume as DirectVolume2d) : + DirectVolume3d.createEmpty(directVolume as DirectVolume3d)) console.log('directVolume', directVolume) @@ -598,7 +600,9 @@ export function UnitsDirectVolumeVisual<P extends UnitsDirectVolumeProps>(builde if (updateState.createGeometry) { directVolume = includesUnitKind(newProps.unitKinds, unit) ? await createGeometry(ctx, unit, currentStructure, newProps, directVolume) - : DirectVolume2d.createEmpty(directVolume) + : (webgl.isWebGL2 ? + DirectVolume2d.createEmpty(directVolume as DirectVolume2d) : + DirectVolume3d.createEmpty(directVolume as DirectVolume3d)) updateState.updateColor = true } @@ -606,9 +610,12 @@ export function UnitsDirectVolumeVisual<P extends UnitsDirectVolumeProps>(builde // await createColors(ctx, locationIt, newProps, renderObject.values) // } - // TODO why do I need to cast here? - DirectVolume2d.updateValues(renderObject.values, newProps as UnitsDirectVolumeProps) - updateRenderableState(renderObject.state, newProps as UnitsDirectVolumeProps) + if (renderObject.type === 'direct-volume-2d') { + DirectVolume2d.updateValues(renderObject.values, newProps) + } else { + DirectVolume3d.updateValues(renderObject.values, newProps) + } + updateRenderableState(renderObject.state, newProps) currentProps = newProps } diff --git a/src/mol-geo/representation/structure/visual/gaussian-density-volume.ts b/src/mol-geo/representation/structure/visual/gaussian-density-volume.ts index 83a90531ac83012082919676ef3c96cbf39ec75a..c129566b131f07479175a7b61cea98e2521c2ba0 100644 --- a/src/mol-geo/representation/structure/visual/gaussian-density-volume.ts +++ b/src/mol-geo/representation/structure/visual/gaussian-density-volume.ts @@ -9,42 +9,43 @@ import { UnitsVisual, VisualUpdateState } from '..'; import { RuntimeContext } from 'mol-task' import { UnitsDirectVolumeVisual, UnitsDirectVolumeParams } from '../units-visual'; import { StructureElementIterator, getElementLoci, markElement } from './util/element'; -import { GaussianDensityProps, GaussianDensityParams } from 'mol-model/structure/structure/unit/gaussian-density'; +import { GaussianDensityProps, GaussianDensityParams, computeUnitGaussianDensityTexture } from 'mol-model/structure/structure/unit/gaussian-density'; import { paramDefaultValues } from 'mol-view/parameter'; -import { DirectVolume2d } from '../../../geometry/direct-volume/direct-volume'; -import { ValueCell } from 'mol-util'; -import { Vec3, Vec2 } from 'mol-math/linear-algebra'; +import { DirectVolume2d, DirectVolume3d } from '../../../geometry/direct-volume/direct-volume'; -async function createGaussianDensityVolume(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianDensityProps, directVolume?: DirectVolume2d): Promise<DirectVolume2d> { - const p = { ...props, useGpu: true, ignoreCache: true } - const { transform, texture, bbox, gridDimension } = await unit.computeGaussianDensity(p, ctx) - if (!texture || !bbox || !gridDimension) throw new Error('missing renderTarget and/or boundingBox and/or gridDimension') +async function createGaussianDensityVolume(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianDensityProps, directVolume?: DirectVolume2d | DirectVolume3d): Promise<DirectVolume2d | DirectVolume3d> { + const { webgl } = props + if (webgl === undefined) throw new Error('createGaussianDensityVolume requires `webgl` in props') - if (directVolume) { - ValueCell.update(directVolume.gridDimension, gridDimension) - ValueCell.update(directVolume.gridTexture, renderTarget.image) - ValueCell.update(directVolume.gridTextureDim, Vec2.set(directVolume.gridTextureDim.ref.value, renderTarget.width, renderTarget.height)) - ValueCell.update(directVolume.bboxMin, bbox.min) - ValueCell.update(directVolume.bboxMax, bbox.max) - ValueCell.update(directVolume.bboxSize, Vec3.sub(directVolume.bboxSize.ref.value, bbox.max, bbox.min)) - ValueCell.update(directVolume.transform, transform) - } else { - directVolume = { - kind: 'direct-volume-2d' as 'direct-volume-2d', - gridDimension: ValueCell.create(gridDimension), - gridTexture: ValueCell.create(renderTarget.image), - gridTextureDim: ValueCell.create(Vec2.create(renderTarget.width, renderTarget.height)), - bboxMin: ValueCell.create(bbox.min), - bboxMax: ValueCell.create(bbox.max), - bboxSize: ValueCell.create(Vec3.sub(Vec3.zero(), bbox.max, bbox.min)), - transform: ValueCell.create(transform), - } - } + const p = { ...props, useGpu: true } + const oldTexture = directVolume ? directVolume.gridTexture.ref.value : undefined + const densityTextureData = await computeUnitGaussianDensityTexture(unit, p, oldTexture).runInContext(ctx) + const { transform, texture, bbox, gridDimension } = densityTextureData + + directVolume = texture.depth == 0 ? + DirectVolume2d.create(bbox, gridDimension, transform, texture, directVolume as DirectVolume2d) : + DirectVolume3d.create(bbox, gridDimension, transform, texture, directVolume as DirectVolume3d) + - console.log('gridDimension', gridDimension) - console.log('gridTextureDim', renderTarget.width, renderTarget.height) - console.log('boundingBox', bbox) - console.log('transform', transform) + // if (directVolume) { + // ValueCell.update(directVolume.gridDimension, gridDimension) + // ValueCell.update(directVolume.gridTextureDim, Vec2.set(directVolume.gridTextureDim.ref.value, texture.width, texture.height)) + // ValueCell.update(directVolume.bboxMin, bbox.min) + // ValueCell.update(directVolume.bboxMax, bbox.max) + // ValueCell.update(directVolume.bboxSize, Vec3.sub(directVolume.bboxSize.ref.value, bbox.max, bbox.min)) + // ValueCell.update(directVolume.transform, transform) + // } else { + // directVolume = { + // kind: 'direct-volume-2d' as 'direct-volume-2d', + // gridDimension: ValueCell.create(gridDimension), + // gridTexture: ValueCell.create(texture), + // gridTextureDim: ValueCell.create(Vec2.create(texture.width, texture.height)), + // bboxMin: ValueCell.create(bbox.min), + // bboxMax: ValueCell.create(bbox.max), + // bboxSize: ValueCell.create(Vec3.sub(Vec3.zero(), bbox.max, bbox.min)), + // transform: ValueCell.create(transform), + // } + // } return directVolume; } diff --git a/src/mol-geo/representation/structure/visual/util/common.ts b/src/mol-geo/representation/structure/visual/util/common.ts index 7c95a0300faaa2f271f576bc8cc37bda52178c4a..ef59fe0f370c855cd150a25d3b9c35592f00c4ba 100644 --- a/src/mol-geo/representation/structure/visual/util/common.ts +++ b/src/mol-geo/representation/structure/visual/util/common.ts @@ -8,14 +8,14 @@ import { Unit, Structure } from 'mol-model/structure'; import { LocationIterator } from '../../../../util/location-iterator'; import { Mesh } from '../../../../geometry/mesh/mesh'; import { StructureProps } from '../..'; -import { createMeshRenderObject, createPointsRenderObject, createLinesRenderObject, createDirectVolume2dRenderObject } from 'mol-gl/render-object'; +import { createMeshRenderObject, createPointsRenderObject, createLinesRenderObject, createDirectVolume2dRenderObject, createDirectVolume3dRenderObject } from 'mol-gl/render-object'; import { RuntimeContext } from 'mol-task'; import { TransformData, createIdentityTransform, createTransform } from '../../../../geometry/transform-data'; import { Points } from '../../../../geometry/points/points'; import { createRenderableState } from '../../../../geometry/geometry'; import { Mat4 } from 'mol-math/linear-algebra'; import { Lines } from '../../../../geometry/lines/lines'; -import { DirectVolume2d } from '../../../../geometry/direct-volume/direct-volume'; +import { DirectVolume2d, DirectVolume3d } from '../../../../geometry/direct-volume/direct-volume'; export function createUnitsTransform({ units }: Unit.SymmetryGroup, transformData?: TransformData) { const unitCount = units.length @@ -70,12 +70,13 @@ export async function createUnitsLinesRenderObject(ctx: RuntimeContext, group: U // direct-volume -type StructureDirectVolumeProps = DirectVolume2d.Props & StructureProps +type StructureDirectVolumeProps = DirectVolume2d.Props & DirectVolume3d.Props & StructureProps -export async function createUnitsDirectVolumeRenderObject(ctx: RuntimeContext, group: Unit.SymmetryGroup, directVolume: DirectVolume2d, locationIt: LocationIterator, props: StructureDirectVolumeProps) { +export async function createUnitsDirectVolumeRenderObject(ctx: RuntimeContext, group: Unit.SymmetryGroup, directVolume: DirectVolume2d | DirectVolume3d, locationIt: LocationIterator, props: StructureDirectVolumeProps) { // TODO transform support // const transform = createUnitsTransform(group) - const values = await DirectVolume2d.createValues(ctx, directVolume, props) const state = createRenderableState(props) - return createDirectVolume2dRenderObject(values, state) + return directVolume.kind === 'direct-volume-2d' ? + createDirectVolume2dRenderObject(await DirectVolume2d.createValues(ctx, directVolume, props), state) : + createDirectVolume3dRenderObject(await DirectVolume3d.createValues(ctx, directVolume, props), state) } \ No newline at end of file diff --git a/src/mol-geo/representation/volume/direct-volume.ts b/src/mol-geo/representation/volume/direct-volume.ts index 8c1aea142c9feabd0585df879609b526cf5c1dc7..8e5eaecdf20e2df0264bdd0430037c73a5d835b9 100644 --- a/src/mol-geo/representation/volume/direct-volume.ts +++ b/src/mol-geo/representation/volume/direct-volume.ts @@ -13,13 +13,19 @@ import { MarkerAction } from '../../geometry/marker-data'; import { Loci, EmptyLoci } from 'mol-model/loci'; import { createRenderableState, updateRenderableState, Geometry } from '../../geometry/geometry'; import { paramDefaultValues } from 'mol-view/parameter'; -import { ValueCell } from 'mol-util'; import { DirectVolume2d, DirectVolume3d } from '../../geometry/direct-volume/direct-volume'; -import { Vec2, Vec3 } from 'mol-math/linear-algebra'; +import { Vec3, Mat4 } from 'mol-math/linear-algebra'; import { Box3D } from 'mol-math/geometry'; -import { createImageData, Context } from 'mol-gl/webgl/context'; -import { debugTexture } from 'mol-gl/util'; +import { Context } from 'mol-gl/webgl/context'; import { DirectVolume3dValues, DirectVolume2dValues } from 'mol-gl/renderable/direct-volume'; +import { createTexture } from 'mol-gl/webgl/texture'; + +function getBoundingBox(gridDimension: Vec3, transform: Mat4) { + const bbox = Box3D.empty() + Box3D.add(bbox, gridDimension) + Box3D.transform(bbox, bbox, transform) + return bbox +} // 2d volume texture @@ -80,46 +86,17 @@ function createVolumeTexture2d(volume: VolumeData, maxTextureSize: number) { export function createDirectVolume2d(ctx: RuntimeContext, webgl: Context, volume: VolumeData, directVolume?: DirectVolume2d) { const gridDimension = volume.data.space.dimensions as Vec3 const textureImage = createVolumeTexture2d(volume, webgl.maxTextureSize) + // debugTexture(createImageData(textureImage.array, textureImage.width, textureImage.height), 1/3) const transform = VolumeData.getGridToCartesianTransform(volume) - - console.log('textureImage', textureImage) - debugTexture(createImageData(textureImage.array, textureImage.width, textureImage.height), 1/3) - - const bbox = Box3D.empty() - Box3D.add(bbox, gridDimension) - Box3D.transform(bbox, bbox, transform) - + const bbox = getBoundingBox(gridDimension, transform) const dim = Vec3.create(gridDimension[0], gridDimension[1], gridDimension[2]) dim[0] += 1 // horizontal padding dim[0] += 1 // vertical padding - if (directVolume) { - ValueCell.update(directVolume.gridDimension, dim) - ValueCell.update(directVolume.gridTexture, textureImage) - ValueCell.update(directVolume.gridTextureDim, Vec2.set(directVolume.gridTextureDim.ref.value, textureImage.width, textureImage.height)) - ValueCell.update(directVolume.bboxMin, bbox.min) - ValueCell.update(directVolume.bboxMax, bbox.max) - ValueCell.update(directVolume.bboxSize, Vec3.sub(directVolume.bboxSize.ref.value, bbox.max, bbox.min)) - ValueCell.update(directVolume.transform, transform) - } else { - directVolume = { - kind: 'direct-volume-2d' as 'direct-volume-2d', - gridDimension: ValueCell.create(dim), - gridTexture: ValueCell.create(textureImage), - gridTextureDim: ValueCell.create(Vec2.create(textureImage.width, textureImage.height)), - bboxMin: ValueCell.create(bbox.min), - bboxMax: ValueCell.create(bbox.max), - bboxSize: ValueCell.create(Vec3.sub(Vec3.zero(), bbox.max, bbox.min)), - transform: ValueCell.create(transform), - } - } - - console.log('gridDimension', dim) - console.log('gridTextureDim', textureImage.width, textureImage.height) - console.log('boundingBox', bbox) - console.log('transform', transform) + const texture = directVolume ? directVolume.gridTexture.ref.value : createTexture(webgl, 'image-uint8', 'rgba', 'ubyte', 'linear') + texture.load(textureImage) - return directVolume; + return DirectVolume2d.create(bbox, dim, transform, texture, directVolume) } // 3d volume texture @@ -150,33 +127,12 @@ export function createDirectVolume3d(ctx: RuntimeContext, webgl: Context, volume const gridDimension = volume.data.space.dimensions as Vec3 const textureVolume = createVolumeTexture3d(volume) const transform = VolumeData.getGridToCartesianTransform(volume) + const bbox = getBoundingBox(gridDimension, transform) - console.log('textureVolume', textureVolume) - - const bbox = Box3D.empty() - Box3D.add(bbox, gridDimension) - Box3D.transform(bbox, bbox, transform) - - if (directVolume) { - ValueCell.update(directVolume.gridDimension, gridDimension) - ValueCell.update(directVolume.gridTexture, textureVolume) - ValueCell.update(directVolume.bboxMin, bbox.min) - ValueCell.update(directVolume.bboxMax, bbox.max) - ValueCell.update(directVolume.bboxSize, Vec3.sub(directVolume.bboxSize.ref.value, bbox.max, bbox.min)) - ValueCell.update(directVolume.transform, transform) - } else { - directVolume = { - kind: 'direct-volume-3d' as 'direct-volume-3d', - gridDimension: ValueCell.create(gridDimension), - gridTexture: ValueCell.create(textureVolume), - bboxMin: ValueCell.create(bbox.min), - bboxMax: ValueCell.create(bbox.max), - bboxSize: ValueCell.create(Vec3.sub(Vec3.zero(), bbox.max, bbox.min)), - transform: ValueCell.create(transform), - } - } + const texture = directVolume ? directVolume.gridTexture.ref.value : createTexture(webgl, 'volume-uint8', 'rgba', 'ubyte', 'linear') + texture.load(textureVolume) - return directVolume; + return DirectVolume3d.create(bbox, gridDimension, transform, texture, directVolume) } // diff --git a/src/mol-gl/renderable/direct-volume.ts b/src/mol-gl/renderable/direct-volume.ts index 024848348e5bdea0cbeac1543fbc4c14fb8b83b5..4138d480b10c3659bac37f8db8d93cff876843dc 100644 --- a/src/mol-gl/renderable/direct-volume.ts +++ b/src/mol-gl/renderable/direct-volume.ts @@ -59,7 +59,7 @@ export const DirectVolume2dSchema = { ...DirectVolumeBaseSchema, dGridTexType: DefineSpec('string', ['2d']), uGridTexDim: UniformSpec('v2'), - tGridTex: TextureSpec('image-uint8', 'rgba', 'ubyte', 'linear'), + tGridTex: TextureSpec('texture2d', 'rgba', 'ubyte', 'linear'), } export type DirectVolume2dSchema = typeof DirectVolume2dSchema export type DirectVolume2dValues = Values<DirectVolume2dSchema> @@ -73,7 +73,7 @@ export function DirectVolume2dRenderable(ctx: Context, id: number, values: Direc export const DirectVolume3dSchema = { ...DirectVolumeBaseSchema, dGridTexType: DefineSpec('string', ['3d']), - tGridTex: TextureSpec('volume-uint8', 'rgba', 'ubyte', 'linear'), + tGridTex: TextureSpec('texture3d', 'rgba', 'ubyte', 'linear'), } export type DirectVolume3dSchema = typeof DirectVolume3dSchema export type DirectVolume3dValues = Values<DirectVolume3dSchema> diff --git a/src/mol-gl/renderable/schema.ts b/src/mol-gl/renderable/schema.ts index fd8db0a530df4d007e36510fbd0dfc6760d40386..1c25d2ab4258f5b6a94d944d29056a39654047aa 100644 --- a/src/mol-gl/renderable/schema.ts +++ b/src/mol-gl/renderable/schema.ts @@ -10,7 +10,7 @@ import { UniformKind, UniformValues } from '../webgl/uniform'; import { DefineKind, DefineValues } from '../shader-code'; import { Vec2, Vec3, Vec4, Mat3, Mat4 } from 'mol-math/linear-algebra'; import { TextureImage, TextureVolume } from './util'; -import { TextureValues, TextureType, TextureFormat, TextureFilter, TextureKind } from '../webgl/texture'; +import { TextureValues, TextureType, TextureFormat, TextureFilter, TextureKind, Texture } from '../webgl/texture'; export type ValueKindType = { 'number': number @@ -29,7 +29,7 @@ export type KindValue = { 'v4': Vec4 'm3': Mat3 'm4': Mat4 - 't2': number + 't': number 'uint8': Uint8Array 'int8': Int8Array @@ -43,6 +43,8 @@ export type KindValue = { 'image-float32': TextureImage<Float32Array> 'volume-uint8': TextureVolume<Uint8Array> 'volume-float32': TextureVolume<Float32Array> + 'texture2d': Texture + 'texture3d': Texture 'number': number 'string': string diff --git a/src/mol-gl/webgl/render-item.ts b/src/mol-gl/webgl/render-item.ts index 7c37fb60991798c495cadda0707299055d60f00b..fa224bdfc54f801ea22adc75a49153ce7be3ea20 100644 --- a/src/mol-gl/webgl/render-item.ts +++ b/src/mol-gl/webgl/render-item.ts @@ -14,6 +14,7 @@ import { idFactory } from 'mol-util/id-factory'; import { deleteVertexArray, createVertexArray } from './vertex-array'; import { ValueCell } from 'mol-util'; import { ReferenceItem } from 'mol-util/reference-cache'; +import { TextureImage, TextureVolume } from 'mol-gl/renderable/util'; const getNextRenderItemId = idFactory() @@ -227,9 +228,11 @@ export function createRenderItem(ctx: Context, drawMode: DrawMode, shaderCode: S const value = textureValues[k] if (value.ref.version !== versions[k]) { // console.log('texture version changed, uploading image', k) - textures[k].load(value.ref.value) - versions[k] = value.ref.version - valueChanges.textures = true + if (schema[k].kind !== 'texture2d' && schema[k].kind !== 'texture3d') { + textures[k].load(value.ref.value as TextureImage<any> | TextureVolume<any>) + versions[k] = value.ref.version + valueChanges.textures = true + } } }) diff --git a/src/mol-gl/webgl/texture.ts b/src/mol-gl/webgl/texture.ts index 6d6e2447b586497d07db458eaef43f683b568c0c..db42755fbe1fc0c2be5c970ade0f98b20735db6e 100644 --- a/src/mol-gl/webgl/texture.ts +++ b/src/mol-gl/webgl/texture.ts @@ -19,7 +19,10 @@ export type TextureKindValue = { 'image-float32': TextureImage<Float32Array> 'volume-uint8': TextureVolume<Uint8Array> 'volume-float32': TextureVolume<Float32Array> + 'texture2d': Texture + 'texture3d': Texture } +export type TextureValueType = Helpers.ValueOf<TextureKindValue> export type TextureKind = keyof TextureKindValue export type TextureType = 'ubyte' | 'float' export type TextureFormat = 'alpha' | 'rgb' | 'rgba' @@ -32,9 +35,16 @@ export function getTarget(ctx: Context, kind: TextureKind): number { switch (kind) { case 'image-uint8': return gl.TEXTURE_2D case 'image-float32': return gl.TEXTURE_2D - case 'volume-uint8': return (gl as WebGL2RenderingContext).TEXTURE_3D - case 'volume-float32': return (gl as WebGL2RenderingContext).TEXTURE_3D + case 'texture2d': return gl.TEXTURE_2D } + if(isWebGL2(gl)) { + switch (kind) { + case 'volume-uint8': return gl.TEXTURE_3D + case 'volume-float32': return gl.TEXTURE_3D + case 'texture3d': return gl.TEXTURE_3D + } + } + throw new Error('unknown texture kind') } export function getFormat(ctx: Context, format: TextureFormat): number { @@ -119,7 +129,7 @@ export interface Texture { readonly depth: number define: (width: number, height: number, depth?: number) => void - load: (image: TextureImage<any>) => void + load: (image: TextureImage<any> | TextureVolume<any>) => void bind: (id: TextureId) => void unbind: (id: TextureId) => void /** Use `layer` to attach a z-slice of a 3D texture */ @@ -130,7 +140,7 @@ export interface Texture { export type TextureId = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 -export type TextureValues = { [k: string]: ValueCell<TextureImage<any>> } +export type TextureValues = { [k: string]: ValueCell<TextureValueType> } export type Textures = { [k: string]: Texture } export function createTexture(ctx: Context, kind: TextureKind, _format: TextureFormat, _type: TextureType, _filter: TextureFilter): Texture { @@ -241,9 +251,13 @@ export function createTextures(ctx: Context, schema: RenderableSchema, values: T Object.keys(schema).forEach((k, i) => { const spec = schema[k] if (spec.type === 'texture') { - const texture = createTexture(ctx, spec.kind, spec.format, spec.dataType, spec.filter) - texture.load(values[k].ref.value) - textures[k] = texture + if (spec.kind === 'texture2d' || spec.kind === 'texture3d') { + textures[k] = values[k].ref.value as Texture + } else { + const texture = createTexture(ctx, spec.kind, spec.format, spec.dataType, spec.filter) + texture.load(values[k].ref.value as TextureImage<any> | TextureVolume<any>) + textures[k] = texture + } } }) return textures diff --git a/src/mol-gl/webgl/uniform.ts b/src/mol-gl/webgl/uniform.ts index 9fc22194e19ca8fdd6b8f488093bceb88297110a..7ef816a1d335ba63bdc65377f71222cde8598c8e 100644 --- a/src/mol-gl/webgl/uniform.ts +++ b/src/mol-gl/webgl/uniform.ts @@ -17,7 +17,7 @@ export type UniformKindValue = { 'v4': Vec4 'm3': Mat3 'm4': Mat4 - 't2': number + 't': number } export type UniformKind = keyof UniformKindValue export type UniformType = number | Vec2 | Vec3 | Vec4 | Mat3 | Mat4 @@ -37,7 +37,7 @@ function createUniformSetter(ctx: Context, program: WebGLProgram, name: string, } switch (kind) { case 'f': return (value: number) => gl.uniform1f(location, value) - case 'i': case 't2': return (value: number) => gl.uniform1i(location, value) + case 'i': case 't': return (value: number) => gl.uniform1i(location, value) case 'v2': return (value: Vec2) => (gl as WebGLRenderingContext).uniform2fv(location, value) // TODO remove cast when webgl2 types are fixed case 'v3': return (value: Vec3) => (gl as WebGLRenderingContext).uniform3fv(location, value) case 'v4': return (value: Vec4) => (gl as WebGLRenderingContext).uniform4fv(location, value) @@ -78,7 +78,7 @@ export function getTextureUniformUpdaters(ctx: Context, program: WebGLProgram, s Object.keys(schema).forEach(k => { const spec = schema[k] if (spec.type === 'texture') { - updaters[k] = createUniformUpdater(ctx, program, k, 't2') + updaters[k] = createUniformUpdater(ctx, program, k, 't') } }) return updaters diff --git a/src/mol-math/geometry/common.ts b/src/mol-math/geometry/common.ts index e7d730768f119e4aee5890536b8793ec56cd98e2..6eab9cfa2492ef2a0cc1b95145ab8fa646805dd3 100644 --- a/src/mol-math/geometry/common.ts +++ b/src/mol-math/geometry/common.ts @@ -24,8 +24,11 @@ export type DensityData = { transform: Mat4, field: Tensor, idField: Tensor, +} - texture?: Texture, - bbox?: Box3D, - gridDimension?: Vec3 +export type DensityTextureData = { + transform: Mat4, + texture: Texture, + bbox: Box3D, + gridDimension: Vec3 } \ No newline at end of file diff --git a/src/mol-math/geometry/gaussian-density.ts b/src/mol-math/geometry/gaussian-density.ts index cdc1f696f0095691cec96b72b21ccf3884aed0a5..f7e39348e622e8ff2429dc66e57d7df9765c8c79 100644 --- a/src/mol-math/geometry/gaussian-density.ts +++ b/src/mol-math/geometry/gaussian-density.ts @@ -9,6 +9,7 @@ import { Vec3 } from '../linear-algebra'; import { RuntimeContext, Task } from 'mol-task'; import { PositionData, DensityData } from './common'; import { GaussianDensityCPU } from './gaussian-density/cpu'; +import { Context } from 'mol-gl/webgl/context'; // import { GaussianDensityGPU } from './gaussian-density/gpu'; const GaussianDensityGPU = typeof document !== 'undefined' @@ -20,6 +21,7 @@ export const DefaultGaussianDensityProps = { radiusOffset: 0, smoothness: 1.5, useGpu: true, + webgl: undefined as Context | undefined } export type GaussianDensityProps = typeof DefaultGaussianDensityProps diff --git a/src/mol-math/geometry/gaussian-density/gpu.ts b/src/mol-math/geometry/gaussian-density/gpu.ts index 0d42cc35c71367237b834c1890380571b14181be..bd86f88b7d799b268255a66f3910b77e180974f7 100644 --- a/src/mol-math/geometry/gaussian-density/gpu.ts +++ b/src/mol-math/geometry/gaussian-density/gpu.ts @@ -6,13 +6,13 @@ */ import { RuntimeContext } from 'mol-task' -import { PositionData, DensityData } from '../common' +import { PositionData, DensityData, DensityTextureData } from '../common' import { Box3D } from '../../geometry' import { GaussianDensityProps, getDelta } from '../gaussian-density' import { OrderedSet } from 'mol-data/int' import { Vec3, Tensor, Mat4 } from '../../linear-algebra' import { GaussianDensityValues } from 'mol-gl/renderable/gaussian-density' -import { ValueCell } from 'mol-util' +import { ValueCell, defaults } from 'mol-util' import { RenderableState } from 'mol-gl/renderable' import { createRenderable, createGaussianDensityRenderObject } from 'mol-gl/render-object' import { Context, createContext, getGLContext } from 'mol-gl/webgl/context'; @@ -20,38 +20,38 @@ import { createFramebuffer } from 'mol-gl/webgl/framebuffer'; import { createTexture, Texture, TextureAttachment } from 'mol-gl/webgl/texture'; import { GLRenderingContext } from 'mol-gl/webgl/compat'; -export async function GaussianDensityGPU(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps): Promise<DensityData> { - // TODO allow passing a context via props - const webgl = getWebGLContext() +export async function GaussianDensityGPU(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps): Promise<DensityData> { + const webgl = defaults(props.webgl, getWebGLContext()) - const useMultiDraw = webgl.maxDrawBuffers > 0 - console.log('useMultiDraw', useMultiDraw) - - console.time('gpu gaussian density render') - const { texture, scale, bbox, dim } = useMultiDraw ? - await GaussianDensityMultiDrawBuffer(ctx, webgl, position, box, radius, props) : - await GaussianDensitySingleDrawBuffer(ctx, webgl, position, box, radius, props) - console.timeEnd('gpu gaussian density render') + const { transform, texture, gridDimension } = await GaussianDensityTexture(ctx, webgl, position, box, radius, props) console.time('gpu gaussian density read') - const field = useMultiDraw ? - fieldFromTexture3d(webgl, texture, dim) : - fieldFromTexture2d(webgl, texture, dim) + const field = webgl.maxDrawBuffers > 0 ? + fieldFromTexture3d(webgl, texture, gridDimension) : + fieldFromTexture2d(webgl, texture, gridDimension) console.timeEnd('gpu gaussian density read') const idData = field.space.create() const idField = Tensor.create(field.space, idData) + return { field, idField, transform } +} + +export async function GaussianDensityTexture(ctx: RuntimeContext, webgl: Context, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps, oldTexture?: Texture): Promise<DensityTextureData> { + const { texture, scale, bbox, dim } = webgl.maxDrawBuffers > 0 ? + await GaussianDensityMultiDrawBuffer(ctx, webgl, position, box, radius, props, oldTexture) : + await GaussianDensitySingleDrawBuffer(ctx, webgl, position, box, radius, props, oldTexture) + const transform = Mat4.identity() Mat4.fromScaling(transform, scale) Mat4.setTranslation(transform, bbox.min) - return { field, idField, transform, texture, bbox, gridDimension: dim } + return { transform, texture, bbox, gridDimension: dim } } // -async function GaussianDensitySingleDrawBuffer(ctx: RuntimeContext, webgl: Context, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps) { +async function GaussianDensitySingleDrawBuffer(ctx: RuntimeContext, webgl: Context, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps, texture?: Texture) { const { smoothness } = props const { drawCount, positions, radii, delta, expandedBox, dim } = await prepareGaussianDensityData(ctx, position, box, radius, props) @@ -83,7 +83,9 @@ async function GaussianDensitySingleDrawBuffer(ctx: RuntimeContext, webgl: Conte const framebuffer = createFramebuffer(webgl) framebuffer.bind() - const texture = createTexture(webgl, 'image-uint8', 'rgba', 'ubyte', 'linear') + if (!texture) { + texture = createTexture(webgl, 'image-uint8', 'rgba', 'ubyte', 'linear') + } texture.define(fboTexDimX, fboTexDimY) const program = renderable.getProgram('draw') @@ -114,7 +116,7 @@ async function GaussianDensitySingleDrawBuffer(ctx: RuntimeContext, webgl: Conte return { texture, scale: Vec3.inverse(Vec3.zero(), delta), bbox: expandedBox, dim } } -async function GaussianDensityMultiDrawBuffer(ctx: RuntimeContext, webgl: Context, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps) { +async function GaussianDensityMultiDrawBuffer(ctx: RuntimeContext, webgl: Context, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps, texture?: Texture) { const { smoothness } = props const { drawCount, positions, radii, delta, expandedBox, dim } = await prepareGaussianDensityData(ctx, position, box, radius, props) @@ -131,7 +133,9 @@ async function GaussianDensityMultiDrawBuffer(ctx: RuntimeContext, webgl: Contex const framebuffer = createFramebuffer(webgl) framebuffer.bind() - const texture = createTexture(webgl, 'volume-uint8', 'rgba', 'ubyte', 'linear') + if (!texture) { + texture = createTexture(webgl, 'volume-uint8', 'rgba', 'ubyte', 'linear') + } texture.define(dx, dy, dz) if (drawBuffers === 1) { @@ -204,7 +208,7 @@ function getWebGLContext() { return webglContext } -async function prepareGaussianDensityData(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps) { +async function prepareGaussianDensityData(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps) { const { resolution, radiusOffset } = props const { indices, x, y, z } = position diff --git a/src/mol-model/structure/structure/unit/gaussian-density.ts b/src/mol-model/structure/structure/unit/gaussian-density.ts index 63e27cb5758c0a939df922ebdc430a1be301e1ff..a5d4d0681972dc13e3a20449cd8712a594489975 100644 --- a/src/mol-model/structure/structure/unit/gaussian-density.ts +++ b/src/mol-model/structure/structure/unit/gaussian-density.ts @@ -9,7 +9,10 @@ import { SizeTheme } from 'mol-view/theme/size'; import { GaussianDensity } from 'mol-math/geometry/gaussian-density'; import { Task, RuntimeContext } from 'mol-task'; import { DensityData } from 'mol-math/geometry'; -import { NumberParam, paramDefaultValues, BooleanParam } from 'mol-view/parameter'; +import { NumberParam, paramDefaultValues, BooleanParam, ValueParam } from 'mol-view/parameter'; +import { Context } from 'mol-gl/webgl/context'; +import { GaussianDensityTexture } from 'mol-math/geometry/gaussian-density/gpu'; +import { Texture } from 'mol-gl/webgl/texture'; export const GaussianDensityParams = { resolution: NumberParam('Resolution', '', 1, 0.1, 10, 0.1), @@ -17,6 +20,7 @@ export const GaussianDensityParams = { smoothness: NumberParam('Smoothness', '', 1.5, 0.5, 2.5, 0.1), useGpu: BooleanParam('Use GPU', '', true), ignoreCache: BooleanParam('Ignore Cache', '', false), + webgl: ValueParam('WebGL Context', '', undefined as Context | undefined), } export const DefaultGaussianDensityProps = paramDefaultValues(GaussianDensityParams) export type GaussianDensityProps = typeof DefaultGaussianDensityProps @@ -29,7 +33,7 @@ function getConformation(unit: Unit) { } } -export function computeUnitGaussianDensity(unit: Unit, props: GaussianDensityProps) { +function getConformationAndRadius(unit: Unit) { const conformation = getConformation(unit) const { elements } = unit const position = { @@ -46,11 +50,25 @@ export function computeUnitGaussianDensity(unit: Unit, props: GaussianDensityPro return sizeTheme.size(l) } + return { position, radius } +} + +export function computeUnitGaussianDensity(unit: Unit, props: GaussianDensityProps) { + const { position, radius } = getConformationAndRadius(unit) return Task.create('Gaussian Density', async ctx => { return await GaussianDensity(ctx, position, unit.lookup3d.boundary.box, radius, props); }); } +export function computeUnitGaussianDensityTexture(unit: Unit, props: GaussianDensityProps, texture?: Texture) { + const webgl = props.webgl + if (!webgl) throw new Error('nned webgl context for computeUnitGaussianDensityTexture') + const { position, radius } = getConformationAndRadius(unit) + return Task.create('Gaussian Density', async ctx => { + return await GaussianDensityTexture(ctx, webgl, position, unit.lookup3d.boundary.box, radius, props, texture); + }); +} + export async function computeUnitGaussianDensityCached(unit: Unit, props: GaussianDensityProps, cache: Map<string, DensityData>, ctx?: RuntimeContext) { const key = `${props.radiusOffset}|${props.resolution}|${props.smoothness}` let density = cache.get(key)