diff --git a/src/mol-geo/geometry/geometry.ts b/src/mol-geo/geometry/geometry.ts index d8fb32a391aa059b9e4bcabf06ae27378b73a3a6..e5a5667b79ecd1cf9369759f3143323635168835 100644 --- a/src/mol-geo/geometry/geometry.ts +++ b/src/mol-geo/geometry/geometry.ts @@ -5,7 +5,7 @@ */ import { Mesh } from './mesh/mesh'; -import { Point } from './point/point'; +import { Points } from './points/points'; import { RenderableState } from 'mol-gl/renderable'; import { ValueCell } from 'mol-util'; import { BaseValues } from 'mol-gl/renderable/schema'; @@ -14,8 +14,9 @@ import { ColorThemeProps } from 'mol-view/theme/color'; import { LocationIterator } from '../util/location-iterator'; import { ColorType } from './color-data'; import { SizeType } from './size-data'; +import { Lines } from './lines/lines'; -export type GeometryKindType = { 'mesh': Mesh, 'point': Point } +export type GeometryKindType = { 'mesh': Mesh, 'points': Points, 'lines': Lines } export type GeometryKind = keyof GeometryKindType export type Geometry = Helpers.ValueOf<GeometryKindType> @@ -23,7 +24,8 @@ export namespace Geometry { export function getDrawCount(geometry: Geometry) { switch (geometry.kind) { case 'mesh': return geometry.triangleCount * 3 - case 'point': return geometry.vertexCount + case 'points': return geometry.pointCount + case 'lines': return geometry.lineCount } } diff --git a/src/mol-geo/geometry/line/line-builder.ts b/src/mol-geo/geometry/line/line-builder.ts deleted file mode 100644 index 0ffdd02fcbce683e436c0030ffe0517135c6ceda..0000000000000000000000000000000000000000 --- a/src/mol-geo/geometry/line/line-builder.ts +++ /dev/null @@ -1 +0,0 @@ -// TODO \ No newline at end of file diff --git a/src/mol-geo/geometry/line/line.ts b/src/mol-geo/geometry/line/line.ts deleted file mode 100644 index 0ffdd02fcbce683e436c0030ffe0517135c6ceda..0000000000000000000000000000000000000000 --- a/src/mol-geo/geometry/line/line.ts +++ /dev/null @@ -1 +0,0 @@ -// TODO \ No newline at end of file diff --git a/src/mol-geo/geometry/lines/lines-builder.ts b/src/mol-geo/geometry/lines/lines-builder.ts new file mode 100644 index 0000000000000000000000000000000000000000..74a5fcadd9ad0ef0183a0ead56e01bedd50d84bd --- /dev/null +++ b/src/mol-geo/geometry/lines/lines-builder.ts @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { ValueCell } from 'mol-util/value-cell' +import { ChunkedArray } from 'mol-data/util'; +import { Lines } from './lines'; + +export interface LinesBuilder { + add(startX: number, startY: number, startZ: number, endX: number, endY: number, endZ: number, group: number): void + getLines(): Lines +} + +export namespace LinesBuilder { + export function create(initialCount = 2048, chunkSize = 1024, lines?: Lines): LinesBuilder { + const mappings = ChunkedArray.create(Float32Array, 2, chunkSize, lines ? lines.mappingBuffer.ref.value : initialCount); + const groups = ChunkedArray.create(Float32Array, 1, chunkSize, lines ? lines.groupBuffer.ref.value : initialCount); + const indices = ChunkedArray.create(Uint32Array, 3, chunkSize * 3, lines ? lines.indexBuffer.ref.value : initialCount * 3); + const starts = ChunkedArray.create(Float32Array, 3, chunkSize, lines ? lines.startBuffer.ref.value : initialCount); + const ends = ChunkedArray.create(Float32Array, 3, chunkSize, lines ? lines.endBuffer.ref.value : initialCount); + + return { + add: (startX: number, startY: number, startZ: number, endX: number, endY: number, endZ: number, group: number) => { + const offset = mappings.elementCount + for (let i = 0; i < 4; ++i) { + ChunkedArray.add3(starts, startX, startY, startZ); + ChunkedArray.add3(ends, endX, endY, endZ); + ChunkedArray.add(groups, group); + } + ChunkedArray.add2(mappings, -1, 1); + ChunkedArray.add2(mappings, -1, -1); + ChunkedArray.add2(mappings, 1, 1); + ChunkedArray.add2(mappings, 1, -1); + // ChunkedArray.add3(indices, offset, offset + 1, offset + 2); + // ChunkedArray.add3(indices, offset + 1, offset + 3, offset + 2); + ChunkedArray.add3(indices, offset + 2, offset + 1, offset); + ChunkedArray.add3(indices, offset + 2, offset + 3, offset + 1); + }, + getLines: () => { + const mb = ChunkedArray.compact(mappings, true) as Float32Array + const ib = ChunkedArray.compact(indices, true) as Uint32Array + const gb = ChunkedArray.compact(groups, true) as Float32Array + const sb = ChunkedArray.compact(starts, true) as Float32Array + const eb = ChunkedArray.compact(ends, true) as Float32Array + console.log(indices.elementCount, mappings.elementCount, groups.elementCount) + return { + kind: 'lines', + lineCount: indices.elementCount / 2, + mappingBuffer: lines ? ValueCell.update(lines.mappingBuffer, mb) : ValueCell.create(mb), + indexBuffer: lines ? ValueCell.update(lines.indexBuffer, ib) : ValueCell.create(ib), + groupBuffer: lines ? ValueCell.update(lines.groupBuffer, gb) : ValueCell.create(gb), + startBuffer: lines ? ValueCell.update(lines.startBuffer, sb) : ValueCell.create(sb), + endBuffer: lines ? ValueCell.update(lines.endBuffer, eb) : ValueCell.create(eb), + } + } + } + } +} \ No newline at end of file diff --git a/src/mol-geo/geometry/lines/lines.ts b/src/mol-geo/geometry/lines/lines.ts new file mode 100644 index 0000000000000000000000000000000000000000..9c050ea223d098cbb0b128dd138efe70d65f15ee --- /dev/null +++ b/src/mol-geo/geometry/lines/lines.ts @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { ValueCell } from 'mol-util' +import { Mat4 } from 'mol-math/linear-algebra' +import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util'; +import { Geometry } from '../geometry'; +import { RuntimeContext } from 'mol-task'; +import { createColors } from '../color-data'; +import { createMarkers } from '../marker-data'; +import { createSizes } from '../size-data'; +import { TransformData } from '../transform-data'; +import { LocationIterator } from '../../util/location-iterator'; +import { SizeThemeProps } from 'mol-view/theme/size'; +import { LinesValues } from 'mol-gl/renderable/lines'; + +/** Wide line */ +export interface Lines { + readonly kind: 'lines', + /** Number of lines */ + lineCount: number, + /** Mapping buffer as array of xy values wrapped in a value cell */ + readonly mappingBuffer: ValueCell<Float32Array>, + /** Index buffer as array of vertex index triplets wrapped in a value cell */ + readonly indexBuffer: ValueCell<Uint32Array>, + /** Group buffer as array of group ids for each vertex wrapped in a value cell */ + readonly groupBuffer: ValueCell<Float32Array>, + /** Line start buffer as array of xyz values wrapped in a value cell */ + readonly startBuffer: ValueCell<Float32Array>, + /** Line end buffer as array of xyz values wrapped in a value cell */ + readonly endBuffer: ValueCell<Float32Array>, +} + +export namespace Lines { + export function createEmpty(lines?: Lines): Lines { + const mb = lines ? lines.mappingBuffer.ref.value : new Float32Array(0) + const ib = lines ? lines.indexBuffer.ref.value : new Uint32Array(0) + const gb = lines ? lines.groupBuffer.ref.value : new Float32Array(0) + const sb = lines ? lines.startBuffer.ref.value : new Float32Array(0) + const eb = lines ? lines.endBuffer.ref.value : new Float32Array(0) + return { + kind: 'lines', + lineCount: 0, + mappingBuffer: lines ? ValueCell.update(lines.mappingBuffer, mb) : ValueCell.create(mb), + indexBuffer: lines ? ValueCell.update(lines.indexBuffer, ib) : ValueCell.create(ib), + groupBuffer: lines ? ValueCell.update(lines.groupBuffer, gb) : ValueCell.create(gb), + startBuffer: lines ? ValueCell.update(lines.startBuffer, sb) : ValueCell.create(sb), + endBuffer: lines ? ValueCell.update(lines.endBuffer, eb) : ValueCell.create(eb), + } + } + + export function transformImmediate(line: Lines, t: Mat4) { + transformRangeImmediate(line, t, 0, line.lineCount) + } + + export function transformRangeImmediate(lines: Lines, t: Mat4, offset: number, count: number) { + const start = lines.startBuffer.ref.value + transformPositionArray(t, start, offset, count * 4) + ValueCell.update(lines.startBuffer, start); + const end = lines.endBuffer.ref.value + transformPositionArray(t, end, offset, count * 4) + ValueCell.update(lines.endBuffer, end); + } + + // + + export const DefaultProps = { + ...Geometry.DefaultProps, + lineSizeAttenuation: false, + sizeTheme: { name: 'uniform', value: 1 } as SizeThemeProps, + } + export type Props = typeof DefaultProps + + export async function createValues(ctx: RuntimeContext, lines: Lines, transform: TransformData, locationIt: LocationIterator, props: Props): Promise<LinesValues> { + const { instanceCount, groupCount } = locationIt + const color = await createColors(ctx, locationIt, props.colorTheme) + const size = await createSizes(ctx, locationIt, props.sizeTheme) + const marker = createMarkers(instanceCount * groupCount) + + const counts = { drawCount: lines.lineCount * 2 * 3, groupCount, instanceCount } + + return { + aMapping: lines.mappingBuffer, + aGroup: lines.groupBuffer, + aStart: lines.startBuffer, + aEnd: lines.endBuffer, + elements: lines.indexBuffer, + ...color, + ...size, + ...marker, + ...transform, + + ...Geometry.createValues(props, counts), + dLineSizeAttenuation: ValueCell.create(props.lineSizeAttenuation), + dDoubleSided: ValueCell.create(true), + dFlipSided: ValueCell.create(false), + } + } + + export function updateValues(values: LinesValues, props: Props) { + Geometry.updateValues(values, props) + ValueCell.updateIfChanged(values.dLineSizeAttenuation, props.lineSizeAttenuation) + } +} \ No newline at end of file diff --git a/src/mol-geo/geometry/point/point-builder.ts b/src/mol-geo/geometry/points/points-builder.ts similarity index 60% rename from src/mol-geo/geometry/point/point-builder.ts rename to src/mol-geo/geometry/points/points-builder.ts index e00a9e06e3da840a267c19100e9ffb412242ccdd..b8964397e468eb2acf1e5a3007a0fba17a597373 100644 --- a/src/mol-geo/geometry/point/point-builder.ts +++ b/src/mol-geo/geometry/points/points-builder.ts @@ -6,31 +6,31 @@ import { ValueCell } from 'mol-util/value-cell' import { ChunkedArray } from 'mol-data/util'; -import { Point } from './point'; +import { Points } from './points'; -export interface PointBuilder { +export interface PointsBuilder { add(x: number, y: number, z: number, group: number): void - getPoint(): Point + getPoints(): Points } -export namespace PointBuilder { - export function create(initialCount = 2048, chunkSize = 1024, point?: Point): PointBuilder { - const vertices = ChunkedArray.create(Float32Array, 3, chunkSize, point ? point.vertexBuffer.ref.value : initialCount); - const groups = ChunkedArray.create(Float32Array, 1, chunkSize, point ? point.groupBuffer.ref.value : initialCount); +export namespace PointsBuilder { + export function create(initialCount = 2048, chunkSize = 1024, points?: Points): PointsBuilder { + const vertices = ChunkedArray.create(Float32Array, 3, chunkSize, points ? points.centerBuffer.ref.value : initialCount); + const groups = ChunkedArray.create(Float32Array, 1, chunkSize, points ? points.groupBuffer.ref.value : initialCount); return { add: (x: number, y: number, z: number, group: number) => { ChunkedArray.add3(vertices, x, y, z); ChunkedArray.add(groups, group); }, - getPoint: () => { + getPoints: () => { const vb = ChunkedArray.compact(vertices, true) as Float32Array const gb = ChunkedArray.compact(groups, true) as Float32Array return { - kind: 'point', - vertexCount: vertices.elementCount, - vertexBuffer: point ? ValueCell.update(point.vertexBuffer, vb) : ValueCell.create(vb), - groupBuffer: point ? ValueCell.update(point.groupBuffer, gb) : ValueCell.create(gb), + kind: 'points', + pointCount: vertices.elementCount, + centerBuffer: points ? ValueCell.update(points.centerBuffer, vb) : ValueCell.create(vb), + groupBuffer: points ? ValueCell.update(points.groupBuffer, gb) : ValueCell.create(gb), } } } diff --git a/src/mol-geo/geometry/point/point.ts b/src/mol-geo/geometry/points/points.ts similarity index 63% rename from src/mol-geo/geometry/point/point.ts rename to src/mol-geo/geometry/points/points.ts index 2ab8080d2b7c40815f477fef93e7ebc15d4ee4a1..e3bad473a4d1632a0fbfdb2e7ffcc2270b7f931f 100644 --- a/src/mol-geo/geometry/point/point.ts +++ b/src/mol-geo/geometry/points/points.ts @@ -8,7 +8,7 @@ import { ValueCell } from 'mol-util' import { Mat4 } from 'mol-math/linear-algebra' import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util'; import { Geometry } from '../geometry'; -import { PointValues } from 'mol-gl/renderable'; +import { PointsValues } from 'mol-gl/renderable'; import { RuntimeContext } from 'mol-task'; import { createColors } from '../color-data'; import { createMarkers } from '../marker-data'; @@ -18,36 +18,36 @@ import { LocationIterator } from '../../util/location-iterator'; import { SizeThemeProps } from 'mol-view/theme/size'; /** Point cloud */ -export interface Point { - readonly kind: 'point', +export interface Points { + readonly kind: 'points', /** Number of vertices in the point cloud */ - vertexCount: number, + pointCount: number, /** Vertex buffer as array of xyz values wrapped in a value cell */ - readonly vertexBuffer: ValueCell<Float32Array>, + readonly centerBuffer: ValueCell<Float32Array>, /** Group buffer as array of group ids for each vertex wrapped in a value cell */ readonly groupBuffer: ValueCell<Float32Array>, } -export namespace Point { - export function createEmpty(point?: Point): Point { - const vb = point ? point.vertexBuffer.ref.value : new Float32Array(0) - const gb = point ? point.groupBuffer.ref.value : new Float32Array(0) +export namespace Points { + export function createEmpty(points?: Points): Points { + const cb = points ? points.centerBuffer.ref.value : new Float32Array(0) + const gb = points ? points.groupBuffer.ref.value : new Float32Array(0) return { - kind: 'point', - vertexCount: 0, - vertexBuffer: point ? ValueCell.update(point.vertexBuffer, vb) : ValueCell.create(vb), - groupBuffer: point ? ValueCell.update(point.groupBuffer, gb) : ValueCell.create(gb), + kind: 'points', + pointCount: 0, + centerBuffer: points ? ValueCell.update(points.centerBuffer, cb) : ValueCell.create(cb), + groupBuffer: points ? ValueCell.update(points.groupBuffer, gb) : ValueCell.create(gb), } } - export function transformImmediate(point: Point, t: Mat4) { - transformRangeImmediate(point, t, 0, point.vertexCount) + export function transformImmediate(points: Points, t: Mat4) { + transformRangeImmediate(points, t, 0, points.pointCount) } - export function transformRangeImmediate(point: Point, t: Mat4, offset: number, count: number) { - const v = point.vertexBuffer.ref.value - transformPositionArray(t, v, offset, count) - ValueCell.update(point.vertexBuffer, v); + export function transformRangeImmediate(points: Points, t: Mat4, offset: number, count: number) { + const c = points.centerBuffer.ref.value + transformPositionArray(t, c, offset, count) + ValueCell.update(points.centerBuffer, c); } // @@ -61,17 +61,17 @@ export namespace Point { } export type Props = typeof DefaultProps - export async function createValues(ctx: RuntimeContext, point: Point, transform: TransformData, locationIt: LocationIterator, props: Props): Promise<PointValues> { + export async function createValues(ctx: RuntimeContext, points: Points, transform: TransformData, locationIt: LocationIterator, props: Props): Promise<PointsValues> { const { instanceCount, groupCount } = locationIt const color = await createColors(ctx, locationIt, props.colorTheme) const size = await createSizes(ctx, locationIt, props.sizeTheme) const marker = createMarkers(instanceCount * groupCount) - const counts = { drawCount: point.vertexCount, groupCount, instanceCount } + const counts = { drawCount: points.pointCount, groupCount, instanceCount } return { - aPosition: point.vertexBuffer, - aGroup: point.groupBuffer, + aPosition: points.centerBuffer, + aGroup: points.groupBuffer, ...color, ...size, ...marker, @@ -84,7 +84,7 @@ export namespace Point { } } - export function updateValues(values: PointValues, props: Props) { + export function updateValues(values: PointsValues, props: Props) { Geometry.updateValues(values, props) ValueCell.updateIfChanged(values.dPointSizeAttenuation, props.pointSizeAttenuation) ValueCell.updateIfChanged(values.dPointFilledCircle, props.pointFilledCircle) diff --git a/src/mol-geo/representation/structure/index.ts b/src/mol-geo/representation/structure/index.ts index a29ab4fdaac7f9505b15f8adf3fbe996f2e3c6fc..e8b1dfc969ce762c2f386cf9151df8bc2692c837 100644 --- a/src/mol-geo/representation/structure/index.ts +++ b/src/mol-geo/representation/structure/index.ts @@ -11,7 +11,8 @@ import { SizeThemeProps } from 'mol-view/theme/size'; import { Representation, RepresentationProps } from '..'; import { Geometry } from '../../geometry/geometry'; import { Mesh } from '../../geometry/mesh/mesh'; -import { Point } from '../../geometry/point/point'; +import { Points } from '../../geometry/points/points'; +import { Lines } from '../../geometry/lines/lines'; export interface StructureRepresentation<P extends RepresentationProps = {}> extends Representation<Structure, P> { } @@ -28,11 +29,17 @@ export const DefaultStructureMeshProps = { } export type StructureMeshProps = typeof DefaultStructureMeshProps -export const DefaultStructurePointProps = { - ...Point.DefaultProps, +export const DefaultStructurePointsProps = { + ...Points.DefaultProps, ...DefaultStructureProps, } -export type StructurePointProps = typeof DefaultStructurePointProps +export type StructurePointsProps = typeof DefaultStructurePointsProps + +export const DefaultStructureLinesProps = { + ...Lines.DefaultProps, + ...DefaultStructureProps, +} +export type StructureLinesProps = typeof DefaultStructureLinesProps export interface VisualUpdateState { updateTransform: boolean diff --git a/src/mol-geo/representation/structure/representation/surface.ts b/src/mol-geo/representation/structure/representation/surface.ts index d9237e575d449c7215b937ae1ec7c8390af0780d..9842fbbf0eae30a40cb257a0c7a1f1a9120b54c3 100644 --- a/src/mol-geo/representation/structure/representation/surface.ts +++ b/src/mol-geo/representation/structure/representation/surface.ts @@ -13,10 +13,12 @@ import { Loci } from 'mol-model/loci'; import { PickingId } from '../../../geometry/picking'; import { Task } from 'mol-task'; import { GaussianDensityPointVisual, DefaultGaussianDensityPointProps } from '../visual/gaussian-density-point'; +import { DefaultGaussianWireframeProps, GaussianWireframeVisual } from '../visual/gaussian-surface-wireframe'; export const DefaultSurfaceProps = { - ...DefaultGaussianSurfaceProps, - ...DefaultGaussianDensityPointProps, + // ...DefaultGaussianSurfaceProps, + // ...DefaultGaussianDensityPointProps, + ...DefaultGaussianWireframeProps, } export type SurfaceProps = typeof DefaultSurfaceProps @@ -26,19 +28,21 @@ export function SurfaceRepresentation(): SurfaceRepresentation { let currentProps: SurfaceProps const gaussianSurfaceRepr = UnitsRepresentation('Gaussian surface', GaussianSurfaceVisual) const gaussianPointRepr = UnitsRepresentation('Gaussian point grid', GaussianDensityPointVisual) + const gaussianWireframeRepr = UnitsRepresentation('Gaussian wireframe', GaussianWireframeVisual) return { label: 'Surface', get renderObjects() { - return [ ...gaussianSurfaceRepr.renderObjects, ...gaussianPointRepr.renderObjects ] + return [ ...gaussianSurfaceRepr.renderObjects, ...gaussianPointRepr.renderObjects, ...gaussianWireframeRepr.renderObjects ] }, get props() { - return { ...gaussianSurfaceRepr.props, ...gaussianPointRepr.props } + return { ...gaussianSurfaceRepr.props, ...gaussianPointRepr.props, ...gaussianWireframeRepr.props } }, createOrUpdate: (props: Partial<SurfaceProps> = {}, structure?: Structure) => { currentProps = Object.assign({}, DefaultSurfaceProps, currentProps, props) return Task.create('Creating SurfaceRepresentation', async ctx => { - await gaussianSurfaceRepr.createOrUpdate(currentProps, structure).runInContext(ctx) + // await gaussianSurfaceRepr.createOrUpdate(currentProps, structure).runInContext(ctx) // await gaussianPointRepr.createOrUpdate(currentProps, structure).runInContext(ctx) + await gaussianWireframeRepr.createOrUpdate(currentProps, structure).runInContext(ctx) }) }, getLoci: (pickingId: PickingId) => { @@ -50,6 +54,7 @@ export function SurfaceRepresentation(): SurfaceRepresentation { destroy() { gaussianSurfaceRepr.destroy() gaussianPointRepr.destroy() + gaussianWireframeRepr.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 069085d36c16b4d0a769dc3b6820da29abf852a5..ae26180d368af28e9455ee65bee99042392dd18a 100644 --- a/src/mol-geo/representation/structure/units-visual.ts +++ b/src/mol-geo/representation/structure/units-visual.ts @@ -4,28 +4,40 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ +// TODO refactor to make DRY + import { Unit, Structure } from 'mol-model/structure'; import { RepresentationProps, Visual } from '../'; -import { DefaultStructureMeshProps, VisualUpdateState, DefaultStructurePointProps } from '.'; +import { DefaultStructureMeshProps, VisualUpdateState, DefaultStructurePointsProps, DefaultStructureLinesProps } 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, PointRenderObject } from 'mol-gl/render-object'; -import { createUnitsMeshRenderObject, createUnitsPointRenderObject, createUnitsTransform } from './visual/util/common'; +import { MeshRenderObject, PointsRenderObject, LinesRenderObject } from 'mol-gl/render-object'; +import { createUnitsMeshRenderObject, createUnitsPointsRenderObject, createUnitsTransform, createUnitsLinesRenderObject } from './visual/util/common'; import { deepEqual, ValueCell, UUID } from 'mol-util'; import { Interval } from 'mol-data/int'; -import { Point } from '../../geometry/point/point'; +import { Points } from '../../geometry/points/points'; import { updateRenderableState } from '../../geometry/geometry'; import { createColors } from '../../geometry/color-data'; import { createSizes } from '../../geometry/size-data'; +import { Lines } from '../../geometry/lines/lines'; export type StructureGroup = { structure: Structure, group: Unit.SymmetryGroup } export interface UnitsVisual<P extends RepresentationProps = {}> extends Visual<StructureGroup, P> { } +function sameGroupConformation(groupA: Unit.SymmetryGroup, groupB: Unit.SymmetryGroup) { + return ( + groupA.units.length === groupB.units.length && + Unit.conformationId(groupA.units[0]) === Unit.conformationId(groupB.units[0]) + ) +} + +// mesh + export const DefaultUnitsMeshProps = { ...DefaultStructureMeshProps, unitKinds: [ Unit.Kind.Atomic, Unit.Kind.Spheres ] as Unit.Kind[] @@ -175,37 +187,185 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu } } -function sameGroupConformation(groupA: Unit.SymmetryGroup, groupB: Unit.SymmetryGroup) { - return ( - groupA.units.length === groupB.units.length && - Unit.conformationId(groupA.units[0]) === Unit.conformationId(groupB.units[0]) - ) +// points + +export const DefaultUnitsPointsProps = { + ...DefaultStructurePointsProps, + unitKinds: [ Unit.Kind.Atomic, Unit.Kind.Spheres ] as Unit.Kind[] +} +export type UnitsPointsProps = typeof DefaultUnitsPointsProps + +export interface UnitsPointVisualBuilder<P extends UnitsPointsProps> { + defaultProps: P + createPoints(ctx: RuntimeContext, unit: Unit, structure: Structure, props: P, points?: Points): Promise<Points> + 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 UnitsPointsVisual<P extends UnitsPointsProps>(builder: UnitsPointVisualBuilder<P>): UnitsVisual<P> { + const { defaultProps, createPoints, createLocationIterator, getLoci, mark, setUpdateState } = builder + const updateState = VisualUpdateState.create() + + let renderObject: PointsRenderObject | undefined + let currentProps: P + let points: Points + 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) + currentProps.colorTheme.structure = currentStructure + currentGroup = group + + const unit = group.units[0] + currentConformationId = Unit.conformationId(unit) + points = currentProps.unitKinds.includes(unit.kind) + ? await createPoints(ctx, unit, currentStructure, currentProps, points) + : Points.createEmpty(points) + + // TODO create empty location iterator when not in unitKinds + locationIt = createLocationIterator(group) + renderObject = await createUnitsPointsRenderObject(ctx, group, points, locationIt, currentProps) + } + + async function update(ctx: RuntimeContext, props: Partial<P> = {}) { + if (!renderObject) return + + const newProps = Object.assign({}, currentProps, props) + newProps.colorTheme.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 (!deepEqual(newProps.sizeTheme, currentProps.sizeTheme)) updateState.updateSize = true + if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) 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) { + points = newProps.unitKinds.includes(unit.kind) + ? await createPoints(ctx, unit, currentStructure, newProps, points) + : Points.createEmpty(points) + ValueCell.update(renderObject.values.drawCount, points.pointCount) + updateState.updateColor = true + } + + if (updateState.updateSize) { + await createSizes(ctx, locationIt, newProps.sizeTheme, renderObject.values) + } + + if (updateState.updateColor) { + await createColors(ctx, locationIt, newProps.colorTheme, renderObject.values) + } + + // TODO why do I need to cast here? + Points.updateValues(renderObject.values, newProps as UnitsPointsProps) + updateRenderableState(renderObject.state, newProps as UnitsPointsProps) + + 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) { + if (!renderObject) return false + const { tMarker } = renderObject.values + const { groupCount, instanceCount } = locationIt + + function apply(interval: Interval) { + const start = Interval.start(interval) + const end = Interval.end(interval) + return applyMarkerAction(tMarker.ref.value.array, start, end, action) + } + + let changed = false + if (isEveryLoci(loci)) { + changed = apply(Interval.ofBounds(0, groupCount * instanceCount)) + } else { + changed = mark(loci, currentGroup, apply) + } + if (changed) { + ValueCell.update(tMarker, tMarker.ref.value) + } + return changed + }, + destroy() { + // TODO + renderObject = undefined + } + } } -// +// lines -export const DefaultUnitsPointProps = { - ...DefaultStructurePointProps, +export const DefaultUnitsLinesProps = { + ...DefaultStructureLinesProps, unitKinds: [ Unit.Kind.Atomic, Unit.Kind.Spheres ] as Unit.Kind[] } -export type UnitsPointProps = typeof DefaultUnitsPointProps +export type UnitsLinesProps = typeof DefaultUnitsLinesProps -export interface UnitsPointVisualBuilder<P extends UnitsPointProps> { +export interface UnitsLinesVisualBuilder<P extends UnitsLinesProps> { defaultProps: P - createPoint(ctx: RuntimeContext, unit: Unit, structure: Structure, props: P, point?: Point): Promise<Point> + createLines(ctx: RuntimeContext, unit: Unit, structure: Structure, props: P, lines?: Lines): Promise<Lines> 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 UnitsPointVisual<P extends UnitsPointProps>(builder: UnitsPointVisualBuilder<P>): UnitsVisual<P> { - const { defaultProps, createPoint, createLocationIterator, getLoci, mark, setUpdateState } = builder +export function UnitsLinesVisual<P extends UnitsLinesProps>(builder: UnitsLinesVisualBuilder<P>): UnitsVisual<P> { + const { defaultProps, createLines, createLocationIterator, getLoci, mark, setUpdateState } = builder const updateState = VisualUpdateState.create() - let renderObject: PointRenderObject | undefined + let renderObject: LinesRenderObject | undefined let currentProps: P - let point: Point + let lines: Lines let currentGroup: Unit.SymmetryGroup let currentStructure: Structure let locationIt: LocationIterator @@ -218,13 +378,13 @@ export function UnitsPointVisual<P extends UnitsPointProps>(builder: UnitsPointV const unit = group.units[0] currentConformationId = Unit.conformationId(unit) - point = currentProps.unitKinds.includes(unit.kind) - ? await createPoint(ctx, unit, currentStructure, currentProps, point) - : Point.createEmpty(point) + lines = currentProps.unitKinds.includes(unit.kind) + ? await createLines(ctx, unit, currentStructure, currentProps, lines) + : Lines.createEmpty(lines) // TODO create empty location iterator when not in unitKinds locationIt = createLocationIterator(group) - renderObject = await createUnitsPointRenderObject(ctx, group, point, locationIt, currentProps) + renderObject = await createUnitsLinesRenderObject(ctx, group, lines, locationIt, currentProps) } async function update(ctx: RuntimeContext, props: Partial<P> = {}) { @@ -261,10 +421,10 @@ export function UnitsPointVisual<P extends UnitsPointProps>(builder: UnitsPointV } if (updateState.createGeometry) { - point = newProps.unitKinds.includes(unit.kind) - ? await createPoint(ctx, unit, currentStructure, newProps, point) - : Point.createEmpty(point) - ValueCell.update(renderObject.values.drawCount, point.vertexCount) + lines = newProps.unitKinds.includes(unit.kind) + ? await createLines(ctx, unit, currentStructure, newProps, lines) + : Lines.createEmpty(lines) + ValueCell.update(renderObject.values.drawCount, lines.lineCount) updateState.updateColor = true } @@ -277,8 +437,8 @@ export function UnitsPointVisual<P extends UnitsPointProps>(builder: UnitsPointV } // TODO why do I need to cast here? - Point.updateValues(renderObject.values, newProps as UnitsPointProps) - updateRenderableState(renderObject.state, newProps as UnitsPointProps) + Lines.updateValues(renderObject.values, newProps as UnitsLinesProps) + updateRenderableState(renderObject.state, newProps as UnitsLinesProps) currentProps = newProps } diff --git a/src/mol-geo/representation/structure/visual/element-point.ts b/src/mol-geo/representation/structure/visual/element-point.ts index f624d11f10d49c0d3ec3b3009b3e24f421882e12..ab6f51d8aa94b00a5de9243ed1c23e5c7447e087 100644 --- a/src/mol-geo/representation/structure/visual/element-point.ts +++ b/src/mol-geo/representation/structure/visual/element-point.ts @@ -10,12 +10,12 @@ import { UnitsVisual, VisualUpdateState } from '..'; import { getElementLoci, StructureElementIterator, markElement } from './util/element'; import { Vec3 } from 'mol-math/linear-algebra'; import { SizeThemeProps } from 'mol-view/theme/size'; -import { UnitsPointVisual, DefaultUnitsPointProps } from '../units-visual'; -import { Point } from '../../../geometry/point/point'; -import { PointBuilder } from '../../../geometry/point/point-builder'; +import { UnitsPointsVisual, DefaultUnitsPointsProps } from '../units-visual'; +import { Points } from '../../../geometry/points/points'; +import { PointsBuilder } from '../../../geometry/points/points-builder'; export const DefaultElementPointProps = { - ...DefaultUnitsPointProps, + ...DefaultUnitsPointsProps, sizeTheme: { name: 'uniform', value: 0.2 } as SizeThemeProps, pointSizeAttenuation: true, @@ -24,10 +24,10 @@ export type ElementPointProps = typeof DefaultElementPointProps // TODO size -export async function createElementPoint(ctx: RuntimeContext, unit: Unit, structure: Structure, props: ElementPointProps, point: Point) { +export async function createElementPoint(ctx: RuntimeContext, unit: Unit, structure: Structure, props: ElementPointProps, points: Points) { const elements = unit.elements const n = elements.length - const builder = PointBuilder.create(n, n / 10, point) + const builder = PointsBuilder.create(n, n / 10, points) const pos = unit.conformation.invariantPosition const p = Vec3.zero() @@ -40,13 +40,13 @@ export async function createElementPoint(ctx: RuntimeContext, unit: Unit, struct await ctx.update({ message: 'Creating points', current: i, max: n }); } } - return builder.getPoint() + return builder.getPoints() } export function ElementPointVisual(): UnitsVisual<ElementPointProps> { - return UnitsPointVisual<ElementPointProps>({ + return UnitsPointsVisual<ElementPointProps>({ defaultProps: DefaultElementPointProps, - createPoint: createElementPoint, + createPoints: createElementPoint, createLocationIterator: StructureElementIterator.fromGroup, getLoci: getElementLoci, mark: markElement, diff --git a/src/mol-geo/representation/structure/visual/gaussian-density-point.ts b/src/mol-geo/representation/structure/visual/gaussian-density-point.ts index b6c3efd2ea2b92c48de6f4d520dcf695f644d9f0..838df00b7ce741689dafd5fd3463fadea6517ed8 100644 --- a/src/mol-geo/representation/structure/visual/gaussian-density-point.ts +++ b/src/mol-geo/representation/structure/visual/gaussian-density-point.ts @@ -10,14 +10,14 @@ import { UnitsVisual, VisualUpdateState } from '..'; import { StructureElementIterator } from './util/element'; import { EmptyLoci } from 'mol-model/loci'; import { Vec3 } from 'mol-math/linear-algebra'; -import { UnitsPointVisual, DefaultUnitsPointProps } from '../units-visual'; +import { UnitsPointsVisual, DefaultUnitsPointsProps } from '../units-visual'; import { computeGaussianDensity, DefaultGaussianDensityProps } from './util/gaussian'; -import { Point } from '../../../geometry/point/point'; -import { PointBuilder } from '../../../geometry/point/point-builder'; +import { Points } from '../../../geometry/points/points'; +import { PointsBuilder } from '../../../geometry/points/points-builder'; import { SizeThemeProps } from 'mol-view/theme/size'; export const DefaultGaussianDensityPointProps = { - ...DefaultUnitsPointProps, + ...DefaultUnitsPointsProps, ...DefaultGaussianDensityProps, sizeTheme: { name: 'uniform', value: 1 } as SizeThemeProps, @@ -25,14 +25,14 @@ export const DefaultGaussianDensityPointProps = { } export type GaussianDensityPointProps = typeof DefaultGaussianDensityPointProps -export async function createGaussianDensityPoint(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianDensityPointProps, point?: Point) { +export async function createGaussianDensityPoint(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianDensityPointProps, points?: Points) { const { transform, field: { space, data } } = await computeGaussianDensity(unit, structure, props).runAsChild(ctx) const { dimensions, get } = space const [ xn, yn, zn ] = dimensions const n = xn * yn * zn * 3 - const builder = PointBuilder.create(n, n / 10, point) + const builder = PointsBuilder.create(n, n / 10, points) const p = Vec3.zero() let i = 0 @@ -52,13 +52,13 @@ export async function createGaussianDensityPoint(ctx: RuntimeContext, unit: Unit } } } - return builder.getPoint() + return builder.getPoints() } export function GaussianDensityPointVisual(): UnitsVisual<GaussianDensityPointProps> { - return UnitsPointVisual<GaussianDensityPointProps>({ + return UnitsPointsVisual<GaussianDensityPointProps>({ defaultProps: DefaultGaussianDensityPointProps, - createPoint: createGaussianDensityPoint, + createPoints: createGaussianDensityPoint, createLocationIterator: StructureElementIterator.fromGroup, getLoci: () => EmptyLoci, mark: () => false, diff --git a/src/mol-geo/representation/structure/visual/gaussian-surface-wireframe.ts b/src/mol-geo/representation/structure/visual/gaussian-surface-wireframe.ts new file mode 100644 index 0000000000000000000000000000000000000000..b1ddba32c48ceaef6ec83745e41e98beca931722 --- /dev/null +++ b/src/mol-geo/representation/structure/visual/gaussian-surface-wireframe.ts @@ -0,0 +1,71 @@ +/** + * 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 { Mesh } from '../../../geometry/mesh/mesh'; +import { UnitsLinesVisual, DefaultUnitsLinesProps } from '../units-visual'; +import { StructureElementIterator, getElementLoci, markElement } from './util/element'; +import { computeMarchingCubes } from '../../../util/marching-cubes/algorithm'; +import { computeGaussianDensity, DefaultGaussianDensityProps, GaussianDensityProps } from './util/gaussian'; +import { Lines } from '../../../geometry/lines/lines'; +import { LinesBuilder } from '../../../geometry/lines/lines-builder'; + +async function createGaussianWireframe(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianDensityProps, lines?: Lines): Promise<Lines> { + const { smoothness } = props + const { transform, field, idField } = await computeGaussianDensity(unit, structure, props).runAsChild(ctx) + + const surface = await computeMarchingCubes({ + isoLevel: Math.exp(-smoothness), + scalarField: field, + idField + }).runAsChild(ctx) + + Mesh.transformImmediate(surface, transform) + + const vb = surface.vertexBuffer.ref.value + const ib = surface.indexBuffer.ref.value + const gb = surface.groupBuffer.ref.value + + const builder = LinesBuilder.create(surface.triangleCount * 3, surface.triangleCount / 10, lines) + + // TODO avoid duplicate lines and move to '../../../geometry/lines/lines' as Lines.fromMesh() + for (let i = 0, il = surface.triangleCount * 3; i < il; i += 3) { + const i0 = ib[i], i1 = ib[i + 1], i2 = ib[i + 2]; + const x0 = vb[i0 * 3], y0 = vb[i0 * 3 + 1], z0 = vb[i0 * 3 + 2]; + const x1 = vb[i1 * 3], y1 = vb[i1 * 3 + 1], z1 = vb[i1 * 3 + 2]; + const x2 = vb[i2 * 3], y2 = vb[i2 * 3 + 1], z2 = vb[i2 * 3 + 2]; + builder.add(x0, y0, z0, x1, y1, z1, gb[i0]) + builder.add(x0, y0, z0, x2, y2, z2, gb[i0]) + builder.add(x1, y1, z1, x2, y2, z2, gb[i1]) + } + + const l = builder.getLines(); + console.log(l) + return l +} + +export const DefaultGaussianWireframeProps = { + ...DefaultUnitsLinesProps, + ...DefaultGaussianDensityProps, +} +export type GaussianWireframeProps = typeof DefaultGaussianWireframeProps + +export function GaussianWireframeVisual(): UnitsVisual<GaussianWireframeProps> { + return UnitsLinesVisual<GaussianWireframeProps>({ + defaultProps: DefaultGaussianWireframeProps, + createLines: createGaussianWireframe, + createLocationIterator: StructureElementIterator.fromGroup, + getLoci: getElementLoci, + mark: markElement, + setUpdateState: (state: VisualUpdateState, newProps: GaussianWireframeProps, currentProps: GaussianWireframeProps) => { + if (newProps.resolutionFactor !== currentProps.resolutionFactor) state.createGeometry = true + if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true + if (newProps.smoothness !== currentProps.smoothness) 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 2c6b6c51096819ea3352edaf0814293782542fbf..a29346e90484201a6874367cf9b70108502ead0f 100644 --- a/src/mol-geo/representation/structure/visual/util/common.ts +++ b/src/mol-geo/representation/structure/visual/util/common.ts @@ -8,13 +8,13 @@ import { Unit, Structure } from 'mol-model/structure'; import { LocationIterator } from '../../../../util/location-iterator'; import { Mesh } from '../../../../geometry/mesh/mesh'; import { StructureProps } from '../..'; -import { createMeshRenderObject, createPointRenderObject } from 'mol-gl/render-object'; +import { createMeshRenderObject, createPointsRenderObject, createLinesRenderObject } from 'mol-gl/render-object'; import { RuntimeContext } from 'mol-task'; -import { PointProps } from 'mol-geo/representation/structure/representation/point'; import { TransformData, createIdentityTransform, createTransform } from '../../../../geometry/transform-data'; -import { Point } from '../../../../geometry/point/point'; +import { Points } from '../../../../geometry/points/points'; import { createRenderableState } from '../../../../geometry/geometry'; import { Mat4 } from 'mol-math/linear-algebra'; +import { Lines } from '../../../../geometry/lines/lines'; export function createUnitsTransform({ units }: Unit.SymmetryGroup, transformData?: TransformData) { const unitCount = units.length @@ -44,13 +44,25 @@ export async function createUnitsMeshRenderObject(ctx: RuntimeContext, group: Un return createMeshRenderObject(values, state) } -// point +// points -type StructurePointProps = PointProps & StructureProps +type StructurePointsProps = Points.Props & StructureProps -export async function createUnitsPointRenderObject(ctx: RuntimeContext, group: Unit.SymmetryGroup, point: Point, locationIt: LocationIterator, props: StructurePointProps) { +export async function createUnitsPointsRenderObject(ctx: RuntimeContext, group: Unit.SymmetryGroup, points: Points, locationIt: LocationIterator, props: StructurePointsProps) { const transform = createUnitsTransform(group) - const values = await Point.createValues(ctx, point, transform, locationIt, props) + const values = await Points.createValues(ctx, points, transform, locationIt, props) const state = createRenderableState(props) - return createPointRenderObject(values, state) + return createPointsRenderObject(values, state) +} + +// lines + +type StructureLinesProps = Lines.Props & StructureProps + +export async function createUnitsLinesRenderObject(ctx: RuntimeContext, group: Unit.SymmetryGroup, lines: Lines, locationIt: LocationIterator, props: StructureLinesProps) { + const transform = createUnitsTransform(group) + const values = await Lines.createValues(ctx, lines, transform, locationIt, props) + console.log('values', values) + const state = createRenderableState(props) + return createLinesRenderObject(values, state) } \ No newline at end of file diff --git a/src/mol-geo/representation/structure/visual/util/gaussian.ts b/src/mol-geo/representation/structure/visual/util/gaussian.ts index ce06f079db8ad584c7b3814cf4c301aab98e6aa4..da5993c35f8e32c646838f9efd4509307a2e9524 100644 --- a/src/mol-geo/representation/structure/visual/util/gaussian.ts +++ b/src/mol-geo/representation/structure/visual/util/gaussian.ts @@ -11,7 +11,7 @@ import { Box3D } from 'mol-math/geometry'; import { SizeTheme } from 'mol-view/theme/size'; export const DefaultGaussianDensityProps = { - resolutionFactor: 6, + resolutionFactor: 4, radiusOffset: 0, smoothness: 1.5, } diff --git a/src/mol-gl/_spec/renderer.spec.ts b/src/mol-gl/_spec/renderer.spec.ts index ae5c9805a5eaf593c53d8bb5ec0fe64d7ea8e594..ab7feffcc3e4fa8df4bd6047f85fed95a005d47b 100644 --- a/src/mol-gl/_spec/renderer.spec.ts +++ b/src/mol-gl/_spec/renderer.spec.ts @@ -15,8 +15,8 @@ import { createValueColor } from 'mol-geo/geometry/color-data'; import { createValueSize } from 'mol-geo/geometry/size-data'; import { createContext } from '../webgl/context'; import { RenderableState } from '../renderable'; -import { createPointRenderObject } from '../render-object'; -import { PointValues } from '../renderable/point'; +import { createPointsRenderObject } from '../render-object'; +import { PointsValues } from '../renderable/points'; import Scene from '../scene'; import { createEmptyMarkers } from 'mol-geo/geometry/marker-data'; import { fillSerial } from 'mol-util/array'; @@ -56,7 +56,7 @@ function createPoints() { const m4 = Mat4.identity() Mat4.toArray(m4, aTransform.ref.value, 0) - const values: PointValues = { + const values: PointsValues = { aPosition, aGroup, aTransform, @@ -82,7 +82,7 @@ function createPoints() { depthMask: true, } - return createPointRenderObject(values, state) + return createPointsRenderObject(values, state) } describe('renderer', () => { diff --git a/src/mol-gl/render-object.ts b/src/mol-gl/render-object.ts index eda1a45c123b6e8adffc4708d547cbb8fdd24ebd..e183c1ca927f2997053301d73b232d4a3cb08f2a 100644 --- a/src/mol-gl/render-object.ts +++ b/src/mol-gl/render-object.ts @@ -4,30 +4,33 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { PointRenderable, MeshRenderable, RenderableState } from './renderable' +import { PointsRenderable, MeshRenderable, RenderableState, MeshValues, PointsValues, LinesValues, LinesRenderable } from './renderable' import { RenderableValues } from './renderable/schema'; import { idFactory } from 'mol-util/id-factory'; import { Context } from './webgl/context'; -import { MeshValues } from './renderable/mesh'; -import { PointValues } from './renderable/point'; const getNextId = idFactory(0, 0x7FFFFFFF) export interface BaseRenderObject { id: number, type: string, values: RenderableValues, state: RenderableState } export interface MeshRenderObject extends BaseRenderObject { type: 'mesh', values: MeshValues } -export interface PointRenderObject extends BaseRenderObject { type: 'point', values: PointValues } -export type RenderObject = MeshRenderObject | PointRenderObject +export interface PointsRenderObject extends BaseRenderObject { type: 'points', values: PointsValues } +export interface LinesRenderObject extends BaseRenderObject { type: 'lines', values: LinesValues } +export type RenderObject = MeshRenderObject | PointsRenderObject | LinesRenderObject export function createMeshRenderObject(values: MeshValues, state: RenderableState): MeshRenderObject { return { id: getNextId(), type: 'mesh', values, state } } -export function createPointRenderObject(values: PointValues, state: RenderableState): PointRenderObject { - return { id: getNextId(), type: 'point', values, state } +export function createPointsRenderObject(values: PointsValues, state: RenderableState): PointsRenderObject { + return { id: getNextId(), type: 'points', values, state } +} +export function createLinesRenderObject(values: LinesValues, state: RenderableState): LinesRenderObject { + return { id: getNextId(), type: 'lines', values, state } } export function createRenderable(ctx: Context, o: RenderObject) { switch (o.type) { case 'mesh': return MeshRenderable(ctx, o.id, o.values, o.state) - case 'point': return PointRenderable(ctx, o.id, o.values, o.state) + case 'points': return PointsRenderable(ctx, o.id, o.values, o.state) + case 'lines': return LinesRenderable(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 ad35465e1ecb499d5072273c50bbfd5b6b1dced6..bc7c47cc693a855675e735d46b49c74c9bead189 100644 --- a/src/mol-gl/renderable.ts +++ b/src/mol-gl/renderable.ts @@ -8,7 +8,9 @@ import { Program } from './webgl/program'; import { RenderableValues, Values, RenderableSchema, BaseValues } from './renderable/schema'; import { RenderVariant, RenderItem } from './webgl/render-item'; import { Sphere3D } from 'mol-math/geometry'; -import { calculateBoundingSphereFromValues } from './renderable/util'; +// import { calculateBoundingSphereFromValues } from './renderable/util'; +// import { Sphere } from 'mol-geo/primitive/sphere'; +import { Vec3 } from 'mol-math/linear-algebra'; export type RenderableState = { visible: boolean @@ -28,27 +30,33 @@ export interface Renderable<T extends RenderableValues & BaseValues> { } export function createRenderable<T extends Values<RenderableSchema> & BaseValues>(renderItem: RenderItem, values: T, state: RenderableState): Renderable<T> { - let boundingSphere: Sphere3D | undefined + // TODO + let boundingSphere: Sphere3D = Sphere3D.create(Vec3.zero(), 50) return { get values () { return values }, get state () { return state }, get boundingSphere () { - if (boundingSphere) return boundingSphere - boundingSphere = calculateBoundingSphereFromValues(values) return boundingSphere + // TODO + // if (boundingSphere) return boundingSphere + // boundingSphere = calculateBoundingSphereFromValues(values) + // return boundingSphere }, get opaque () { return values.uAlpha.ref.value === 1 }, render: (variant: RenderVariant) => renderItem.render(variant), getProgram: (variant: RenderVariant) => renderItem.getProgram(variant), update: () => { - const valueChanges = renderItem.update() - if (valueChanges.attributes) boundingSphere = undefined + renderItem.update() + // TODO + // const valueChanges = renderItem.update() + // if (valueChanges.attributes) boundingSphere = undefined }, dispose: () => renderItem.destroy() } } -export { PointRenderable, PointSchema, PointValues } from './renderable/point' -export { MeshRenderable, MeshSchema, MeshValues } from './renderable/mesh' \ No newline at end of file +export { MeshRenderable, MeshSchema, MeshValues } from './renderable/mesh' +export { PointsRenderable, PointsSchema, PointsValues } from './renderable/points' +export { LinesRenderable, LinesSchema, LinesValues } from './renderable/lines' \ No newline at end of file diff --git a/src/mol-gl/renderable/line.ts b/src/mol-gl/renderable/line.ts deleted file mode 100644 index 54cd72a4b8ba4dcf581d87842432de530588078b..0000000000000000000000000000000000000000 --- a/src/mol-gl/renderable/line.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ diff --git a/src/mol-gl/renderable/lines.ts b/src/mol-gl/renderable/lines.ts new file mode 100644 index 0000000000000000000000000000000000000000..854eb0f4a80622c54b6e3c85cc056814f2dda260 --- /dev/null +++ b/src/mol-gl/renderable/lines.ts @@ -0,0 +1,37 @@ +/** + * 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 { GlobalUniformSchema, BaseSchema, AttributeSpec, DefineSpec, Values, InternalSchema, SizeSchema, ElementsSpec } from './schema'; +import { ValueCell } from 'mol-util'; +import { LinesShaderCode } from '../shader-code'; + +export const LinesSchema = { + ...BaseSchema, + ...SizeSchema, + aMapping: AttributeSpec('float32', 2, 0), + aStart: AttributeSpec('float32', 3, 0), + aEnd: AttributeSpec('float32', 3, 0), + elements: ElementsSpec('uint32'), + dLineSizeAttenuation: DefineSpec('boolean'), + dDoubleSided: DefineSpec('boolean'), + dFlipSided: DefineSpec('boolean'), +} +export type LinesSchema = typeof LinesSchema +export type LinesValues = Values<LinesSchema> + +export function LinesRenderable(ctx: Context, id: number, values: LinesValues, state: RenderableState): Renderable<LinesValues> { + const schema = { ...GlobalUniformSchema, ...InternalSchema, ...LinesSchema } + const internalValues = { + uObjectId: ValueCell.create(id) + } + const shaderCode = LinesShaderCode + const renderItem = createRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }) + + return createRenderable(renderItem, values, state); +} \ No newline at end of file diff --git a/src/mol-gl/renderable/mesh.ts b/src/mol-gl/renderable/mesh.ts index 91d41fa0b02e257c9ac52634b5df2e224b88de8b..5a4d243949ee644f5cd74614ef6dcc84fcab9ed2 100644 --- a/src/mol-gl/renderable/mesh.ts +++ b/src/mol-gl/renderable/mesh.ts @@ -13,6 +13,7 @@ import { ValueCell } from 'mol-util'; export const MeshSchema = { ...BaseSchema, + aPosition: AttributeSpec('float32', 3, 0), aNormal: AttributeSpec('float32', 3, 0), elements: ElementsSpec('uint32'), dFlatShaded: DefineSpec('boolean'), diff --git a/src/mol-gl/renderable/point.ts b/src/mol-gl/renderable/points.ts similarity index 65% rename from src/mol-gl/renderable/point.ts rename to src/mol-gl/renderable/points.ts index 46e9275ce81e455c14aae7d532c8564c2e5d2404..de003f0dda63986c956ec49022e3a52809eafd46 100644 --- a/src/mol-gl/renderable/point.ts +++ b/src/mol-gl/renderable/points.ts @@ -7,30 +7,27 @@ import { Renderable, RenderableState, createRenderable } from '../renderable' import { Context } from '../webgl/context'; import { createRenderItem } from '../webgl/render-item'; -import { GlobalUniformSchema, BaseSchema, AttributeSpec, UniformSpec, DefineSpec, Values, InternalSchema, TextureSpec } from './schema'; -import { PointShaderCode } from '../shader-code'; +import { GlobalUniformSchema, BaseSchema, AttributeSpec, UniformSpec, DefineSpec, Values, InternalSchema, SizeSchema } from './schema'; +import { PointsShaderCode } from '../shader-code'; import { ValueCell } from 'mol-util'; -export const PointSchema = { +export const PointsSchema = { ...BaseSchema, - aSize: AttributeSpec('float32', 1, 0), - uSize: UniformSpec('f'), - uSizeTexDim: UniformSpec('v2'), - tSize: TextureSpec('alpha', 'ubyte'), - dSizeType: DefineSpec('string', ['uniform', 'attribute']), + ...SizeSchema, + aPosition: AttributeSpec('float32', 3, 0), dPointSizeAttenuation: DefineSpec('boolean'), dPointFilledCircle: DefineSpec('boolean'), uPointEdgeBleach: UniformSpec('f'), } -export type PointSchema = typeof PointSchema -export type PointValues = Values<PointSchema> +export type PointsSchema = typeof PointsSchema +export type PointsValues = Values<PointsSchema> -export function PointRenderable(ctx: Context, id: number, values: PointValues, state: RenderableState): Renderable<PointValues> { - const schema = { ...GlobalUniformSchema, ...InternalSchema, ...PointSchema } +export function PointsRenderable(ctx: Context, id: number, values: PointsValues, state: RenderableState): Renderable<PointsValues> { + const schema = { ...GlobalUniformSchema, ...InternalSchema, ...PointsSchema } const internalValues = { uObjectId: ValueCell.create(id) } - const shaderCode = PointShaderCode + const shaderCode = PointsShaderCode const renderItem = createRenderItem(ctx, 'points', shaderCode, schema, { ...values, ...internalValues }) const renderable = createRenderable(renderItem, values, state); diff --git a/src/mol-gl/renderable/schema.ts b/src/mol-gl/renderable/schema.ts index 7364e731b384e90cfe0224981c202a60e232f4ee..f7b7ca4dedaedabd356611a24d9407285311e502 100644 --- a/src/mol-gl/renderable/schema.ts +++ b/src/mol-gl/renderable/schema.ts @@ -141,27 +141,43 @@ export const InternalSchema = { uObjectId: UniformSpec('i'), } +export const ColorSchema = { + aColor: AttributeSpec('float32', 3, 0), + uColor: UniformSpec('v3'), + uColorTexDim: UniformSpec('v2'), + tColor: TextureSpec('rgb', 'ubyte'), + dColorType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'group_instance']), +} +export type ColorSchema = typeof ColorSchema +export type ColorValues = Values<ColorSchema> + +export const SizeSchema = { + aSize: AttributeSpec('float32', 1, 0), + uSize: UniformSpec('f'), + uSizeTexDim: UniformSpec('v2'), + tSize: TextureSpec('alpha', 'ubyte'), + dSizeType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'group_instance']), +} +export type SizeSchema = typeof SizeSchema +export type SizeValues = Values<SizeSchema> + export const BaseSchema = { + ...ColorSchema, + aInstance: AttributeSpec('float32', 1, 1), - aPosition: AttributeSpec('float32', 3, 0), aGroup: AttributeSpec('float32', 1, 0), aTransform: AttributeSpec('float32', 16, 1), - aColor: AttributeSpec('float32', 3, 0), uAlpha: UniformSpec('f'), uInstanceCount: UniformSpec('i'), uGroupCount: UniformSpec('i'), - uColor: UniformSpec('v3'), - uColorTexDim: UniformSpec('v2'), uMarkerTexDim: UniformSpec('v2'), - tColor: TextureSpec('rgb', 'ubyte'), tMarker: TextureSpec('alpha', 'ubyte'), drawCount: ValueSpec('number'), instanceCount: ValueSpec('number'), - dColorType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'group_instance']), dUseFog: DefineSpec('boolean'), } export type BaseSchema = typeof BaseSchema diff --git a/src/mol-gl/shader-code.ts b/src/mol-gl/shader-code.ts index 93f933e6bb7e8ec21921d1a37775da287f0e0696..263dd0b5fd193146c29acac2ea5f7d85860964da 100644 --- a/src/mol-gl/shader-code.ts +++ b/src/mol-gl/shader-code.ts @@ -24,9 +24,14 @@ export function ShaderCode(vert: string, frag: string): ShaderCode { return { id: shaderCodeId(), vert, frag } } -export const PointShaderCode = ShaderCode( - require('mol-gl/shader/point.vert'), - require('mol-gl/shader/point.frag') +export const PointsShaderCode = ShaderCode( + require('mol-gl/shader/points.vert'), + require('mol-gl/shader/points.frag') +) + +export const LinesShaderCode = ShaderCode( + require('mol-gl/shader/lines.vert'), + require('mol-gl/shader/lines.frag') ) export const MeshShaderCode = ShaderCode( diff --git a/src/mol-gl/shader/lines.frag b/src/mol-gl/shader/lines.frag new file mode 100644 index 0000000000000000000000000000000000000000..98caeadbf7fd714c79fb17425b2fd7f44ec24de8 --- /dev/null +++ b/src/mol-gl/shader/lines.frag @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +precision highp float; +precision highp int; + +#pragma glslify: import('./chunks/common-frag-params.glsl') +#pragma glslify: import('./chunks/color-frag-params.glsl') + +void main(){ + #pragma glslify: import('./chunks/assign-material-color.glsl') + + #if defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking) + gl_FragColor = material; + #else + gl_FragColor = material; + + #pragma glslify: import('./chunks/apply-marker-color.glsl') + #pragma glslify: import('./chunks/apply-fog.glsl') + #endif +} \ No newline at end of file diff --git a/src/mol-gl/shader/lines.vert b/src/mol-gl/shader/lines.vert new file mode 100644 index 0000000000000000000000000000000000000000..7e0b02eda3c7bacf8131b2db99bd77ec8d4f71f4 --- /dev/null +++ b/src/mol-gl/shader/lines.vert @@ -0,0 +1,127 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + * + * heavily based on code by WestLangley from https://github.com/WestLangley/three.js/blob/af28b2fb706ac109771ecad0a7447fad90ab3210/examples/js/lines/LineMaterial.js + */ + +precision highp float; +precision highp int; + +#pragma glslify: import('./chunks/common-vert-params.glsl') +#pragma glslify: import('./chunks/color-vert-params.glsl') + +uniform float uPixelRatio; +uniform float uViewportHeight; + +#if defined(dSizeType_uniform) + uniform float uSize; +#elif defined(dSizeType_attribute) + attribute float aSize; +#elif defined(dSizeType_instance) || defined(dSizeType_group) || defined(dSizeType_groupInstance) + varying vec4 vSize; + uniform vec2 uSizeTexDim; + uniform sampler2D tSize; +#endif + +attribute vec3 aPosition; +attribute mat4 aTransform; +attribute float aInstance; +attribute float aGroup; + +attribute vec2 aMapping; +attribute vec3 aStart; +attribute vec3 aEnd; + +void trimSegment(const in vec4 start, inout vec4 end) { + // trim end segment so it terminates between the camera plane and the near plane + // conservative estimate of the near plane + float a = uProjection[2][2]; // 3nd entry in 3th column + float b = uProjection[3][2]; // 3nd entry in 4th column + float nearEstimate = -0.5 * b / a; + float alpha = (nearEstimate - start.z) / (end.z - start.z); + end.xyz = mix(start.xyz, end.xyz, alpha); +} + +void main(){ + #pragma glslify: import('./chunks/assign-color-varying.glsl') + + // TODO move to chunk (also in point.vert) + #if defined(dSizeType_uniform) + float size = uSize; + #elif defined(dSizeType_attribute) + float size = aSize; + #elif defined(dSizeType_instance) + float size = readFromTexture(tSize, aInstance, uSizeTexDim).r; + #elif defined(dSizeType_group) + float size = readFromTexture(tSize, aGroup, uSizeTexDim).r; + #elif defined(dSizeType_groupInstance) + float size = readFromTexture(tSize, aInstance * float(uGroupCount) + aGroup, uSizeTexDim).r; + #endif + + float linewidth = 3.0; // size; + + mat4 modelView = uView * uModel * aTransform; + + // camera space + vec4 start = modelView * vec4(aStart, 1.0); + vec4 end = modelView * vec4(aEnd, 1.0); + + // special case for perspective projection, and segments that terminate either in, or behind, the camera plane + // clearly the gpu firmware has a way of addressing this issue when projecting into ndc space + // but we need to perform ndc-space calculations in the shader, so we must address this issue directly + // perhaps there is a more elegant solution -- WestLangley + bool perspective = (uProjection[2][3] == -1.0); // 4th entry in the 3rd column + if (perspective) { + if (start.z < 0.0 && end.z >= 0.0) { + trimSegment(start, end); + } else if (end.z < 0.0 && start.z >= 0.0) { + trimSegment(end, start); + } + } + + // clip space + vec4 clipStart = uProjection * start; + vec4 clipEnd = uProjection * end; + + // ndc space + vec2 ndcStart = clipStart.xy / clipStart.w; + vec2 ndcEnd = clipEnd.xy / clipEnd.w; + + // direction + vec2 dir = ndcEnd - ndcStart; + + // account for clip-space aspect ratio + dir.x *= uPixelRatio; + dir = normalize(dir); + + // perpendicular to dir + vec2 offset = vec2(dir.y, - dir.x); + + // undo aspect ratio adjustment + dir.x /= uPixelRatio; + offset.x /= uPixelRatio; + + // sign flip + if (aMapping.x < 0.0) offset *= -1.0; + + // adjust for linewidth + offset *= linewidth; + + // adjust for clip-space to screen-space conversion + offset /= uViewportHeight; + + // select end + vec4 clip = (aMapping.y < 0.5) ? clipStart : clipEnd; + + // back to clip space + offset *= clip.w; + clip.xy += offset; + gl_Position = clip; + + // gl_Position = uProjection * (modelView * vec4(aEnd.x * 5.0 - 5.0, aMapping.y * 5.0, 2.0, 1.0)); + + // TODO + // vViewPosition = (projectionMatrixInverse * clip).xyz; +} \ No newline at end of file diff --git a/src/mol-gl/shader/point.frag b/src/mol-gl/shader/points.frag similarity index 100% rename from src/mol-gl/shader/point.frag rename to src/mol-gl/shader/points.frag diff --git a/src/mol-gl/shader/point.vert b/src/mol-gl/shader/points.vert similarity index 100% rename from src/mol-gl/shader/point.vert rename to src/mol-gl/shader/points.vert