diff --git a/src/mol-canvas3d/helper/bounding-sphere-helper.ts b/src/mol-canvas3d/helper/bounding-sphere-helper.ts index 191e13a807569f380adbe458ce44e4589d329560..8f5a9634784f0812431f232bf71a145724796116 100644 --- a/src/mol-canvas3d/helper/bounding-sphere-helper.ts +++ b/src/mol-canvas3d/helper/bounding-sphere-helper.ts @@ -4,7 +4,7 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { createRenderObject, RenderObject, getNextMaterialId } from 'mol-gl/render-object' +import { createRenderObject, GraphicsRenderObject, getNextMaterialId } from 'mol-gl/render-object' import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder'; import { addSphere } from 'mol-geo/geometry/mesh/builder/sphere'; import { Mesh } from 'mol-geo/geometry/mesh/mesh'; @@ -27,15 +27,15 @@ export const DebugHelperParams = { export type DebugHelperParams = typeof DebugHelperParams export type DebugHelperProps = PD.Values<DebugHelperParams> -type BoundingSphereData = { boundingSphere: Sphere3D, renderObject: RenderObject, mesh: Mesh } +type BoundingSphereData = { boundingSphere: Sphere3D, renderObject: GraphicsRenderObject, mesh: Mesh } export class BoundingSphereHelper { readonly scene: Scene private readonly parent: Scene private _props: DebugHelperProps - private objectsData = new Map<RenderObject, BoundingSphereData>() - private instancesData = new Map<RenderObject, BoundingSphereData>() + private objectsData = new Map<GraphicsRenderObject, BoundingSphereData>() + private instancesData = new Map<GraphicsRenderObject, BoundingSphereData>() private sceneData: BoundingSphereData | undefined constructor(ctx: WebGLContext, parent: Scene, props: Partial<DebugHelperProps>) { diff --git a/src/mol-gl/render-object.ts b/src/mol-gl/render-object.ts index 0103f84fa9bec989bc7168ee7d21926af8f24cb0..d2fc23f412db073b39e79335fbfc88755217ce7a 100644 --- a/src/mol-gl/render-object.ts +++ b/src/mol-gl/render-object.ts @@ -8,7 +8,6 @@ import { RenderableState, Renderable } from './renderable' import { RenderableValues } from './renderable/schema'; import { idFactory } from 'mol-util/id-factory'; import { WebGLContext } from './webgl/context'; -import { GaussianDensityValues, GaussianDensityRenderable } from './renderable/gaussian-density'; import { DirectVolumeValues, DirectVolumeRenderable } from './renderable/direct-volume'; import { MeshValues, MeshRenderable } from './renderable/mesh'; import { PointsValues, PointsRenderable } from './renderable/points'; @@ -28,16 +27,10 @@ export interface TextRenderObject extends BaseRenderObject { type: 'text', value export interface LinesRenderObject extends BaseRenderObject { type: 'lines', values: LinesValues } export interface DirectVolumeRenderObject extends BaseRenderObject { type: 'direct-volume', values: DirectVolumeValues } -export interface GaussianDensityRenderObject extends BaseRenderObject { type: 'gaussian-density', values: GaussianDensityValues } - // export type GraphicsRenderObject = MeshRenderObject | PointsRenderObject | SpheresRenderObject | TextRenderObject | LinesRenderObject | DirectVolumeRenderObject -export type ComputeRenderObject = GaussianDensityRenderObject - -export type RenderObject = GraphicsRenderObject | ComputeRenderObject - export type RenderObjectKindType = { 'mesh': MeshRenderObject 'points': PointsRenderObject @@ -45,8 +38,6 @@ export type RenderObjectKindType = { 'text': TextRenderObject 'lines': LinesRenderObject 'direct-volume': DirectVolumeRenderObject - - 'gaussian-density': GaussianDensityRenderObject } export type RenderObjectValuesType = { 'mesh': MeshValues @@ -55,8 +46,6 @@ export type RenderObjectValuesType = { 'text': TextValues 'lines': LinesValues 'direct-volume': DirectVolumeValues - - 'gaussian-density': GaussianDensityValues } export type RenderObjectType = keyof RenderObjectKindType @@ -66,7 +55,7 @@ export function createRenderObject<T extends RenderObjectType>(type: T, values: return { id: getNextId(), type, values, state, materialId } as RenderObjectKindType[T] } -export function createRenderable(ctx: WebGLContext, o: RenderObject): Renderable<any> { +export function createRenderable(ctx: WebGLContext, o: GraphicsRenderObject): Renderable<any> { switch (o.type) { case 'mesh': return MeshRenderable(ctx, o.id, o.values, o.state, o.materialId) case 'points': return PointsRenderable(ctx, o.id, o.values, o.state, o.materialId) @@ -74,7 +63,5 @@ export function createRenderable(ctx: WebGLContext, o: RenderObject): Renderable case 'text': return TextRenderable(ctx, o.id, o.values, o.state, o.materialId) case 'lines': return LinesRenderable(ctx, o.id, o.values, o.state, o.materialId) case 'direct-volume': return DirectVolumeRenderable(ctx, o.id, o.values, o.state, o.materialId) - - case 'gaussian-density': return GaussianDensityRenderable(ctx, o.id, o.values, o.state) } } \ No newline at end of file diff --git a/src/mol-gl/renderable.ts b/src/mol-gl/renderable.ts index 34f0eee4159c8750f94c7eabc9669119e7b4a6b8..1139a14dd9c53796c1903eee76c51c55c2d94d53 100644 --- a/src/mol-gl/renderable.ts +++ b/src/mol-gl/renderable.ts @@ -50,4 +50,28 @@ export function createRenderable<T extends Values<RenderableSchema>>(renderItem: update: () => renderItem.update(), dispose: () => renderItem.destroy() } +} + +// + +export interface ComputeRenderable<T extends RenderableValues> { + readonly id: number + readonly values: T + + render: () => void + getProgram: () => Program + update: () => void + dispose: () => void +} + +export function createComputeRenderable<T extends Values<RenderableSchema>>(renderItem: RenderItem, values: T): ComputeRenderable<T> { + return { + id: getNextRenderableId(), + values, + + render: () => renderItem.render('draw'), + getProgram: () => renderItem.getProgram('draw'), + update: () => renderItem.update(), + dispose: () => renderItem.destroy() + } } \ No newline at end of file diff --git a/src/mol-gl/scene.ts b/src/mol-gl/scene.ts index e8e079ceb5d01b3aeb5e0a07442bc510e0d481a4..6e2685cc33c53ddb08e9d6c00f8f7c27ce92519a 100644 --- a/src/mol-gl/scene.ts +++ b/src/mol-gl/scene.ts @@ -7,7 +7,7 @@ import { Renderable } from './renderable' import { WebGLContext } from './webgl/context'; import { RenderableValues, BaseValues } from './renderable/schema'; -import { RenderObject, createRenderable, GraphicsRenderObject } from './render-object'; +import { GraphicsRenderObject, createRenderable } from './render-object'; import { Object3D } from './object3d'; import { Sphere3D } from 'mol-math/geometry'; import { Vec3 } from 'mol-math/linear-algebra'; @@ -58,16 +58,16 @@ interface Scene extends Object3D { readonly boundingSphere: Sphere3D update: (objects: ArrayLike<GraphicsRenderObject> | undefined, keepBoundingSphere: boolean) => void - add: (o: RenderObject) => Renderable<any> - remove: (o: RenderObject) => void - has: (o: RenderObject) => boolean + add: (o: GraphicsRenderObject) => Renderable<any> + remove: (o: GraphicsRenderObject) => void + has: (o: GraphicsRenderObject) => boolean clear: () => void - forEach: (callbackFn: (value: Renderable<RenderableValues & BaseValues>, key: RenderObject) => void) => void + forEach: (callbackFn: (value: Renderable<RenderableValues & BaseValues>, key: GraphicsRenderObject) => void) => void } namespace Scene { export function create(ctx: WebGLContext): Scene { - const renderableMap = new Map<RenderObject, Renderable<RenderableValues & BaseValues>>() + const renderableMap = new Map<GraphicsRenderObject, Renderable<RenderableValues & BaseValues>>() const renderables: Renderable<RenderableValues & BaseValues>[] = [] const boundingSphere = Sphere3D.zero() let boundingSphereDirty = true @@ -95,7 +95,7 @@ namespace Scene { } if (!keepBoundingSphere) boundingSphereDirty = true }, - add: (o: RenderObject) => { + add: (o: GraphicsRenderObject) => { if (!renderableMap.has(o)) { const renderable = createRenderable(ctx, o) renderables.push(renderable) @@ -108,7 +108,7 @@ namespace Scene { return renderableMap.get(o)! } }, - remove: (o: RenderObject) => { + remove: (o: GraphicsRenderObject) => { const renderable = renderableMap.get(o) if (renderable) { renderable.dispose() @@ -118,7 +118,7 @@ namespace Scene { boundingSphereDirty = true } }, - has: (o: RenderObject) => { + has: (o: GraphicsRenderObject) => { return renderableMap.has(o) }, clear: () => { @@ -129,7 +129,7 @@ namespace Scene { renderableMap.clear() boundingSphereDirty = true }, - forEach: (callbackFn: (value: Renderable<any>, key: RenderObject) => void) => { + forEach: (callbackFn: (value: Renderable<any>, key: GraphicsRenderObject) => void) => { renderableMap.forEach(callbackFn) }, get count() { diff --git a/src/mol-gl/shader-code.ts b/src/mol-gl/shader-code.ts index 48e635d1271d17a18261167443f74c16b6e26195..dc3a3f88e6c4a5010714b4df143f632947665abe 100644 --- a/src/mol-gl/shader-code.ts +++ b/src/mol-gl/shader-code.ts @@ -60,12 +60,6 @@ export const MeshShaderCode = ShaderCode( { standardDerivatives: true, fragDepth: false } ) -export const GaussianDensityShaderCode = ShaderCode( - require('mol-gl/shader/gaussian-density.vert'), - require('mol-gl/shader/gaussian-density.frag'), - { standardDerivatives: false, fragDepth: false } -) - export const DirectVolumeShaderCode = ShaderCode( require('mol-gl/shader/direct-volume.vert'), require('mol-gl/shader/direct-volume.frag'), diff --git a/src/mol-math/geometry/gaussian-density/gpu.ts b/src/mol-math/geometry/gaussian-density/gpu.ts index 2a361c5ae427e06737ed9ff7901ed97aa20e4753..312de7cf2e86e9da8ad406b10d75dae192622090 100644 --- a/src/mol-math/geometry/gaussian-density/gpu.ts +++ b/src/mol-math/geometry/gaussian-density/gpu.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Michael Krone <michael.krone@uni-tuebingen.de> @@ -11,17 +11,47 @@ import { Box3D } from '../../geometry' import { GaussianDensityGPUProps, 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 { RenderableState, Renderable } from 'mol-gl/renderable' -import { createRenderable, createRenderObject } from 'mol-gl/render-object' +import { createComputeRenderable, ComputeRenderable } from 'mol-gl/renderable' import { WebGLContext } from 'mol-gl/webgl/context'; import { createTexture, Texture } from 'mol-gl/webgl/texture'; import { GLRenderingContext } from 'mol-gl/webgl/compat'; import { decodeFloatRGB } from 'mol-util/float-packing'; +import { ShaderCode } from 'mol-gl/shader-code'; +import { createRenderItem } from 'mol-gl/webgl/render-item'; +import { ValueSpec, AttributeSpec, UniformSpec, TextureSpec, DefineSpec, Values } from 'mol-gl/renderable/schema'; + +export const GaussianDensitySchema = { + drawCount: ValueSpec('number'), + instanceCount: ValueSpec('number'), + + aRadius: AttributeSpec('float32', 1, 0), + aPosition: AttributeSpec('float32', 3, 0), + aGroup: AttributeSpec('float32', 1, 0), + + uCurrentSlice: UniformSpec('f'), + uCurrentX: UniformSpec('f'), + uCurrentY: UniformSpec('f'), + uBboxMin: UniformSpec('v3'), + uBboxMax: UniformSpec('v3'), + uBboxSize: UniformSpec('v3'), + uGridDim: UniformSpec('v3'), + uGridTexDim: UniformSpec('v3'), + uAlpha: UniformSpec('f'), + tMinDistanceTex: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'), + + dGridTexType: DefineSpec('string', ['2d', '3d']), + dCalcType: DefineSpec('string', ['density', 'minDistance', 'groupId']), +} + +export const GaussianDensityShaderCode = ShaderCode( + require('mol-gl/shader/gaussian-density.vert'), + require('mol-gl/shader/gaussian-density.frag'), + { standardDerivatives: false, fragDepth: false } +) /** name for shared framebuffer used for gpu gaussian surface operations */ -const FramebufferName = 'gaussian-density-gpu' +const FramebufferName = 'gaussian-density' export async function GaussianDensityGPU(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, webgl: WebGLContext): Promise<DensityData> { // always use texture2d when the gaussian density needs to be downloaded from the GPU, @@ -64,13 +94,12 @@ async function GaussianDensityTexture2d(ctx: RuntimeContext, webgl: WebGLContext const minDistanceTexture = createTexture(webgl, 'image-uint8', 'rgba', 'ubyte', 'nearest') minDistanceTexture.define(texDimX, texDimY) - const renderObject = getGaussianDensityRenderObject(webgl, drawCount, positions, radii, groups, minDistanceTexture, expandedBox, dim, smoothness) - const renderable = createRenderable(webgl, renderObject) + const renderable = getGaussianDensityRenderable(webgl, drawCount, positions, radii, groups, minDistanceTexture, expandedBox, dim, smoothness) // const { gl, framebufferCache } = webgl - const { uCurrentSlice, uCurrentX, uCurrentY } = renderObject.values + const { uCurrentSlice, uCurrentX, uCurrentY } = renderable.values const framebuffer = framebufferCache.get(webgl, FramebufferName).value framebuffer.bind() @@ -94,7 +123,7 @@ async function GaussianDensityTexture2d(ctx: RuntimeContext, webgl: WebGLContext ValueCell.update(uCurrentSlice, i) ValueCell.update(uCurrentX, currX) ValueCell.update(uCurrentY, currY) - renderable.render('draw') + renderable.render() ++currCol currX += dx } @@ -123,13 +152,12 @@ async function GaussianDensityTexture3d(ctx: RuntimeContext, webgl: WebGLContext const minDistanceTexture = createTexture(webgl, 'volume-uint8', 'rgba', 'ubyte', 'nearest') minDistanceTexture.define(dx, dy, dz) - const renderObject = getGaussianDensityRenderObject(webgl, drawCount, positions, radii, groups, minDistanceTexture, expandedBox, dim, smoothness) - const renderable = createRenderable(webgl, renderObject) + const renderable = getGaussianDensityRenderable(webgl, drawCount, positions, radii, groups, minDistanceTexture, expandedBox, dim, smoothness) // const { gl, framebufferCache } = webgl - const { uCurrentSlice } = renderObject.values + const { uCurrentSlice } = renderable.values const framebuffer = framebufferCache.get(webgl, FramebufferName).value framebuffer.bind() @@ -143,7 +171,7 @@ async function GaussianDensityTexture3d(ctx: RuntimeContext, webgl: WebGLContext for (let i = 0; i < dz; ++i) { ValueCell.update(uCurrentSlice, i) fbTex.attachFramebuffer(framebuffer, 0, i) - renderable.render('draw') + renderable.render() } } @@ -204,11 +232,11 @@ async function prepareGaussianDensityData(ctx: RuntimeContext, position: Positio return { drawCount: n, positions, radii, groups, delta, expandedBox, dim } } -function getGaussianDensityRenderObject(webgl: WebGLContext, drawCount: number, positions: Float32Array, radii: Float32Array, groups: Float32Array, minDistanceTexture: Texture, box: Box3D, dimensions: Vec3, smoothness: number) { +function getGaussianDensityRenderable(webgl: WebGLContext, drawCount: number, positions: Float32Array, radii: Float32Array, groups: Float32Array, minDistanceTexture: Texture, box: Box3D, dimensions: Vec3, smoothness: number) { const extent = Vec3.sub(Vec3.zero(), box.max, box.min) const { texDimX, texDimY } = getTexture2dSize(webgl.maxTextureSize, dimensions) - const values: GaussianDensityValues = { + const values: Values<typeof GaussianDensitySchema> = { drawCount: ValueCell.create(drawCount), instanceCount: ValueCell.create(1), @@ -230,16 +258,12 @@ function getGaussianDensityRenderObject(webgl: WebGLContext, drawCount: number, dGridTexType: ValueCell.create(minDistanceTexture.depth > 0 ? '3d' : '2d'), dCalcType: ValueCell.create('density'), } - const state: RenderableState = { - visible: true, - alphaFactor: 1, - pickable: false, - opaque: true - } - const renderObject = createRenderObject('gaussian-density', values, state, -1) + const schema = { ...GaussianDensitySchema } + const shaderCode = GaussianDensityShaderCode + const renderItem = createRenderItem(webgl, 'points', shaderCode, schema, values, -1) - return renderObject + return createComputeRenderable(renderItem, values) } function setRenderingDefaults(gl: GLRenderingContext) { @@ -249,30 +273,30 @@ function setRenderingDefaults(gl: GLRenderingContext) { gl.enable(gl.BLEND) } -function setupMinDistanceRendering(webgl: WebGLContext, renderable: Renderable<any>) { +function setupMinDistanceRendering(webgl: WebGLContext, renderable: ComputeRenderable<any>) { const { gl } = webgl ValueCell.update(renderable.values.dCalcType, 'minDistance') renderable.update() - renderable.getProgram('draw').use() + renderable.getProgram().use() gl.blendFunc(gl.ONE, gl.ONE) // the shader writes 1 - dist so we set blending to MAX gl.blendEquation(webgl.extensions.blendMinMax.MAX) } -function setupDensityRendering(webgl: WebGLContext, renderable: Renderable<any>) { +function setupDensityRendering(webgl: WebGLContext, renderable: ComputeRenderable<any>) { const { gl } = webgl ValueCell.update(renderable.values.dCalcType, 'density') renderable.update() - renderable.getProgram('draw').use() + renderable.getProgram().use() gl.blendFunc(gl.ONE, gl.ONE) gl.blendEquation(gl.FUNC_ADD) } -function setupGroupIdRendering(webgl: WebGLContext, renderable: Renderable<any>) { +function setupGroupIdRendering(webgl: WebGLContext, renderable: ComputeRenderable<any>) { const { gl } = webgl ValueCell.update(renderable.values.dCalcType, 'groupId') renderable.update() - renderable.getProgram('draw').use() + renderable.getProgram().use() // overwrite color, don't change alpha gl.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ZERO, gl.ONE) gl.blendEquation(gl.FUNC_ADD)