diff --git a/src/mol-geo/geometry/geometry.ts b/src/mol-geo/geometry/geometry.ts index 668e118df8cb0377f01069cf1c9ffadcd0a36d51..4663f7286ab47426b7cc5f3566332c7ed07e9ee0 100644 --- a/src/mol-geo/geometry/geometry.ts +++ b/src/mol-geo/geometry/geometry.ts @@ -21,6 +21,7 @@ import { TransformData } from './transform-data'; import { Theme } from 'mol-theme/theme'; import { RenderObjectValuesType } from 'mol-gl/render-object'; import { ValueOf } from 'mol-util/type-helpers'; +import { Isosurface } from './isosurface/isosurface'; export type GeometryKindType = { 'mesh': Mesh, @@ -29,6 +30,7 @@ export type GeometryKindType = { 'text': Text, 'lines': Lines, 'direct-volume': DirectVolume, + 'isosurface': Isosurface, } export type GeometryKindParams = { 'mesh': Mesh.Params, @@ -37,6 +39,7 @@ export type GeometryKindParams = { 'text': Text.Params, 'lines': Lines.Params, 'direct-volume': DirectVolume.Params, + 'isosurface': Isosurface.Params, } export type GeometryKind = keyof GeometryKindType export type Geometry = ValueOf<GeometryKindType> @@ -63,6 +66,7 @@ export namespace Geometry { case 'text': return geometry.charCount * 2 * 3 case 'lines': return geometry.lineCount * 2 * 3 case 'direct-volume': return 12 * 3 + case 'isosurface': return geometry.vertexCount.ref.value * 3 } } @@ -76,6 +80,8 @@ export namespace Geometry { return getDrawCount(geometry) === 0 ? 0 : (arrayMax(geometry.groupBuffer.ref.value) + 1) case 'direct-volume': return 1 + case 'isosurface': + return geometry.groupCount.ref.value } } @@ -88,6 +94,7 @@ export namespace Geometry { case 'text': return Text.Utils as any case 'lines': return Lines.Utils as any case 'direct-volume': return DirectVolume.Utils as any + case 'isosurface': return Isosurface.Utils as any } throw new Error('unknown geometry kind') } diff --git a/src/mol-geo/geometry/isosurface/isosurface.ts b/src/mol-geo/geometry/isosurface/isosurface.ts new file mode 100644 index 0000000000000000000000000000000000000000..9b72acbb4007f67499f0dce72756464ffbd2819e --- /dev/null +++ b/src/mol-geo/geometry/isosurface/isosurface.ts @@ -0,0 +1,159 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { ValueCell } from 'mol-util' +import { Sphere3D } from 'mol-math/geometry' +import { ParamDefinition as PD } from 'mol-util/param-definition'; +import { LocationIterator } from 'mol-geo/util/location-iterator'; +import { TransformData } from '../transform-data'; +import { createColors } from '../color-data'; +import { createMarkers } from '../marker-data'; +import { GeometryUtils } from '../geometry'; +import { Theme } from 'mol-theme/theme'; +import { Color } from 'mol-util/color'; +import { BaseGeometry } from '../base'; +import { createEmptyOverpaint } from '../overpaint-data'; +import { createEmptyTransparency } from '../transparency-data'; +import { IsosurfaceValues } from 'mol-gl/renderable/isosurface'; +import { calculateTransformBoundingSphere } from 'mol-gl/renderable/util'; +import { Texture } from 'mol-gl/webgl/texture'; +import { Vec2 } from 'mol-math/linear-algebra'; +import { fillSerial } from 'mol-util/array'; + +export interface Isosurface { + readonly kind: 'isosurface', + + /** Number of vertices in the isosurface */ + readonly vertexCount: ValueCell<number>, + /** Number of groups in the isosurface */ + readonly groupCount: ValueCell<number>, + + readonly vertexTexture: ValueCell<Texture>, + readonly vertexTextureDim: ValueCell<Vec2>, + + /** Normal buffer as array of xyz values for each vertex wrapped in a value cell */ + readonly normalBuffer: ValueCell<Float32Array>, + /** Group buffer as array of group ids for each vertex wrapped in a value cell */ + readonly groupBuffer: ValueCell<Float32Array>, + + readonly boundingSphere: ValueCell<Sphere3D>, +} + +export namespace Isosurface { + export function create(vertexCount: number, groupCount: number, vertexTexture: Texture, normalBuffer: Float32Array, groupBuffer: Float32Array, boundingSphere: Sphere3D, isosurface?: Isosurface): Isosurface { + const { width, height } = vertexTexture + if (isosurface) { + ValueCell.update(isosurface.vertexCount, vertexCount) + ValueCell.update(isosurface.groupCount, groupCount) + ValueCell.update(isosurface.vertexTexture, vertexTexture) + ValueCell.update(isosurface.vertexTextureDim, Vec2.set(isosurface.vertexTextureDim.ref.value, width, height)) + ValueCell.update(isosurface.normalBuffer, normalBuffer) + ValueCell.update(isosurface.groupBuffer, groupBuffer) + ValueCell.update(isosurface.boundingSphere, boundingSphere) + return isosurface + } else { + return { + kind: 'isosurface', + vertexCount: ValueCell.create(vertexCount), + groupCount: ValueCell.create(groupCount), + vertexTexture: ValueCell.create(vertexTexture), + vertexTextureDim: ValueCell.create(Vec2.create(width, height)), + normalBuffer: ValueCell.create(normalBuffer), + groupBuffer: ValueCell.create(groupBuffer), + boundingSphere: ValueCell.create(boundingSphere), + } + } + } + + export function createEmpty(isosurface?: Isosurface): Isosurface { + return {} as Isosurface // TODO + } + + export const Params = { + ...BaseGeometry.Params, + doubleSided: PD.Boolean(false), + flipSided: PD.Boolean(false), + flatShaded: PD.Boolean(false), + } + export type Params = typeof Params + + export const Utils: GeometryUtils<Isosurface, Params> = { + Params, + createEmpty, + createValues, + createValuesSimple, + updateValues, + updateBoundingSphere, + createRenderableState: BaseGeometry.createRenderableState, + updateRenderableState: BaseGeometry.updateRenderableState + } + + function createValues(isosurface: Isosurface, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): IsosurfaceValues { + const { instanceCount, groupCount } = locationIt + const color = createColors(locationIt, theme.color) + const marker = createMarkers(instanceCount * groupCount) + const overpaint = createEmptyOverpaint() + const transparency = createEmptyTransparency() + + const counts = { drawCount: isosurface.vertexCount.ref.value, groupCount, instanceCount } + + const transformBoundingSphere = calculateTransformBoundingSphere(isosurface.boundingSphere.ref.value, transform.aTransform.ref.value, transform.instanceCount.ref.value) + + return { + tPosition: isosurface.vertexTexture, + uPositionTexDim: isosurface.vertexTextureDim, + aIndex: ValueCell.create(fillSerial(new Float32Array(isosurface.vertexCount.ref.value))), + aNormal: isosurface.normalBuffer, + aGroup: isosurface.groupBuffer, + boundingSphere: ValueCell.create(transformBoundingSphere), + invariantBoundingSphere: isosurface.boundingSphere, + + ...color, + ...marker, + ...overpaint, + ...transparency, + ...transform, + + ...BaseGeometry.createValues(props, counts), + dDoubleSided: ValueCell.create(props.doubleSided), + dFlatShaded: ValueCell.create(props.flatShaded), + dFlipSided: ValueCell.create(props.flipSided), + dPositionTexture: ValueCell.create(true), + } + } + + function createValuesSimple(isosurface: Isosurface, props: Partial<PD.Values<Params>>, colorValue: Color, sizeValue: number, transform?: TransformData) { + const s = BaseGeometry.createSimple(colorValue, sizeValue, transform) + const p = { ...PD.getDefaultValues(Params), ...props } + return createValues(isosurface, s.transform, s.locationIterator, s.theme, p) + } + + function updateValues(values: IsosurfaceValues, props: PD.Values<Params>) { + if (Color.fromNormalizedArray(values.uHighlightColor.ref.value, 0) !== props.highlightColor) { + ValueCell.update(values.uHighlightColor, Color.toArrayNormalized(props.highlightColor, values.uHighlightColor.ref.value, 0)) + } + if (Color.fromNormalizedArray(values.uSelectColor.ref.value, 0) !== props.selectColor) { + ValueCell.update(values.uSelectColor, Color.toArrayNormalized(props.selectColor, values.uSelectColor.ref.value, 0)) + } + ValueCell.updateIfChanged(values.alpha, props.alpha) // `uAlpha` is set in renderable.render + ValueCell.updateIfChanged(values.dUseFog, props.useFog) + + ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided) + ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded) + ValueCell.updateIfChanged(values.dFlipSided, props.flipSided) + } + + function updateBoundingSphere(values: IsosurfaceValues, isosurface: Isosurface) { + const invariantBoundingSphere = isosurface.boundingSphere.ref.value + const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value) + if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) { + ValueCell.update(values.boundingSphere, boundingSphere) + } + if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) { + ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere) + } + } +} \ No newline at end of file diff --git a/src/mol-gl/render-object.ts b/src/mol-gl/render-object.ts index d2fc23f412db073b39e79335fbfc88755217ce7a..43959218c714d639613820de9ed17e1a1c1bc219 100644 --- a/src/mol-gl/render-object.ts +++ b/src/mol-gl/render-object.ts @@ -14,6 +14,7 @@ import { PointsValues, PointsRenderable } from './renderable/points'; import { LinesValues, LinesRenderable } from './renderable/lines'; import { SpheresValues, SpheresRenderable } from './renderable/spheres'; import { TextValues, TextRenderable } from './renderable/text'; +import { IsosurfaceValues, IsosurfaceRenderable } from './renderable/isosurface'; const getNextId = idFactory(0, 0x7FFFFFFF) @@ -26,10 +27,11 @@ export interface SpheresRenderObject extends BaseRenderObject { type: 'spheres', export interface TextRenderObject extends BaseRenderObject { type: 'text', values: TextValues } export interface LinesRenderObject extends BaseRenderObject { type: 'lines', values: LinesValues } export interface DirectVolumeRenderObject extends BaseRenderObject { type: 'direct-volume', values: DirectVolumeValues } +export interface IsosurfaceRenderObject extends BaseRenderObject { type: 'isosurface', values: IsosurfaceValues } // -export type GraphicsRenderObject = MeshRenderObject | PointsRenderObject | SpheresRenderObject | TextRenderObject | LinesRenderObject | DirectVolumeRenderObject +export type GraphicsRenderObject = MeshRenderObject | PointsRenderObject | SpheresRenderObject | TextRenderObject | LinesRenderObject | DirectVolumeRenderObject | IsosurfaceRenderObject export type RenderObjectKindType = { 'mesh': MeshRenderObject @@ -38,6 +40,7 @@ export type RenderObjectKindType = { 'text': TextRenderObject 'lines': LinesRenderObject 'direct-volume': DirectVolumeRenderObject + 'isosurface': IsosurfaceRenderObject } export type RenderObjectValuesType = { 'mesh': MeshValues @@ -46,6 +49,7 @@ export type RenderObjectValuesType = { 'text': TextValues 'lines': LinesValues 'direct-volume': DirectVolumeValues + 'isosurface': IsosurfaceValues } export type RenderObjectType = keyof RenderObjectKindType @@ -63,5 +67,6 @@ export function createRenderable(ctx: WebGLContext, o: GraphicsRenderObject): Re 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 'isosurface': return IsosurfaceRenderable(ctx, o.id, o.values, o.state, o.materialId) } } \ No newline at end of file diff --git a/src/mol-gl/renderable/isosurface.ts b/src/mol-gl/renderable/isosurface.ts new file mode 100644 index 0000000000000000000000000000000000000000..5d91db20a51e07eb5de93b38494517362e2f4d90 --- /dev/null +++ b/src/mol-gl/renderable/isosurface.ts @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { Renderable, RenderableState, createRenderable } from '../renderable' +import { WebGLContext } from '../webgl/context'; +import { createGraphicsRenderItem } from '../webgl/render-item'; +import { GlobalUniformSchema, BaseSchema, AttributeSpec, DefineSpec, Values, InternalSchema, InternalValues, UniformSpec, TextureSpec } from './schema'; +import { MeshShaderCode } from '../shader-code'; +import { ValueCell } from 'mol-util'; + +export const IsosurfaceSchema = { + ...BaseSchema, + + aIndex: AttributeSpec('float32', 1, 0), + aNormal: AttributeSpec('float32', 3, 0), + + uPositionTexDim: UniformSpec('v2'), + tPosition: TextureSpec('texture', 'rgba', 'float', 'nearest'), + + dFlatShaded: DefineSpec('boolean'), + dDoubleSided: DefineSpec('boolean'), + dFlipSided: DefineSpec('boolean'), + dPositionTexture: DefineSpec('boolean'), +} +export type IsosurfaceSchema = typeof IsosurfaceSchema +export type IsosurfaceValues = Values<IsosurfaceSchema> + +export function IsosurfaceRenderable(ctx: WebGLContext, id: number, values: IsosurfaceValues, state: RenderableState, materialId: number): Renderable<IsosurfaceValues> { + const schema = { ...GlobalUniformSchema, ...InternalSchema, ...IsosurfaceSchema } + const internalValues: InternalValues = { + uObjectId: ValueCell.create(id), + uPickable: ValueCell.create(state.pickable ? 1 : 0) + } + const shaderCode = MeshShaderCode + const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId) + + return createRenderable(renderItem, values, state) +} \ No newline at end of file diff --git a/src/mol-gl/shader/chunks/assign-position.glsl b/src/mol-gl/shader/chunks/assign-position.glsl index 06231cf7ff806afbd988c00961471fe9cb3b81dd..f29c87b9347ab5e4a2d246232d4341332d798e61 100644 --- a/src/mol-gl/shader/chunks/assign-position.glsl +++ b/src/mol-gl/shader/chunks/assign-position.glsl @@ -1,4 +1,9 @@ mat4 modelView = uView * uModel * aTransform; -vec4 mvPosition = modelView * vec4(aPosition, 1.0); +#ifdef dPositionTexture + vec3 position = readFromTexture(tPosition, aIndex, uPositionTexDim).xyz; +#else + vec3 position = aPosition; +#endif +vec4 mvPosition = modelView * vec4(position, 1.0); vViewPosition = mvPosition.xyz; gl_Position = uProjection * mvPosition; \ No newline at end of file diff --git a/src/mol-gl/shader/mesh.vert b/src/mol-gl/shader/mesh.vert index 99cf417396901d1ec6a3d61b1b5978550a8586d9..acc3c7148db564d3f1282f5e82585363d09d18a4 100644 --- a/src/mol-gl/shader/mesh.vert +++ b/src/mol-gl/shader/mesh.vert @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -10,7 +10,13 @@ precision highp int; #pragma glslify: import('./chunks/common-vert-params.glsl') #pragma glslify: import('./chunks/color-vert-params.glsl') -attribute vec3 aPosition; +#ifdef dPositionTexture + attribute float aIndex; + uniform vec2 uPositionTexDim; + uniform sampler2D tPosition; +#else + attribute vec3 aPosition; +#endif attribute mat4 aTransform; attribute float aInstance; attribute float aGroup;