diff --git a/src/apps/render-test/state.ts b/src/apps/render-test/state.ts index b5923215a789b9f548b5989fefb24f4b11b6d15c..aeaf9c4f135f4318c753efe8301ea8e29e04f3c8 100644 --- a/src/apps/render-test/state.ts +++ b/src/apps/render-test/state.ts @@ -20,7 +20,7 @@ import { Run } from 'mol-task' import { Symmetry } from 'mol-model/structure' // import mcubes from './utils/mcubes' -import { getStructuresFromPdbId } from './utils' +import { getStructuresFromPdbId, log } from './utils' import { StructureRepresentation } from 'mol-geo/representation/structure'; // import Cylinder from 'mol-geo/primitive/cylinder'; @@ -45,14 +45,14 @@ export default class State { this.loading.next(true) const structures = await getStructuresFromPdbId(pdbId) - const struct = await Run(Symmetry.buildAssembly(structures[0], '1')) + const struct = await Run(Symmetry.buildAssembly(structures[0], '1'), log, 100) const structPointRepr = StructureRepresentation(Point) - await Run(structPointRepr.create(struct)) + await Run(structPointRepr.create(struct), log, 100) structPointRepr.renderObjects.forEach(viewer.add) // const structSpacefillRepr = StructureRepresentation(Spacefill) - // await Run(structSpacefillRepr.create(struct, { detail: 0 })) + // await Run(structSpacefillRepr.create(struct, { detail: 2 }), log, 100) // structSpacefillRepr.renderObjects.forEach(viewer.add) viewer.requestDraw() diff --git a/src/mol-geo/color/data.ts b/src/mol-geo/color/data.ts index 71c820850271283fef7cb96818423a46f3d0cb4d..6e32d8d42d94ea7b1f058003e766526e03e1711e 100644 --- a/src/mol-geo/color/data.ts +++ b/src/mol-geo/color/data.ts @@ -5,15 +5,15 @@ */ import { ValueCell } from 'mol-util'; -import { ColorTexture, createColorTexture } from 'mol-gl/renderable/util'; +import { Texture, createColorTexture } from 'mol-gl/renderable/util'; import Color from './color'; import { Mesh } from '../shape/mesh'; export type UniformColor = { type: 'uniform', value: number[] } export type AttributeColor = { type: 'attribute', value: ValueCell<Float32Array> } -export type InstanceColor = { type: 'instance', value: ValueCell<ColorTexture> } -export type ElementColor = { type: 'element', value: ValueCell<ColorTexture> } -export type ElementInstanceColor = { type: 'element-instance', value: ValueCell<ColorTexture> } +export type InstanceColor = { type: 'instance', value: ValueCell<Texture> } +export type ElementColor = { type: 'element', value: ValueCell<Texture> } +export type ElementInstanceColor = { type: 'element-instance', value: ValueCell<Texture> } export type ColorData = UniformColor | AttributeColor | InstanceColor | ElementColor | ElementInstanceColor export interface UniformColorProps { diff --git a/src/mol-geo/representation/structure/index.ts b/src/mol-geo/representation/structure/index.ts index 092e2f7b7c8ab6b97cf9d6adfbbacaf28b31b3b6..3bceeebe243884496fe5f3197a0d9b456c9f9428 100644 --- a/src/mol-geo/representation/structure/index.ts +++ b/src/mol-geo/representation/structure/index.ts @@ -9,6 +9,7 @@ import { EquivalenceClasses } from 'mol-data/util'; import { OrderedSet } from 'mol-data/int' import { Task } from 'mol-task' import { RenderObject } from 'mol-gl/scene'; +// import { Mat4, EPSILON } from 'mol-math/linear-algebra'; export interface RepresentationProps { @@ -40,13 +41,22 @@ export function StructureRepresentation<Props>(reprCtor: () => UnitsRepresentati (a, b) => a.unit.model.id === b.unit.model.id && OrderedSet.areEqual(a.group.elements, b.group.elements) ); + // const uniqueTransformations = EquivalenceClasses<number, { unit: Unit, group: ElementGroup }>( + // ({ unit, group }) => unit.operator.matrix.join(','), + // (a, b) => Mat4.areEqual(a.unit.operator.matrix, b.unit.operator.matrix, EPSILON.Value) + // ); + const unitIndices = ElementSet.unitIndices(elements); for (let i = 0, _i = unitIndices.length; i < _i; i++) { const unitIndex = unitIndices[i]; const group = ElementSet.groupFromUnitIndex(elements, unitIndex); - uniqueGroups.add(unitIndex, { unit: units[unitIndex], group }); + const unit = units[unitIndex] + uniqueGroups.add(unitIndex, { unit, group }); + // uniqueTransformations.add(unitIndex, { unit, group }); } + // console.log({ uniqueGroups, uniqueTransformations }) + for (let i = 0, _i = uniqueGroups.groups.length; i < _i; i++) { const groupUnits: Unit[] = [] const group = uniqueGroups.groups[i] diff --git a/src/mol-geo/representation/structure/point.ts b/src/mol-geo/representation/structure/point.ts index 64b2ab7029b1fc0aa411f83d0124935fe28e9e6a..7f187d1720a85741123053ea4375eaaad351a227 100644 --- a/src/mol-geo/representation/structure/point.ts +++ b/src/mol-geo/representation/structure/point.ts @@ -17,6 +17,8 @@ import { VdwRadius } from 'mol-model/structure/model/properties/atomic'; import { createUniformColor, createInstanceColor } from '../../color/data'; import { fillSerial } from 'mol-gl/renderable/util'; import { ColorScale } from '../../color/scale'; +import { createUniformSize } from '../../size/data'; +import { vdwSizeData } from '../../size/structure/vdw'; export const DefaultPointProps = { @@ -59,18 +61,32 @@ export default function Point(): UnitsRepresentation<PointProps> { unitCount }) + // const size = createUniformSize({ value: 1 }) + const size = vdwSizeData({ + units, + elementGroup, + offsetData: { + primitiveCount: elementCount, + offsetCount: elementCount + 1, + offsets: fillSerial(new Uint32Array(elementCount + 1)) + } + }) + console.log(size) + const points = createPointRenderObject({ objectId: 0, position: ValueCell.create(ChunkedArray.compact(vertices, true) as Float32Array), id: ValueCell.create(fillSerial(new Float32Array(unitCount))), - size: ValueCell.create(ChunkedArray.compact(sizes, true) as Float32Array), + size, color, transform: ValueCell.create(transformArray), instanceCount: unitCount, elementCount, - positionCount: vertices.elementCount + positionCount: vertices.elementCount, + + usePointSizeAttenuation: true }) renderObjects.push(points) }), diff --git a/src/mol-geo/size/data.ts b/src/mol-geo/size/data.ts new file mode 100644 index 0000000000000000000000000000000000000000..fe8be6a3a7fbc2f88a87a29d2c2a11c6d3cd5765 --- /dev/null +++ b/src/mol-geo/size/data.ts @@ -0,0 +1,48 @@ +/** + * 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'; + +export type UniformSize = { type: 'uniform', value: number } +export type AttributeSize = { type: 'attribute', value: ValueCell<Float32Array> } +export type SizeData = UniformSize | AttributeSize + +export interface OffsetData { + primitiveCount: number, + offsetCount: number, + offsets: Uint32Array +} + +export interface UniformSizeProps { + value: number +} + +/** Creates size uniform */ +export function createUniformSize(props: UniformSizeProps): UniformSize { + return { type: 'uniform', value: props.value } +} + +export interface AttributeSizeProps { + sizeFn: (elementIdx: number) => number + offsetData: OffsetData +} + +/** Creates size attribute with size for each element (i.e. shared across indtances/units) */ +export function createAttributeSize(props: AttributeSizeProps): AttributeSize { + const { sizeFn, offsetData } = props + const { primitiveCount, offsetCount, offsets } = offsetData + const sizes = new Float32Array(primitiveCount); + const _offsets = offsets // .ref.value + for (let i = 0, il = offsetCount - 1; i < il; ++i) { + const start = _offsets[i] + const end = _offsets[i + 1] + const size = sizeFn(i) + for (let i = start, il = end; i < il; ++i) { + sizes[i] = size + } + } + return { type: 'attribute', value: ValueCell.create(sizes) } +} \ No newline at end of file diff --git a/src/mol-geo/size/index.ts b/src/mol-geo/size/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..5936b8ac26f25282d0868ab5127a4e233b845a15 --- /dev/null +++ b/src/mol-geo/size/index.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { SizeData } from './data' + +export { SizeData } \ No newline at end of file diff --git a/src/mol-geo/size/structure/index.ts b/src/mol-geo/size/structure/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..20e1a15617b1084aee91f124377a32cb576c7dc8 --- /dev/null +++ b/src/mol-geo/size/structure/index.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { ElementGroup, Unit } from 'mol-model/structure'; +import { OffsetData } from '../data'; + +export interface StructureSizeDataProps { + units: ReadonlyArray<Unit>, + elementGroup: ElementGroup, + offsetData: OffsetData +} \ No newline at end of file diff --git a/src/mol-geo/size/structure/vdw.ts b/src/mol-geo/size/structure/vdw.ts new file mode 100644 index 0000000000000000000000000000000000000000..dd4e230b93e5a0d772064e6a00808abd118fde58 --- /dev/null +++ b/src/mol-geo/size/structure/vdw.ts @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { OrderedSet } from 'mol-data/int'; +import { VdwRadius } from 'mol-model/structure/model/properties/atomic'; +import { StructureSizeDataProps } from '.'; +import { createAttributeSize } from '../data'; + +export function vdwSizeData(props: StructureSizeDataProps) { + const { units, elementGroup, offsetData } = props + const { type_symbol } = units[0].model.hierarchy.atoms + return createAttributeSize({ + sizeFn: (elementIdx: number) => { + const e = OrderedSet.getAt(elementGroup.elements, elementIdx) + return VdwRadius(type_symbol.value(e)) + }, + offsetData + }) +} \ No newline at end of file diff --git a/src/mol-gl/renderable/point.ts b/src/mol-gl/renderable/point.ts index c18d2d4c56d48cdaabb88f1c54671f8d6fdecc47..7ec9b4f0583ccdfeaf8680f64271bb6104e96830 100644 --- a/src/mol-gl/renderable/point.ts +++ b/src/mol-gl/renderable/point.ts @@ -11,6 +11,7 @@ import { Renderable } from '../renderable' import { createBaseDefines, createBaseUniforms, createBaseAttributes, destroyUniforms, destroyAttributes } from './util' import { PointShaders, addDefines } from '../shaders' import { ColorData } from 'mol-geo/color'; +import { SizeData } from 'mol-geo/size'; type Point = 'point' @@ -19,15 +20,17 @@ namespace Point { objectId: number position: ValueCell<Float32Array> - size?: ValueCell<Float32Array> id: ValueCell<Float32Array> + size: SizeData color: ColorData transform: ValueCell<Float32Array> instanceCount: number elementCount: number - positionCount: number + positionCount: number, + + usePointSizeAttenuation?: boolean } export function create(regl: REGL.Regl, props: Data): Renderable { @@ -35,6 +38,8 @@ namespace Point { const uniforms = createBaseUniforms(regl, props) const attributes = createBaseAttributes(regl, props) + if (props.usePointSizeAttenuation) defines.POINT_SIZE_ATTENUATION = '' + const command = regl({ ...addDefines(defines, PointShaders), uniforms, diff --git a/src/mol-gl/renderable/util.ts b/src/mol-gl/renderable/util.ts index 067c71ec5882a515a8a7edc5a2e4dd12330871dd..e72399fa149c98c488c4c1ea2372f81a2faf673e 100644 --- a/src/mol-gl/renderable/util.ts +++ b/src/mol-gl/renderable/util.ts @@ -11,6 +11,7 @@ import { Attributes, AttributesData, AttributesBuffers } from '../renderable' import Attribute from '../attribute' import { ColorData } from 'mol-geo/color'; import { ShaderDefines } from '../shaders'; +import { SizeData } from 'mol-geo/size'; export type ReglUniforms = { [k: string]: REGL.Uniform | REGL.Texture } export type ReglAttributes = { [k: string]: REGL.AttributeConfig } @@ -23,12 +24,12 @@ export function calculateTextureInfo (n: number, itemSize: number) { return { width, height, length: width * height * itemSize } } -export interface ColorTexture extends Uint8Array { +export interface Texture extends Uint8Array { width: number, height: number } -export function createColorTexture (n: number): ColorTexture { +export function createColorTexture (n: number): Texture { const colorTexInfo = calculateTextureInfo(n, 3) const colorTexture = new Uint8Array(colorTexInfo.length) return Object.assign(colorTexture, { @@ -50,7 +51,7 @@ export function createTransformAttributes (regl: REGL.Regl, transform: ValueCell } } -export function createColorUniforms (regl: REGL.Regl, color: ValueCell<ColorTexture>) { +export function createColorUniforms (regl: REGL.Regl, color: ValueCell<Texture>) { const colorTex = regl.texture({ width: color.ref.value.width, height: color.ref.value.height, @@ -78,6 +79,15 @@ export function getColorDefines(color: ColorData) { return defines } +export function getSizeDefines(size: SizeData) { + const defines: ShaderDefines = {} + switch (size.type) { + case 'uniform': defines.UNIFORM_SIZE = ''; break; + case 'attribute': defines.ATTRIBUTE_SIZE = ''; break; + } + return defines +} + export function getBuffers<T extends AttributesData>(attributes: Attributes<T>): AttributesBuffers<T> { const buffers: AttributesBuffers<any> = {} for (const k of Object.keys(attributes)) { @@ -100,20 +110,24 @@ interface BaseProps { position: ValueCell<Float32Array> normal?: ValueCell<Float32Array> - size?: ValueCell<Float32Array> id: ValueCell<Float32Array> transform: ValueCell<Float32Array> + + size?: SizeData color: ColorData } export function createBaseUniforms(regl: REGL.Regl, props: BaseProps): ReglUniforms { - const { objectId, instanceCount, elementCount, color } = props + const { objectId, instanceCount, elementCount, color, size } = props const uniforms = { objectId, instanceCount, elementCount } if (color.type === 'instance' || color.type === 'element' || color.type === 'element-instance') { Object.assign(uniforms, createColorUniforms(regl, color.value)) } else if (color.type === 'uniform') { Object.assign(uniforms, { color: color.value }) } + if (size && size.type === 'uniform') { + Object.assign(uniforms, { size: size.value }) + } return uniforms } @@ -126,20 +140,23 @@ export function createBaseAttributes(regl: REGL.Regl, props: BaseProps): ReglAtt elementId: Attribute.create(regl, id, positionCount, { size: 1 }), ...createTransformAttributes(regl, transform, instanceCount) }) - if (color.type === 'attribute') { - attributes.color = Attribute.create(regl, color.value, positionCount, { size: 3 }).buffer - } if (normal) { attributes.normal = Attribute.create(regl, normal as any, positionCount, { size: 3 }).buffer } - if (size) { - attributes.size = Attribute.create(regl, size, positionCount, { size: 1 }).buffer + if (color.type === 'attribute') { + attributes.color = Attribute.create(regl, color.value, positionCount, { size: 3 }).buffer + } + if (size && size.type === 'attribute') { + attributes.size = Attribute.create(regl, size.value, positionCount, { size: 1 }).buffer } return attributes } -export function createBaseDefines(regl: REGL.Regl, props: BaseProps) { - return getColorDefines(props.color) +export function createBaseDefines(regl: REGL.Regl, props: BaseProps): ShaderDefines { + return { + ...getColorDefines(props.color), + ...(props.size ? getSizeDefines(props.size) : undefined) + } } export function destroyAttributes(attributes: ReglAttributes) { diff --git a/src/mol-gl/renderer.ts b/src/mol-gl/renderer.ts index 9c26eb35ca3eda5140739ce98b7181f88cd5b39f..874f029d21b83b7109d2ec85f04ffc236aa1c456 100644 --- a/src/mol-gl/renderer.ts +++ b/src/mol-gl/renderer.ts @@ -39,6 +39,10 @@ const optionalExtensions = [ 'EXT_disjoint_timer_query' ] +function getPixelRatio() { + return (typeof window !== 'undefined') ? window.devicePixelRatio : 1 +} + namespace Renderer { export function create(canvas: HTMLCanvasElement, camera: Camera): Renderer { const regl = glContext.create({ canvas, extensions, optionalExtensions, profile: false }) @@ -52,10 +56,14 @@ namespace Renderer { projection: camera.projection, }, uniforms: { + pixelRatio: getPixelRatio(), + viewportHeight: regl.context('viewportHeight'), + model: regl.context('model' as any), transform: regl.context('transform' as any), view: regl.context('view' as any), projection: regl.context('projection' as any), + 'light.position': Vec3.create(0, 0, -100), 'light.color': Vec3.create(1.0, 1.0, 1.0), 'light.ambient': Vec3.create(0.5, 0.5, 0.5), diff --git a/src/mol-gl/shader/chunks/color-vert-params.glsl b/src/mol-gl/shader/chunks/color-vert-params.glsl index 04a62be6dcb780e8460934cfef16d26b416048f1..847240541e6fe5823478726c0319f491644bd7cd 100644 --- a/src/mol-gl/shader/chunks/color-vert-params.glsl +++ b/src/mol-gl/shader/chunks/color-vert-params.glsl @@ -9,4 +9,4 @@ uniform sampler2D colorTex; #endif -#pragma glslify: read_vec3 = require(../utils/read-vec3.glsl) \ No newline at end of file +#pragma glslify: read_vec3 = require(../utils/read-from-texture.glsl) \ No newline at end of file diff --git a/src/mol-gl/shader/point.vert b/src/mol-gl/shader/point.vert index 080e3b15d2ea63a1220b1aa748f6a7f9b36b8e95..7a0efe818941a6ec7bc4310aacf13d8ebfece846 100644 --- a/src/mol-gl/shader/point.vert +++ b/src/mol-gl/shader/point.vert @@ -12,8 +12,17 @@ uniform int objectId; uniform int instanceCount; uniform int elementCount; +uniform float pixelRatio; +uniform float viewportHeight; + #pragma glslify: import('./chunks/color-vert-params.glsl') +#if defined(UNIFORM_SIZE) + uniform float size; +#elif defined(ATTRIBUTE_SIZE) + attribute float size; +#endif + attribute vec3 position; attribute vec4 transformColumn0, transformColumn1, transformColumn2, transformColumn3; attribute float instanceId; @@ -24,7 +33,13 @@ void main(){ mat4 transform = mat4(transformColumn0, transformColumn1, transformColumn2, transformColumn3); mat4 modelView = view * model * transform; + vec4 mvPosition = modelView * vec4(position, 1.0); + + #ifdef POINT_SIZE_ATTENUATION + gl_PointSize = size * pixelRatio * ((viewportHeight / 2.0) / -mvPosition.z) * 5.0; + #else + gl_PointSize = size * pixelRatio; + #endif - gl_PointSize = 1.0; - gl_Position = projection * modelView * vec4(position, 1.0); + gl_Position = projection * mvPosition; } \ No newline at end of file diff --git a/src/mol-gl/shader/utils/read-vec3.glsl b/src/mol-gl/shader/utils/read-from-texture.glsl similarity index 100% rename from src/mol-gl/shader/utils/read-vec3.glsl rename to src/mol-gl/shader/utils/read-from-texture.glsl diff --git a/src/mol-gl/shaders.ts b/src/mol-gl/shaders.ts index 5e3937fe5c48d57262c6307d8238faf9982882f1..07f1cdc31bfe51692593d71b781240a0ec2c48da 100644 --- a/src/mol-gl/shaders.ts +++ b/src/mol-gl/shaders.ts @@ -21,7 +21,9 @@ export const MeshShaders = { } type ShaderDefine = ( - 'UNIFORM_COLOR' | 'ATTRIBUTE_COLOR' | 'INSTANCE_COLOR' | 'ELEMENT_COLOR' | 'ELEMENT_INSTANCE_COLOR' + 'UNIFORM_COLOR' | 'ATTRIBUTE_COLOR' | 'INSTANCE_COLOR' | 'ELEMENT_COLOR' | 'ELEMENT_INSTANCE_COLOR' | + 'UNIFORM_SIZE' | 'ATTRIBUTE_SIZE' | + 'POINT_SIZE_ATTENUATION' ) export type ShaderDefines = { [k in ShaderDefine]?: number|string diff --git a/src/mol-view/viewer.ts b/src/mol-view/viewer.ts index 785cd8fd048ca0c2aa6754f86cfa48b3037d7661..d7e59cd11623e95b6acb35ca9da32f1c88149ac0 100644 --- a/src/mol-view/viewer.ts +++ b/src/mol-view/viewer.ts @@ -86,7 +86,7 @@ namespace Viewer { get stats() { return renderer.stats - } + }, dispose: () => { input.dispose() controls.dispose()