diff --git a/src/apps/canvas/structure-view.ts b/src/apps/canvas/structure-view.ts index 4224874470d1465db4880d83f58f226d5ee3fe25..d13cae50b250b4e2fb848589bb89f14efbce3030 100644 --- a/src/apps/canvas/structure-view.ts +++ b/src/apps/canvas/structure-view.ts @@ -70,7 +70,7 @@ export async function StructureView(app: App, viewer: Viewer, models: ReadonlyAr const active: { [k: string]: boolean } = { cartoon: true, point: false, - surface: false, + surface: true, ballAndStick: false, carbohydrate: false, spacefill: false, diff --git a/src/mol-geo/geometry/direct-volume/direct-volume.ts b/src/mol-geo/geometry/direct-volume/direct-volume.ts new file mode 100644 index 0000000000000000000000000000000000000000..ddf9be4d65b25094f31202e4f4dbfa5a7829f7ed --- /dev/null +++ b/src/mol-geo/geometry/direct-volume/direct-volume.ts @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { RuntimeContext } from 'mol-task' +import { ValueCell } from 'mol-util' +import { Sphere3D } from 'mol-math/geometry' +import { paramDefaultValues, RangeParam, BooleanParam, SelectParam, TextParam } from 'mol-view/parameter'; +import { DirectVolumeValues } from 'mol-gl/renderable/direct-volume'; +import { TextureImage } 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'; + +export interface DirectVolume { + readonly kind: 'direct-volume', + + readonly gridDimension: ValueCell<Vec3>, + readonly gridTexture: ValueCell<TextureImage>, + readonly gridTextureDim: ValueCell<Vec2>, + readonly bboxSize: ValueCell<Vec3> + readonly bboxMin: ValueCell<Vec3> + readonly bboxMax: ValueCell<Vec3> + readonly transform: ValueCell<Mat4> + + /** Bounding sphere of the volume */ + boundingSphere?: Sphere3D +} + +const VolumeBox = Box() + +const RenderModeOptions = [['isosurface', 'Isosurface'], ['volume', 'Volume']] as [string, string][] + +export namespace DirectVolume { + export function createEmpty(directVolume?: DirectVolume): DirectVolume { + // TODO + return { + + } as DirectVolume + } + + // + + export const Params = { + alpha: RangeParam('Opacity', '', 1, 0, 1, 0.01), + visible: BooleanParam('Visible', '', true), + depthMask: BooleanParam('Depth Mask', '', true), + useFog: BooleanParam('Use Fog', '', false), + isoValue: RangeParam('Iso Value', '', 0.22, 0, 1, 0.01), + renderMode: SelectParam('Render Mode', '', 'volume', RenderModeOptions), + controlPoints: TextParam('Control Points', '', '0.19:0.1, 0.2:0.5, 0.21:0.1, 0.4:0.3'), + } + export const DefaultProps = paramDefaultValues(Params) + export type Props = typeof DefaultProps + + export async function createValues(ctx: RuntimeContext, directVolume: DirectVolume, props: Props): Promise<DirectVolumeValues> { + const { bboxSize, bboxMin, bboxMax, gridDimension, gridTexture, gridTextureDim, transform } = directVolume + + const controlPoints = getControlPointsFromString(props.controlPoints) + const transferTex = createTransferFunctionTexture(controlPoints) + + return { + drawCount: ValueCell.create(VolumeBox.indices.length), + instanceCount: ValueCell.create(1), + + aPosition: ValueCell.create(VolumeBox.vertices as Float32Array), + elements: ValueCell.create(VolumeBox.indices as Uint32Array), + + uAlpha: ValueCell.create(props.alpha), + dUseFog: ValueCell.create(props.useFog), + + uIsoValue: ValueCell.create(props.isoValue), + uBboxMin: bboxMin, + uBboxMax: bboxMax, + uBboxSize: bboxSize, + uTransform: transform, + uGridDim: gridDimension, + uGridTexDim: gridTextureDim, + tGridTex: gridTexture, + dRenderMode: ValueCell.create(props.renderMode), + tTransferTex: transferTex, + } + } + + export function updateValues(values: DirectVolumeValues, props: Props) { + ValueCell.updateIfChanged(values.uIsoValue, props.isoValue) + ValueCell.updateIfChanged(values.uAlpha, props.alpha) + ValueCell.updateIfChanged(values.dUseFog, props.useFog) + ValueCell.updateIfChanged(values.dRenderMode, props.renderMode) + + const controlPoints = getControlPointsFromString(props.controlPoints) + createTransferFunctionTexture(controlPoints, values.tTransferTex) + } +} \ No newline at end of file diff --git a/src/mol-geo/geometry/direct-volume/transfer-function.ts b/src/mol-geo/geometry/direct-volume/transfer-function.ts new file mode 100644 index 0000000000000000000000000000000000000000..9c0d1679c9cebe2d9d24074f964b24ba78c4b8c8 --- /dev/null +++ b/src/mol-geo/geometry/direct-volume/transfer-function.ts @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { TextureImage } from 'mol-gl/renderable/util'; +import { spline } from 'mol-math/interpolate'; +import { ColorScale } from 'mol-util/color'; +import { ColorMatplotlib } from 'mol-util/color/tables'; +import { ValueCell } from 'mol-util'; + +export interface ControlPoint { x: number, alpha: number } + +export function getControlPointsFromString(s: string): ControlPoint[] { + return s.split(/\s*,\s*/).map(p => { + const ps = p.split(/\s*:\s*/) + return { x: parseFloat(ps[0]), alpha: parseFloat(ps[1]) } + }) +} + +export function createTransferFunctionTexture(controlPoints: ControlPoint[], texture?: ValueCell<TextureImage>): ValueCell<TextureImage> { + const cp = [ + { x: 0, alpha: 0 }, + { x: 0, alpha: 0 }, + ...controlPoints, + { x: 1, alpha: 0 }, + { x: 1, alpha: 0 }, + ] + const scale = ColorScale.create({ + domain: [0, 1], + colors: ColorMatplotlib.viridis + }) + + const n = 256 + const array = texture ? texture.ref.value.array : new Uint8Array(n * 4) + + let k = 0 + let x1: number, x2: number + let a0: number, a1: number, a2: number, a3: number + + const il = controlPoints.length + 1 + for (let i = 0; i < il; ++i) { + x1 = cp[i + 1].x + x2 = cp[i + 2].x + + a0 = cp[i].alpha + a1 = cp[i + 1].alpha + a2 = cp[i + 2].alpha + a3 = cp[i + 3].alpha + + const jl = Math.round((x2 - x1) * n) + for (let j = 0; j < jl; ++j) { + const t = j / jl + array[k * 4 + 3] = Math.max(0, spline(a0, a1, a2, a3, t, 0.5) * 255) + scale.colorToArray(k / 255, array, k * 4) + ++k + } + } + + const textureImage = { array, width: 256, height: 1 } + if (texture) { + ValueCell.update(texture, textureImage) + return texture + } else { + return ValueCell.create(textureImage) + } +} \ No newline at end of file diff --git a/src/mol-geo/representation/structure/index.ts b/src/mol-geo/representation/structure/index.ts index 471ca0a035202611b39cab33d6235f64a274d7a2..b5c094fc2efb84ec161a72296e1d7015d7076a16 100644 --- a/src/mol-geo/representation/structure/index.ts +++ b/src/mol-geo/representation/structure/index.ts @@ -14,6 +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 { DirectVolume } from '../../geometry/direct-volume/direct-volume'; export interface StructureRepresentation<P extends RepresentationProps = {}> extends Representation<Structure, P> { } @@ -46,6 +47,13 @@ export const StructureLinesParams = { export const DefaultStructureLinesProps = paramDefaultValues(StructureLinesParams) export type StructureLinesProps = typeof DefaultStructureLinesProps +export const StructureDirectVolumeParams = { + ...DirectVolume.Params, + ...StructureParams, +} +export const DefaultStructureDirectVolumeProps = paramDefaultValues(StructureDirectVolumeParams) +export type StructureDirectVolumeProps = typeof DefaultStructureDirectVolumeProps + export interface VisualUpdateState { updateTransform: boolean updateColor: boolean diff --git a/src/mol-geo/representation/structure/representation/molecular-surface.ts b/src/mol-geo/representation/structure/representation/molecular-surface.ts index 9d3d8361aa6ebb85aec92634fc97294b700fd2fe..e9546a1e5439e097bb698bb29ee8d0ab9228efaf 100644 --- a/src/mol-geo/representation/structure/representation/molecular-surface.ts +++ b/src/mol-geo/representation/structure/representation/molecular-surface.ts @@ -14,14 +14,18 @@ import { PickingId } from '../../../geometry/picking'; import { Task } from 'mol-task'; import { GaussianWireframeVisual, GaussianWireframeParams } from '../visual/gaussian-surface-wireframe'; import { getQualityProps } from '../../util'; -import { paramDefaultValues, MultiSelectParam } from 'mol-view/parameter'; +import { paramDefaultValues, MultiSelectParam, SelectParam } from 'mol-view/parameter'; +import { GaussianDensityVolumeParams, GaussianDensityVolumeVisual } from '../visual/gaussian-density-volume'; +import { SizeThemeName, SizeThemeOptions } from 'mol-view/theme/size'; -const VisualOptions = [['surface', 'Surface'], ['wireframe', 'Wireframe']] as [string, string][] +const VisualOptions = [['surface', 'Surface'], ['wireframe', 'Wireframe'], ['volume', 'Volume']] as [string, string][] export const MolecularSurfaceParams = { ...GaussianSurfaceParams, ...GaussianWireframeParams, + ...GaussianDensityVolumeParams, + sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), visuals: MultiSelectParam<string>('Visuals', '', ['surface'], VisualOptions) } export const DefaultMolecularSurfaceProps = paramDefaultValues(MolecularSurfaceParams) @@ -34,6 +38,7 @@ export function MolecularSurfaceRepresentation(): MolecularSurfaceRepresentation let currentStructure: Structure const gaussianSurfaceRepr = UnitsRepresentation('Gaussian surface', GaussianSurfaceVisual) const gaussianWireframeRepr = UnitsRepresentation('Gaussian wireframe', GaussianWireframeVisual) + const gaussianVolumeRepr = UnitsRepresentation('Gaussian volume', GaussianDensityVolumeVisual) return { label: 'Molecular Surface', params: MolecularSurfaceParams, @@ -41,10 +46,11 @@ export function MolecularSurfaceRepresentation(): MolecularSurfaceRepresentation const renderObjects = [] if (currentProps.visuals.includes('surface')) renderObjects.push(...gaussianSurfaceRepr.renderObjects) if (currentProps.visuals.includes('wireframe')) renderObjects.push(...gaussianWireframeRepr.renderObjects) + if (currentProps.visuals.includes('volume')) renderObjects.push(...gaussianVolumeRepr.renderObjects) return renderObjects }, get props() { - return { ...gaussianSurfaceRepr.props, ...gaussianWireframeRepr.props, visuals: currentProps.visuals } + return { ...gaussianSurfaceRepr.props, ...gaussianWireframeRepr.props, ...gaussianVolumeRepr.props, visuals: currentProps.visuals } }, createOrUpdate: (props: Partial<MolecularSurfaceProps> = {}, structure?: Structure) => { if (structure) currentStructure = structure @@ -53,6 +59,7 @@ export function MolecularSurfaceRepresentation(): MolecularSurfaceRepresentation return Task.create('Creating MolecularSurfaceRepresentation', async ctx => { if (currentProps.visuals.includes('surface')) await gaussianSurfaceRepr.createOrUpdate(currentProps, currentStructure).runInContext(ctx) if (currentProps.visuals.includes('wireframe')) await gaussianWireframeRepr.createOrUpdate(currentProps, currentStructure).runInContext(ctx) + if (currentProps.visuals.includes('volume')) await gaussianVolumeRepr.createOrUpdate(currentProps, currentStructure).runInContext(ctx) }) }, getLoci: (pickingId: PickingId) => { @@ -64,6 +71,7 @@ export function MolecularSurfaceRepresentation(): MolecularSurfaceRepresentation destroy() { gaussianSurfaceRepr.destroy() gaussianWireframeRepr.destroy() + gaussianVolumeRepr.destroy() } } } \ No newline at end of file diff --git a/src/mol-geo/representation/structure/units-visual.ts b/src/mol-geo/representation/structure/units-visual.ts index fa2307e111b3ae8ae386b51dee75b8122d6c4821..0763753ed9fbf0e2f393a9664a716e1bf0d7bdbd 100644 --- a/src/mol-geo/representation/structure/units-visual.ts +++ b/src/mol-geo/representation/structure/units-visual.ts @@ -8,15 +8,15 @@ import { Unit, Structure } from 'mol-model/structure'; import { RepresentationProps, Visual } from '../'; -import { VisualUpdateState, StructureMeshParams, StructurePointsParams, StructureLinesParams } from '.'; +import { VisualUpdateState, StructureMeshParams, StructurePointsParams, StructureLinesParams, StructureDirectVolumeParams } from '.'; import { RuntimeContext } from 'mol-task'; import { PickingId } from '../../geometry/picking'; 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 } from 'mol-gl/render-object'; -import { createUnitsMeshRenderObject, createUnitsPointsRenderObject, createUnitsTransform, createUnitsLinesRenderObject } from './visual/util/common'; +import { MeshRenderObject, PointsRenderObject, LinesRenderObject, DirectVolumeRenderObject } 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'; import { Points } from '../../geometry/points/points'; @@ -25,6 +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 { DirectVolume } from '../../geometry/direct-volume/direct-volume'; export const UnitKindInfo = { 'atomic': {}, @@ -526,4 +527,135 @@ export function UnitsLinesVisual<P extends UnitsLinesProps>(builder: UnitsLinesV renderObject = undefined } } +} + +// direct-volume + +export const UnitsDirectVolumeParams = { + ...StructureDirectVolumeParams, + unitKinds: MultiSelectParam<UnitKind>('Unit Kind', '', [ 'atomic', 'spheres' ], UnitKindOptions), +} +export const DefaultUnitsDirectVolumeProps = paramDefaultValues(UnitsDirectVolumeParams) +export type UnitsDirectVolumeProps = typeof DefaultUnitsDirectVolumeProps + +export interface UnitsDirectVolumeVisualBuilder<P extends UnitsDirectVolumeProps> { + defaultProps: P + createDirectVolume(ctx: RuntimeContext, unit: Unit, structure: Structure, props: P, directVolume?: DirectVolume): Promise<DirectVolume> + createLocationIterator(group: Unit.SymmetryGroup): LocationIterator + getLoci(pickingId: PickingId, group: Unit.SymmetryGroup, id: number): Loci + mark(loci: Loci, group: Unit.SymmetryGroup, apply: (interval: Interval) => boolean): boolean + setUpdateState(state: VisualUpdateState, newProps: P, currentProps: P): void +} + +export function UnitsDirectVolumeVisual<P extends UnitsDirectVolumeProps>(builder: UnitsDirectVolumeVisualBuilder<P>): UnitsVisual<P> { + const { defaultProps, createDirectVolume, createLocationIterator, getLoci, setUpdateState } = builder + const updateState = VisualUpdateState.create() + + let renderObject: DirectVolumeRenderObject | undefined + let currentProps: P + let directVolume: DirectVolume + let currentGroup: Unit.SymmetryGroup + let currentStructure: Structure + let locationIt: LocationIterator + let currentConformationId: UUID + + async function create(ctx: RuntimeContext, group: Unit.SymmetryGroup, props: Partial<P> = {}) { + currentProps = Object.assign({}, defaultProps, props, { structure: currentStructure }) + currentGroup = group + + const unit = group.units[0] + currentConformationId = Unit.conformationId(unit) + directVolume = includesUnitKind(currentProps.unitKinds, unit) + ? await createDirectVolume(ctx, unit, currentStructure, currentProps, directVolume) + : DirectVolume.createEmpty(directVolume) + + // TODO create empty location iterator when not in unitKinds + locationIt = createLocationIterator(group) + renderObject = await createUnitsDirectVolumeRenderObject(ctx, group, directVolume, locationIt, currentProps) + } + + async function update(ctx: RuntimeContext, props: Partial<P> = {}) { + if (!renderObject) return + + const newProps = Object.assign({}, currentProps, props, { structure: currentStructure }) + const unit = currentGroup.units[0] + + locationIt.reset() + VisualUpdateState.reset(updateState) + setUpdateState(updateState, newProps, currentProps) + + const newConformationId = Unit.conformationId(unit) + if (newConformationId !== currentConformationId) { + currentConformationId = newConformationId + updateState.createGeometry = true + } + + if (currentGroup.units.length !== locationIt.instanceCount) updateState.updateTransform = true + + if (sizeChanged(currentProps, newProps)) updateState.createGeometry = true + if (colorChanged(currentProps, newProps)) updateState.updateColor = true + if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true + + // + + // if (updateState.updateTransform) { + // locationIt = createLocationIterator(currentGroup) + // const { instanceCount, groupCount } = locationIt + // createUnitsTransform(currentGroup, renderObject.values) + // createMarkers(instanceCount * groupCount, renderObject.values) + // updateState.updateColor = true + // } + + if (updateState.createGeometry) { + directVolume = includesUnitKind(newProps.unitKinds, unit) + ? await createDirectVolume(ctx, unit, currentStructure, newProps, directVolume) + : DirectVolume.createEmpty(directVolume) + updateState.updateColor = true + } + + // if (updateState.updateColor) { + // await createColors(ctx, locationIt, newProps, renderObject.values) + // } + + // TODO why do I need to cast here? + DirectVolume.updateValues(renderObject.values, newProps as UnitsDirectVolumeProps) + updateRenderableState(renderObject.state, newProps as UnitsDirectVolumeProps) + + currentProps = newProps + } + + return { + get renderObject () { return renderObject }, + async createOrUpdate(ctx: RuntimeContext, props: Partial<P> = {}, structureGroup?: StructureGroup) { + if (structureGroup) currentStructure = structureGroup.structure + const group = structureGroup ? structureGroup.group : undefined + if (!group && !currentGroup) { + throw new Error('missing group') + } else if (group && (!currentGroup || !renderObject)) { + // console.log('unit-visual first create') + await create(ctx, group, props) + } else if (group && group.hashCode !== currentGroup.hashCode) { + // console.log('unit-visual group.hashCode !== currentGroup.hashCode') + await create(ctx, group, props) + } else { + // console.log('unit-visual update') + if (group && !sameGroupConformation(group, currentGroup)) { + // console.log('unit-visual new conformation') + currentGroup = group + } + await update(ctx, props) + } + }, + getLoci(pickingId: PickingId) { + return renderObject ? getLoci(pickingId, currentGroup, renderObject.id) : EmptyLoci + }, + mark(loci: Loci, action: MarkerAction) { + // TODO + return false + }, + destroy() { + // TODO + renderObject = undefined + } + } } \ No newline at end of file diff --git a/src/mol-geo/representation/structure/visual/gaussian-density-volume.ts b/src/mol-geo/representation/structure/visual/gaussian-density-volume.ts new file mode 100644 index 0000000000000000000000000000000000000000..ff77b61640e2b0b325b8b4bad9d37e08f63f893f --- /dev/null +++ b/src/mol-geo/representation/structure/visual/gaussian-density-volume.ts @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { Unit, Structure } from 'mol-model/structure'; +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 { paramDefaultValues } from 'mol-view/parameter'; +import { DirectVolume } from '../../../geometry/direct-volume/direct-volume'; +import { ValueCell } from 'mol-util'; +import { Vec3, Vec2 } from 'mol-math/linear-algebra'; + +async function createGaussianDensityVolume(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianDensityProps, directVolume?: DirectVolume): Promise<DirectVolume> { + const p = { ...props, useGpu: true, ignoreCache: true } + const { transform, renderTarget, bbox, gridDimension } = await unit.computeGaussianDensity(p, ctx) + if (!renderTarget || !bbox || !gridDimension) throw new Error('missing renderTarget and/or boundingBox and/or gridDimension') + + 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' as 'direct-volume', + 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), + } + } + + console.log('gridDimension', gridDimension) + console.log('gridTextureDim', renderTarget.width, renderTarget.height) + console.log('boundingBox', bbox) + console.log('transform', transform) + + return directVolume; +} + +export const GaussianDensityVolumeParams = { + ...UnitsDirectVolumeParams, + ...GaussianDensityParams, +} +export const DefaultGaussianDensityVolumeProps = paramDefaultValues(GaussianDensityVolumeParams) +export type GaussianDensityVolumeProps = typeof DefaultGaussianDensityVolumeProps + +export function GaussianDensityVolumeVisual(): UnitsVisual<GaussianDensityVolumeProps> { + return UnitsDirectVolumeVisual<GaussianDensityVolumeProps>({ + defaultProps: DefaultGaussianDensityVolumeProps, + createDirectVolume: createGaussianDensityVolume, + createLocationIterator: StructureElementIterator.fromGroup, + getLoci: getElementLoci, + mark: markElement, + setUpdateState: (state: VisualUpdateState, newProps: GaussianDensityVolumeProps, currentProps: GaussianDensityVolumeProps) => { + if (newProps.resolution !== currentProps.resolution) state.createGeometry = true + if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true + if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true + if (newProps.useGpu !== currentProps.useGpu) state.createGeometry = true + if (newProps.readSlices !== currentProps.readSlices) state.createGeometry = true + if (newProps.ignoreCache !== currentProps.ignoreCache) state.createGeometry = true + } + }) +} \ No newline at end of file diff --git a/src/mol-geo/representation/structure/visual/util/common.ts b/src/mol-geo/representation/structure/visual/util/common.ts index a29346e90484201a6874367cf9b70108502ead0f..da0f07177e3ca5f86b4762d18767d64998861302 100644 --- a/src/mol-geo/representation/structure/visual/util/common.ts +++ b/src/mol-geo/representation/structure/visual/util/common.ts @@ -8,13 +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 } from 'mol-gl/render-object'; +import { createMeshRenderObject, createPointsRenderObject, createLinesRenderObject, createDirectVolumeRenderObject } 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 { DirectVolume } from '../../../../geometry/direct-volume/direct-volume'; export function createUnitsTransform({ units }: Unit.SymmetryGroup, transformData?: TransformData) { const unitCount = units.length @@ -65,4 +66,16 @@ export async function createUnitsLinesRenderObject(ctx: RuntimeContext, group: U console.log('values', values) const state = createRenderableState(props) return createLinesRenderObject(values, state) +} + +// direct-volume + +type StructureDirectVolumeProps = DirectVolume.Props & StructureProps + +export async function createUnitsDirectVolumeRenderObject(ctx: RuntimeContext, group: Unit.SymmetryGroup, directVolume: DirectVolume, locationIt: LocationIterator, props: StructureDirectVolumeProps) { + // TODO transform support + const transform = createUnitsTransform(group) + const values = await DirectVolume.createValues(ctx, directVolume, props) + const state = createRenderableState(props) + return createDirectVolumeRenderObject(values, state) } \ No newline at end of file diff --git a/src/mol-gl/render-object.ts b/src/mol-gl/render-object.ts index 7b4648ace611a6738a6b20eaa543a71beade3126..939f05c81d354fc2bf20eb810c4421c828ae97cb 100644 --- a/src/mol-gl/render-object.ts +++ b/src/mol-gl/render-object.ts @@ -9,6 +9,7 @@ import { RenderableValues } from './renderable/schema'; import { idFactory } from 'mol-util/id-factory'; import { Context } from './webgl/context'; import { GaussianDensityValues, GaussianDensityRenderable } from './renderable/gaussian-density'; +import { DirectVolumeValues, DirectVolumeRenderable } from './renderable/direct-volume'; const getNextId = idFactory(0, 0x7FFFFFFF) @@ -17,7 +18,8 @@ export interface MeshRenderObject extends BaseRenderObject { type: 'mesh', value export interface PointsRenderObject extends BaseRenderObject { type: 'points', values: PointsValues } export interface LinesRenderObject extends BaseRenderObject { type: 'lines', values: LinesValues } export interface GaussianDensityRenderObject extends BaseRenderObject { type: 'gaussian-density', values: GaussianDensityValues } -export type RenderObject = MeshRenderObject | PointsRenderObject | LinesRenderObject | GaussianDensityRenderObject +export interface DirectVolumeRenderObject extends BaseRenderObject { type: 'direct-volume', values: DirectVolumeValues } +export type RenderObject = MeshRenderObject | PointsRenderObject | LinesRenderObject | GaussianDensityRenderObject | DirectVolumeRenderObject export function createMeshRenderObject(values: MeshValues, state: RenderableState): MeshRenderObject { return { id: getNextId(), type: 'mesh', values, state } @@ -31,6 +33,9 @@ export function createLinesRenderObject(values: LinesValues, state: RenderableSt export function createGaussianDensityRenderObject(values: GaussianDensityValues, state: RenderableState): GaussianDensityRenderObject { return { id: getNextId(), type: 'gaussian-density', values, state } } +export function createDirectVolumeRenderObject(values: DirectVolumeValues, state: RenderableState): DirectVolumeRenderObject { + return { id: getNextId(), type: 'direct-volume', values, state } +} export function createRenderable(ctx: Context, o: RenderObject): Renderable<any> { switch (o.type) { @@ -38,5 +43,6 @@ export function createRenderable(ctx: Context, o: RenderObject): Renderable<any> case 'points': return PointsRenderable(ctx, o.id, o.values, o.state) case 'lines': return LinesRenderable(ctx, o.id, o.values, o.state) case 'gaussian-density': return GaussianDensityRenderable(ctx, o.id, o.values, o.state) + case 'direct-volume': return DirectVolumeRenderable(ctx, o.id, o.values, o.state) } } \ No newline at end of file diff --git a/src/mol-gl/renderable/direct-volume.ts b/src/mol-gl/renderable/direct-volume.ts new file mode 100644 index 0000000000000000000000000000000000000000..1827bcc5205b669e40056e20034eda36c2848d2d --- /dev/null +++ b/src/mol-gl/renderable/direct-volume.ts @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2018 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 { Context } from '../webgl/context'; +import { createRenderItem } from '../webgl/render-item'; +import { AttributeSpec, Values, UniformSpec, GlobalUniformSchema, InternalSchema, TextureSpec, ValueSpec, ElementsSpec, DefineSpec } from './schema'; +import { DirectVolumeShaderCode } from '../shader-code'; +import { ValueCell } from 'mol-util'; + +export const DirectVolumeSchema = { + drawCount: ValueSpec('number'), + instanceCount: ValueSpec('number'), + + aPosition: AttributeSpec('float32', 3, 0), + elements: ElementsSpec('uint32'), + + uAlpha: UniformSpec('f'), + dUseFog: DefineSpec('boolean'), + + uIsoValue: UniformSpec('f'), + uBboxMin: UniformSpec('v3'), + uBboxMax: UniformSpec('v3'), + uBboxSize: UniformSpec('v3'), + uTransform: UniformSpec('m4'), + uGridDim: UniformSpec('v3'), + uGridTexDim: UniformSpec('v2'), + tGridTex: TextureSpec('rgba', 'ubyte', 'linear'), + dRenderMode: DefineSpec('string', ['isosurface', 'volume']), + tTransferTex: TextureSpec('rgba', 'ubyte', 'linear'), +} +export type DirectVolumeSchema = typeof DirectVolumeSchema +export type DirectVolumeValues = Values<DirectVolumeSchema> + +export function DirectVolumeRenderable(ctx: Context, id: number, values: DirectVolumeValues, state: RenderableState): Renderable<DirectVolumeValues> { + const schema = { ...GlobalUniformSchema, ...InternalSchema, ...DirectVolumeSchema } + const internalValues = { + uObjectId: ValueCell.create(id) + } + const shaderCode = DirectVolumeShaderCode + const renderItem = createRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }) + const renderable = createRenderable(renderItem, values, state); + + Object.defineProperty(renderable, 'opaque', { get: () => false }); + + return renderable +} \ No newline at end of file diff --git a/src/mol-gl/renderer.ts b/src/mol-gl/renderer.ts index f1eb405564cc76d2575370c24d5650efeecdad40..3e58b1994c8a5889fddda9b2b16bf504890ea596 100644 --- a/src/mol-gl/renderer.ts +++ b/src/mol-gl/renderer.ts @@ -68,9 +68,17 @@ namespace Renderer { } setClearColor(clearColor) + const view = Mat4.clone(camera.view) + const invView = Mat4.invert(Mat4.identity(), view) + const modelView = Mat4.clone(camera.view) + const invModelView = Mat4.invert(Mat4.identity(), modelView) + const globalUniforms: GlobalUniformValues = { uModel: ValueCell.create(Mat4.identity()), - uView: ValueCell.create(Mat4.clone(camera.view)), + uView: ValueCell.create(camera.view), + uInvView: ValueCell.create(invView), + uModelView: ValueCell.create(modelView), + uInvModelView: ValueCell.create(invModelView), uProjection: ValueCell.create(Mat4.clone(camera.projection)), uPixelRatio: ValueCell.create(ctx.pixelRatio), @@ -131,6 +139,9 @@ namespace Renderer { const render = (scene: Scene, variant: RenderVariant) => { ValueCell.update(globalUniforms.uModel, scene.view) ValueCell.update(globalUniforms.uView, camera.view) + ValueCell.update(globalUniforms.uInvView, Mat4.invert(invView, camera.view)) + ValueCell.update(globalUniforms.uModelView, Mat4.mul(modelView, scene.view, camera.view)) + ValueCell.update(globalUniforms.uInvModelView, Mat4.invert(invModelView, modelView)) ValueCell.update(globalUniforms.uProjection, camera.projection) ValueCell.update(globalUniforms.uFogFar, camera.fogFar) diff --git a/src/mol-gl/shader-code.ts b/src/mol-gl/shader-code.ts index ccca161b9c192dad8ecfcca18956fa5deb3e9e61..9499cb7ab508bcd2566aa409bd07551a45e35d21 100644 --- a/src/mol-gl/shader-code.ts +++ b/src/mol-gl/shader-code.ts @@ -44,6 +44,11 @@ export const GaussianDensityShaderCode = ShaderCode( require('mol-gl/shader/gaussian-density.frag') ) +export const DirectVolumeShaderCode = ShaderCode( + require('mol-gl/shader/direct-volume.vert'), + require('mol-gl/shader/direct-volume.frag') +) + export type ShaderDefines = { [k: string]: ValueCell<DefineType> } diff --git a/src/mol-gl/shader/direct-volume.frag b/src/mol-gl/shader/direct-volume.frag new file mode 100644 index 0000000000000000000000000000000000000000..379a27ee369822adeb0bbe9f61d2f5000e894340 --- /dev/null +++ b/src/mol-gl/shader/direct-volume.frag @@ -0,0 +1,270 @@ +/** + * Copyright (c) 2017-2018 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> + */ + +precision highp float; + +varying vec3 unitCoord; +varying vec3 origPos; + +uniform float uAlpha; +uniform mat4 uInvView; +uniform mat4 uModelView; +uniform mat4 uInvModelView; +uniform float uIsoValue; +uniform vec3 uBboxMin; +uniform vec3 uBboxMax; +uniform vec3 uBboxSize; +uniform sampler2D tGridTex; +uniform vec3 uGridDim; +uniform vec2 uGridTexDim; +uniform sampler2D tTransferTex; + +// float uIsoValue = exp(-1.5); +// float uIsoValue = 0.7; + +varying vec4 vNearPos; +varying vec4 vFarPos; +varying vec3 vPosition; + +#pragma glslify: transpose = require(./utils/transpose.glsl) + +vec3 extractCameraPos(const in mat4 modelView) { + // Get the 3 basis vector planes at the camera origin and transform them into model space. + // + // NOTE: Planes have to be transformed by the inverse transpose of a matrix + // Nice reference here: http://www.opengl.org/discussion_boards/showthread.php/159564-Clever-way-to-transform-plane-by-matrix + // + // So for a transform to model space we need to do: + // inverse(transpose(inverse(MV))) + // This equals : transpose(MV) - see Lemma 5 in http://mathrefresher.blogspot.com.au/2007/06/transpose-of-matrix.html + // + // As each plane is simply (1,0,0,0), (0,1,0,0), (0,0,1,0) we can pull the data directly from the transpose matrix. + // + mat4 modelViewT = transpose(modelView); + + // Get plane normals + vec3 n1 = vec3(modelViewT[0]); + vec3 n2 = vec3(modelViewT[1]); + vec3 n3 = vec3(modelViewT[2]); + + // Get plane distances + float d1 = modelViewT[0].w; + float d2 = modelViewT[1].w; + float d3 = modelViewT[2].w; + + // Get the intersection of these 3 planes + // (uisng math from RealTime Collision Detection by Christer Ericson) + vec3 n2n3 = cross(n2, n3); + float denom = dot(n1, n2n3); + + vec3 top = (n2n3 * d1) + cross(n1, (d3 * n2) - (d2 * n3)); + return top / -denom; +} + +// TODO workaround due to some kind of GPU bug +float myMod(float a, float b) { + return a - b * float(int(a) / int(b)); +} +float myDiv(float a, float b) { + return float(int(a) / int(b)); +} + +vec3 palette(in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d) { + return a + b * cos(6.28318 * (c * t + d)); +} + +vec3 palette1(in float t) { + return palette(t, vec3(0.5,0.5,0.5),vec3(0.5,0.5,0.5),vec3(1.0,1.0,1.0),vec3(0.0,0.10,0.20)); +} + +vec4 textureVal(vec3 pos) { + float zSlice0 = floor(pos.z * uGridDim.z); + float column0 = myMod(zSlice0 * uGridDim.x, uGridTexDim.x) / uGridDim.x; + float row0 = floor(myDiv((zSlice0 * uGridDim.x), uGridTexDim.x)); + vec2 coord0 = (vec2(column0 * uGridDim.x, row0 * uGridDim.y) + (pos.xy * uGridDim.xy)) / uGridTexDim; + vec4 color0 = texture2D(tGridTex, coord0); + + float zSlice1 = zSlice0 + 1.0; + float column1 = myMod(zSlice1 * uGridDim.x, uGridTexDim.x) / uGridDim.x; + float row1 = floor(myDiv((zSlice1 * uGridDim.x), uGridTexDim.x)); + vec2 coord1 = (vec2(column1 * uGridDim.x, row1 * uGridDim.y) + (pos.xy * uGridDim.xy)) / uGridTexDim; + vec4 color1 = texture2D(tGridTex, coord1); + + float delta0 = abs((pos.z * uGridDim.z) - zSlice0); + return mix(color0, color1, delta0); +} + +vec4 transferFunction(float value) { + return texture2D(tTransferTex, vec2(value, 0.0)); +} + +// vec4 textureVal(vec3 pos) { +// vec4 color0 = textureVal1(pos); +// vec4 color1 = textureVal1(vec3(pos.xy, pos.z + 1.1 / uGridDim.z)); +// float delta0 = abs((pos.z * uGridDim.z) - floor(pos.z * uGridDim.z)); +// vec3 tmpCol = vec3(0.45, 0.55, 0.8); + +// // coord = pos.xy; +// // coord.x += col1; +// // coord.x /= uGridTexDim.x; +// // coord.y += row1; +// // coord.y /= uGridTexDim.y; +// // vec4 color1 = texture2D(tGridTex, coord); +// // vec4 color1 = texture2D(tGridTex, unitCoordToGridCoord(pos)); + +// return vec4(tmpCol.rgb, mix(color0.a, color1.a, delta0)); +// // return vec4(tmpCol.rgb, color0.a); +// // return vec4(mix(color0, color1, delta0).w, tmpCol.x, tmpCol.y, tmpCol.z); +// // return vec4(color0.x, tmpCol.x, tmpCol.y, tmpCol.z); +// // return vec4(color0.xyz, 1.0); +// // return vec4(color0.xyz, 1.0); +// } + +vec3 scaleVol = vec3(1.0) / uGridDim; +const float gradOffset = 0.5; +vec3 dx = vec3(gradOffset * scaleVol.x, 0.0, 0.0); +vec3 dy = vec3(0.0, gradOffset * scaleVol.y, 0.0); +vec3 dz = vec3(0.0, 0.0, gradOffset * scaleVol.z); + +vec3 color = vec3(0.45, 0.55, 0.8); + +vec4 raymarch(vec3 cameraPos) { + vec3 pos = unitCoord; + float prevValue = -1.0; + float value = 0.0; + float MAX_STEPS_F = max(max(uGridDim.x, uGridDim.y), uGridDim.z); + // int MAX_STEPS = 2 * int(length(vec3(imgresx, imgresy, imgresz))); + // TODO define this from outside (recompile shader per data set) + const int MAX_STEPS = 300; + float stepSize = 1.0 / MAX_STEPS_F; + vec4 src = vec4(0.0); + vec4 dst = vec4(0.0); + + vec3 rayDir = normalize(origPos - cameraPos); + // rayDir = normalize(vec3(1.0, 1.0, 0.0)); + // return vec4(rayDir, 0.5); + vec3 isoPos; + float tmp; + vec3 gradient = vec3(1.0); + vec3 step = rayDir * (1.0 / uGridDim) * 0.5; + + // dst = vec4(textureVal(vec3(pos.xy, 0.6)).xyz, 0.5); + // vec2 foo = (vec2(5.0 * uGridDim.x, 5.0 * uGridDim.y) + (pos.xy * uGridDim.xy)) / uGridTexDim; + // dst = texture2D(tGridTex, foo); + // dst = texture2D(tGridTex, unitCoord.xy); + // dst.xyz = pos; + // return mix(dst, vec4(1.0, 0.0, 0.0, 1.0), 0.5); + + for(int i = 0; i < MAX_STEPS; ++i){ + if( pos.x <= 1.0 && pos.y <= 1.0 && pos.z <= 1.0 && pos.x >= 0.0 && pos.y >= 0.0 && pos.z >= 0.0) { + value = textureVal(pos).a; // current voxel value + } else { + break; + } + + #if defined(dRenderMode_volume) + // src = texture1D(transferRGBASampler, scalarData); + src = transferFunction(value); + // src.rgb = palette1(value); + // src.a = 1.0 - pow(1.0 - src.a, 0.5); + src.rgb *= src.a; + dst = (1.0 - dst.a) * src + dst; // standard blending + #endif + + #if defined(dRenderMode_isosurface) + if(prevValue > 0.0 && ( // there was a prev Value + (prevValue < uIsoValue && value > uIsoValue) || // entering isosurface + (prevValue > uIsoValue && value < uIsoValue) // leaving isosurface + )) { + tmp = ((prevValue - uIsoValue) / ((prevValue - uIsoValue) - (value - uIsoValue))); + isoPos = mix(pos - step, pos, tmp); + + // compute gradient by central differences + gradient.x = textureVal(isoPos - dx).a - textureVal(isoPos + dx).a; + gradient.y = textureVal(isoPos - dy).a - textureVal(isoPos + dy).a; + gradient.z = textureVal(isoPos - dz).a - textureVal(isoPos + dz).a; + gradient = normalize(gradient); + + float d = float(dot(gradient, rayDir) > 0.0); + gradient = (2.0 * d - 1.0) * gradient; + + src.rgb = color.rgb * abs(dot(gradient, normalize(cameraPos))); + src.a = uAlpha; + + // draw interior darker + if( (prevValue - uIsoValue) < 0.0 ) + src.rgb *= 0.5; + + src.rgb *= src.a; + dst = (1.0 - dst.a) * src + dst; // standard blending + if(dst.a >= 1.0) { + break; + } + } + prevValue = value; + #endif + + pos += step; + } + return dst; +} + +void main () { + // vec3 cameraPos = uInvView[3].xyz / uInvView[3].w; + vec3 cameraPos = extractCameraPos(uModelView); + // vec3 cameraPos = vec3(10.0, 0.0, 0.0); + gl_FragColor = raymarch(cameraPos); + if (length(gl_FragColor.rgb) < 0.00001) discard; + #if defined(dRenderMode_volume) + gl_FragColor.a = uAlpha; + #endif + // gl_FragColor = vec4(unitCoord, 1.0); + // gl_FragColor = vec4(1.0, 0.0, 0.0, 0.5); +} + + +// const float relativeStepSize = 1.0; +// vec3 u_size = uGridDim; + +// void main () { +// // Normalize clipping plane info +// vec3 farPos = vFarPos.xyz / vFarPos.w; +// vec3 nearPos = vNearPos.xyz / vNearPos.w; +// // Calculate unit vector pointing in the view direction through this fragment. +// vec3 viewRay = normalize(nearPos.xyz - farPos.xyz); + +// // Compute the (negative) distance to the front surface or near clipping plane. +// // v_position is the back face of the cuboid, so the initial distance calculated in the dot +// // product below is the distance from near clip plane to the back of the cuboid +// float distance = dot(nearPos - vPosition, viewRay); +// distance = max(distance, min((-0.5 - vPosition.x) / viewRay.x, (u_size.x - 0.5 - vPosition.x) / viewRay.x)); +// distance = max(distance, min((-0.5 - vPosition.y) / viewRay.y, (u_size.y - 0.5 - vPosition.y) / viewRay.y)); +// distance = max(distance, min((-0.5 - vPosition.z) / viewRay.z, (u_size.z - 0.5 - vPosition.z) / viewRay.z)); +// // Now we have the starting position on the front surface +// vec3 front = vPosition + viewRay * distance; +// // Decide how many steps to take +// int nsteps = int(-distance / relativeStepSize + 0.5); +// // if (nsteps < 1) +// // discard; +// // Get starting location and step vector in texture coordinates +// vec3 step = ((vPosition - front) / u_size) / float(nsteps); +// vec3 startLoc = front / u_size; +// // For testing: show the number of steps. This helps to establish +// // whether the rays are correctly oriented +// gl_FragColor = vec4(0.0, float(nsteps) / 1.0 / u_size.x, 1.0, 1.0); +// gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); +// return; + +// // if (u_renderstyle == 0) +// // cast_mip(startLoc, step, nsteps, viewRay); +// // else if (u_renderstyle == 1) +// // cast_iso(start_loc, step, nsteps, view_ray); + + +// if (gl_FragColor.a < 0.05) +// discard; +// } \ No newline at end of file diff --git a/src/mol-gl/shader/direct-volume.vert b/src/mol-gl/shader/direct-volume.vert new file mode 100644 index 0000000000000000000000000000000000000000..0ae3cd9f7a420410e03deb4522f2ea1cceea9dc3 --- /dev/null +++ b/src/mol-gl/shader/direct-volume.vert @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2017-2018 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> + */ + +precision highp float; + +attribute vec3 aPosition; + +varying vec3 unitCoord; +varying vec3 origPos; + +uniform vec3 uBboxSize; +uniform vec3 uBboxMin; +uniform vec3 uBboxMax; +uniform mat4 uTransform; + +uniform mat4 uInvView; +uniform mat4 uModelView; +uniform mat4 uInvModelView; +uniform mat4 uProjection, uView, uModel; + +varying vec4 vNearPos; +varying vec4 vFarPos; +varying vec3 vPosition; + +void main() { + unitCoord = aPosition + vec3(0.5); + vec4 mvPosition = uView * uModel * vec4(unitCoord * uBboxSize + uBboxMin, 1.0); + // vec4 mvPosition = vec4(unitCoord * uBboxSize + uBboxMin, 1.0); + // origPos = mvPosition.xyz; + origPos = unitCoord * uBboxSize + uBboxMin; + gl_Position = uProjection * mvPosition; +} + +// void main() { +// // Project local vertex coordinate to camera position. Then do a step +// // backward (in cam coords) to the near clipping plane, and project back. Do +// // the same for the far clipping plane. This gives us all the information we +// // need to calculate the ray and truncate it to the viewing cone. +// vec3 position = aPosition * uBboxSize + uBboxMin; +// vec4 position4 = vec4(position, 1.0); +// vec4 posInCam = uView * position4; +// // Intersection of ray and near clipping plane (z = -1 in clip coords) +// posInCam.z = -posInCam.w; +// vNearPos = uInvView * posInCam; +// // Intersection of ray and far clipping plane (z = +1 in clip coords) +// posInCam.z = posInCam.w; +// vFarPos = uInvView * posInCam; +// // Set varyings and output pos +// vPosition = position; +// gl_Position = uProjection * uModelView * position4; +// } \ No newline at end of file diff --git a/src/mol-math/geometry/common.ts b/src/mol-math/geometry/common.ts index ae32e5438e3ccd75dbbd339ac1e02bf524f3e7f1..ccb246258fcab53b4a4deabb433a9c2728749124 100644 --- a/src/mol-math/geometry/common.ts +++ b/src/mol-math/geometry/common.ts @@ -2,10 +2,13 @@ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> + * @author Alexander Rose <alexander.rose@weirdbyte.de> */ import { OrderedSet } from 'mol-data/int' -import { Mat4, Tensor } from '../linear-algebra'; +import { Mat4, Tensor, Vec3 } from '../linear-algebra'; +import { RenderTarget } from 'mol-gl/webgl/render-target'; +import { Box3D } from '../geometry'; export interface PositionData { x: ArrayLike<number>, @@ -17,4 +20,12 @@ export interface PositionData { radius?: ArrayLike<number> } -export type DensityData = { transform: Mat4, field: Tensor, idField: Tensor } \ No newline at end of file +export type DensityData = { + transform: Mat4, + field: Tensor, + idField: Tensor, + + renderTarget?: RenderTarget, + bbox?: Box3D, + gridDimension?: Vec3 +} \ No newline at end of file diff --git a/src/mol-math/interpolate.ts b/src/mol-math/interpolate.ts index 83d8f7cd27d24db488aaf11984448a0469001c8c..5712434ee8e3f6259a01d83598fa42ae6ddfe9ef 100644 --- a/src/mol-math/interpolate.ts +++ b/src/mol-math/interpolate.ts @@ -29,6 +29,7 @@ export function lerp (start: number, stop: number, alpha: number) { return start + (stop - start) * alpha } +/** Catmul-Rom spline */ export function spline (p0: number, p1: number, p2: number, p3: number, t: number, tension: number) { const v0 = (p2 - p0) * tension const v1 = (p3 - p1) * tension @@ -37,6 +38,11 @@ export function spline (p0: number, p1: number, p2: number, p3: number, t: numbe return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1 } +export function quadraticBezier(p0: number, p1: number, p2: number, t: number) { + const k = 1 - t + return (k * k * p0) + (2 * k * t * p1) + (t * t * p2) +} + export function smoothstep (min: number, max: number, x: number) { x = saturate(normalize(x, min, max)) return x * x * (3 - 2 * x)