diff --git a/src/apps/render-test/state.ts b/src/apps/render-test/state.ts index ccf415550bdec2850ad6129e824bae02b660aaf2..c786c0ea7f8ba5f8064f97f714cbd5a990145f49 100644 --- a/src/apps/render-test/state.ts +++ b/src/apps/render-test/state.ts @@ -47,12 +47,12 @@ export default class State { const structures = await getStructuresFromPdbId(pdbId) const struct = Symmetry.buildAssembly(structures[0], '1') - const structPointRepr = StructureRepresentation(Point) - await Run(structPointRepr.create(struct)) - structPointRepr.renderObjects.forEach(viewer.add) + // const structPointRepr = StructureRepresentation(Point) + // await Run(structPointRepr.create(struct)) + // structPointRepr.renderObjects.forEach(viewer.add) const structSpacefillRepr = StructureRepresentation(Spacefill) - await Run(structSpacefillRepr.create(struct)) + await Run(structSpacefillRepr.create(struct, { detail: 2 })) structSpacefillRepr.renderObjects.forEach(viewer.add) this.loading.next(false) diff --git a/src/mol-geo/color/index.ts b/src/mol-geo/color/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..33119f31e57e5b75497a93f0f87b16a06a15c554 --- /dev/null +++ b/src/mol-geo/color/index.ts @@ -0,0 +1,13 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +export { ElementColor } from './structure/element'; + +export function hexColorToArray(hexColor: number, array: Helpers.NumberArray, offset: number) { + array[ offset ] = (hexColor >> 16 & 255) / 255 + array[ offset + 1 ] = (hexColor >> 8 & 255) / 255 + array[ offset + 2 ] = (hexColor & 255) / 255 +} \ No newline at end of file diff --git a/src/mol-geo/color/structure/element.ts b/src/mol-geo/color/structure/element.ts new file mode 100644 index 0000000000000000000000000000000000000000..32be722efb086fac16b25b2f3291d278d949bbf5 --- /dev/null +++ b/src/mol-geo/color/structure/element.ts @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { ElementSymbol } from 'mol-model/structure/model/types'; + +// from Jmol http://jmol.sourceforge.net/jscolors/ (or 0xFFFFFF) +export const ElementColors: { [k: string]: number } = { + 'H': 0xFFFFFF, 'HE': 0xD9FFFF, 'LI': 0xCC80FF, 'BE': 0xC2FF00, 'B': 0xFFB5B5, 'C': 0x909090, 'N': 0x3050F8, 'O': 0xFF0D0D, 'F': 0x90E050, 'NE': 0xB3E3F5, 'NA': 0xAB5CF2, 'MG': 0x8AFF00, 'AL': 0xBFA6A6, 'SI': 0xF0C8A0, 'P': 0xFF8000, 'S': 0xFFFF30, 'CL': 0x1FF01F, 'AR': 0x80D1E3, 'K': 0x8F40D4, 'CA': 0x3DFF00, 'SC': 0xE6E6E6, 'TI': 0xBFC2C7, 'V': 0xA6A6AB, 'CR': 0x8A99C7, 'MN': 0x9C7AC7, 'FE': 0xE06633, 'CO': 0xF090A0, 'NI': 0x50D050, 'CU': 0xC88033, 'ZN': 0x7D80B0, 'GA': 0xC28F8F, 'GE': 0x668F8F, 'AS': 0xBD80E3, 'SE': 0xFFA100, 'BR': 0xA62929, 'KR': 0x5CB8D1, 'RB': 0x702EB0, 'SR': 0x00FF00, 'Y': 0x94FFFF, 'ZR': 0x94E0E0, 'NB': 0x73C2C9, 'MO': 0x54B5B5, 'TC': 0x3B9E9E, 'RU': 0x248F8F, 'RH': 0x0A7D8C, 'PD': 0x006985, 'AG': 0xC0C0C0, 'CD': 0xFFD98F, 'IN': 0xA67573, 'SN': 0x668080, 'SB': 0x9E63B5, 'TE': 0xD47A00, 'I': 0x940094, 'XE': 0x940094, 'CS': 0x57178F, 'BA': 0x00C900, 'LA': 0x70D4FF, 'CE': 0xFFFFC7, 'PR': 0xD9FFC7, 'ND': 0xC7FFC7, 'PM': 0xA3FFC7, 'SM': 0x8FFFC7, 'EU': 0x61FFC7, 'GD': 0x45FFC7, 'TB': 0x30FFC7, 'DY': 0x1FFFC7, 'HO': 0x00FF9C, 'ER': 0x00E675, 'TM': 0x00D452, 'YB': 0x00BF38, 'LU': 0x00AB24, 'HF': 0x4DC2FF, 'TA': 0x4DA6FF, 'W': 0x2194D6, 'RE': 0x267DAB, 'OS': 0x266696, 'IR': 0x175487, 'PT': 0xD0D0E0, 'AU': 0xFFD123, 'HG': 0xB8B8D0, 'TL': 0xA6544D, 'PB': 0x575961, 'BI': 0x9E4FB5, 'PO': 0xAB5C00, 'AT': 0x754F45, 'RN': 0x428296, 'FR': 0x420066, 'RA': 0x007D00, 'AC': 0x70ABFA, 'TH': 0x00BAFF, 'PA': 0x00A1FF, 'U': 0x008FFF, 'NP': 0x0080FF, 'PU': 0x006BFF, 'AM': 0x545CF2, 'CM': 0x785CE3, 'BK': 0x8A4FE3, 'CF': 0xA136D4, 'ES': 0xB31FD4, 'FM': 0xB31FBA, 'MD': 0xB30DA6, 'NO': 0xBD0D87, 'LR': 0xC70066, 'RF': 0xCC0059, 'DB': 0xD1004F, 'SG': 0xD90045, 'BH': 0xE00038, 'HS': 0xE6002E, 'MT': 0xEB0026, 'DS': 0xFFFFFF, 'RG': 0xFFFFFF, 'CN': 0xFFFFFF, 'UUT': 0xFFFFFF, 'FL': 0xFFFFFF, 'UUP': 0xFFFFFF, 'LV': 0xFFFFFF, 'UUH': 0xFFFFFF, 'D': 0xFFFFC0, 'T': 0xFFFFA0 +} + +const DefaultElementColor = 0xFFFFFF + +export function ElementColor(element: ElementSymbol): number { + const c = ElementColors[element as any as string]; + return c === void 0 ? DefaultElementColor : c +} \ No newline at end of file diff --git a/src/mol-geo/representation/structure/point.ts b/src/mol-geo/representation/structure/point.ts index 7dbfb5ef282c8ec021fc550f3711b1b602a3d442..2c3143cb79c854591e6bb2d70bafbeed93f01986 100644 --- a/src/mol-geo/representation/structure/point.ts +++ b/src/mol-geo/representation/structure/point.ts @@ -14,6 +14,7 @@ import { ChunkedArray } from 'mol-data/util'; import { Unit, ElementGroup } from 'mol-model/structure'; import { RepresentationProps, UnitsRepresentation } from './index'; import { Task } from 'mol-task' +import { VdwRadius } from 'mol-model/structure/model/properties/atomic'; export const DefaultPointProps = { @@ -23,6 +24,7 @@ export type PointProps = Partial<typeof DefaultPointProps> export default function Point(): UnitsRepresentation<PointProps> { const renderObjects: RenderObject[] = [] const vertices = ChunkedArray.create(Float32Array, 3, 1024, 2048); + const sizes = ChunkedArray.create(Float32Array, 1, 1024, 2048); return { renderObjects, @@ -30,10 +32,12 @@ export default function Point(): UnitsRepresentation<PointProps> { // const l = Element.Location(); const { x, y, z } = units[0].model.conformation + const { type_symbol } = units[0].model.hierarchy.atoms const elementCount = OrderedSet.size(elementGroup.elements) for (let i = 0; i < elementCount; i++) { const e = OrderedSet.getAt(elementGroup.elements, i) ChunkedArray.add3(vertices, x[e], y[e], z[e]) + ChunkedArray.add(sizes, VdwRadius(type_symbol.value(e))) if (i % 10 === 0 && ctx.shouldUpdate) { await ctx.update({ message: 'Point', current: i, max: elementCount }); @@ -51,6 +55,7 @@ export default function Point(): UnitsRepresentation<PointProps> { const points = createRenderObject('point', { position: ValueCell.create(ChunkedArray.compact(vertices, true) as Float32Array), + size: ValueCell.create(ChunkedArray.compact(sizes, true) as Float32Array), color, transform: ValueCell.create(transformArray), diff --git a/src/mol-geo/representation/structure/spacefill.ts b/src/mol-geo/representation/structure/spacefill.ts index 3d4a0c3ab05cc0c4644efaa616be9b4b41ade5ba..2890a64b1162ab9d61ff2ebbf3c0037e00e35b69 100644 --- a/src/mol-geo/representation/structure/spacefill.ts +++ b/src/mol-geo/representation/structure/spacefill.ts @@ -7,7 +7,7 @@ import { ValueCell } from 'mol-util/value-cell' import { createRenderObject, RenderObject } from 'mol-gl/scene' -import { createColorTexture } from 'mol-gl/util'; +// import { createColorTexture } from 'mol-gl/util'; import { Vec3, Mat4 } from 'mol-math/linear-algebra' import { OrderedSet } from 'mol-data/int' import { Unit, ElementGroup } from 'mol-model/structure'; @@ -15,9 +15,10 @@ import { RepresentationProps, UnitsRepresentation } from './index'; import { Task } from 'mol-task' import { MeshBuilder } from '../../shape/mesh-builder'; import { VdwRadius } from 'mol-model/structure/model/properties/atomic'; +import { ElementColor } from '../../color'; export const DefaultSpacefillProps = { - + detail: 0 } export type SpacefillProps = Partial<typeof DefaultSpacefillProps> @@ -27,6 +28,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> { return { renderObjects, create: (units: ReadonlyArray<Unit>, elementGroup: ElementGroup, props: SpacefillProps = {}) => Task.create('Spacefill', async ctx => { + const { detail } = { ...DefaultSpacefillProps, ...props } const meshBuilder = MeshBuilder.create() const v = Vec3.zero() @@ -42,7 +44,11 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> { v[2] = z[e] Mat4.setTranslation(m, v) - meshBuilder.addIcosahedron(m, { radius: VdwRadius(type_symbol.value(e)), detail: 0 }) + meshBuilder.addIcosahedron(m, { + radius: VdwRadius(type_symbol.value(e)), + color: ElementColor(type_symbol.value(e)), + detail + }) if (i % 10 === 0 && ctx.shouldUpdate) { await ctx.update({ message: 'Spacefill', current: i, max: elementCount }); @@ -55,15 +61,16 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> { Mat4.toArray(units[i].operator.matrix, transformArray, i * 16) } - const color = ValueCell.create(createColorTexture(unitsCount)) - color.ref.value.set([ 0, 0, 255 ]) + // const color = ValueCell.create(createColorTexture(unitsCount)) + // color.ref.value.set([ 0, 0, 255 ]) const mesh = meshBuilder.getMesh() + console.log(mesh) const spheres = createRenderObject('mesh', { position: mesh.vertexBuffer, normal: mesh.normalBuffer, - color, + color: { '@type': 'attribute', value: (mesh as any).colorBuffer }, transform: ValueCell.create(transformArray), elements: mesh.indexBuffer, diff --git a/src/mol-geo/shape/mesh-builder.ts b/src/mol-geo/shape/mesh-builder.ts index ea9fc1c8d79d03f4e94ef35a5992e9f1bbe97b7c..68e8291259588723fc2a3977f4358c3a7a20ee05 100644 --- a/src/mol-geo/shape/mesh-builder.ts +++ b/src/mol-geo/shape/mesh-builder.ts @@ -9,15 +9,18 @@ import { Vec3, Mat4 } from 'mol-math/linear-algebra'; import { ChunkedArray } from 'mol-data/util'; import Box, { BoxProps } from '../primitive/box'; +import Cylinder, { CylinderProps } from '../primitive/cylinder'; import Icosahedron, { IcosahedronProps } from '../primitive/icosahedron'; import { Mesh } from './mesh'; +import { hexColorToArray } from '../color'; -type ElementId = { id?: number } +type ElementProps = { id?: number, color?: number } export interface MeshBuilder { add(t: Mat4, _vertices: Float32Array, _normals: Float32Array, _indices?: Uint32Array): number - addBox(t: Mat4, props?: BoxProps & ElementId): number - addIcosahedron(t: Mat4, props?: IcosahedronProps & ElementId): number + addBox(t: Mat4, props?: BoxProps & ElementProps): number + addCylinder(t: Mat4, props?: CylinderProps & ElementProps): number + addIcosahedron(t: Mat4, props?: IcosahedronProps & ElementProps): number getMesh(): Mesh } @@ -29,6 +32,7 @@ export namespace MeshBuilder { export function create(initialCount = 2048, chunkSize = 1024): MeshBuilder { const vertices = ChunkedArray.create(Float32Array, 3, chunkSize, initialCount); const normals = ChunkedArray.create(Float32Array, 3, chunkSize, initialCount); + const colors = ChunkedArray.create(Float32Array, 3, chunkSize, initialCount); const indices = ChunkedArray.create(Uint32Array, 3, chunkSize * 3, initialCount * 3); // const offsets = ChunkedArray.create<number>(n => new Uint32Array(n), 1, 1000); @@ -36,7 +40,7 @@ export namespace MeshBuilder { ChunkedArray.compact(indices, true) - const add = (t: Mat4, _vertices: Float32Array, _normals: Float32Array, _indices: Uint32Array) => { + const add = (t: Mat4, _vertices: Float32Array, _normals: Float32Array, _indices: Uint32Array, _colors?: Float32Array) => { const { elementCount, elementSize } = vertices for (let i = 0, il = _vertices.length; i < il; i += 3) { // position @@ -47,6 +51,11 @@ export namespace MeshBuilder { Vec3.fromArray(tmpV, _normals, i) // Vec3.transformDirection(tmpV, tmpV, n) // TODO ChunkedArray.add3(normals, tmpV[0], tmpV[1], tmpV[2]); + + if (_colors) { + Vec3.fromArray(tmpV, _colors, i) + ChunkedArray.add3(colors, tmpV[0], tmpV[1], tmpV[2]); + } } for (let i = 0, il = _indices.length; i < il; i += 3) { ChunkedArray.add3(indices, _indices[i] + elementCount, _indices[i + 1] + elementCount, _indices[i + 2] + elementCount); @@ -56,23 +65,39 @@ export namespace MeshBuilder { return { add, - addBox: (t: Mat4, props?: BoxProps & ElementId) => { + addBox: (t: Mat4, props?: BoxProps & ElementProps) => { const box = Box(props) return add(t, box.vertices, box.normals, box.indices) }, - addIcosahedron: (t: Mat4, props?: IcosahedronProps & ElementId) => { + addCylinder: (t: Mat4, props?: CylinderProps & ElementProps) => { + const cylinder = Cylinder(props) + return add(t, cylinder.vertices, cylinder.normals, cylinder.indices) + }, + addIcosahedron: (t: Mat4, props?: IcosahedronProps & ElementProps) => { const icosahedron = Icosahedron(props) - return add(t, icosahedron.vertices, icosahedron.normals, icosahedron.indices) + let colors: Float32Array | undefined + if (props && props.color) { + colors = new Float32Array(icosahedron.vertices.length) + for (let i = 0, il = colors.length; i < il; i += 3) { + hexColorToArray(props.color, colors, i) + } + } + return add(t, icosahedron.vertices, icosahedron.normals, icosahedron.indices, colors) }, getMesh: () => { - return { + const mesh = { vertexCount: vertices.elementCount, triangleCount: indices.elementCount, vertexBuffer: ValueCell.create(ChunkedArray.compact(vertices, true) as Float32Array), indexBuffer: ValueCell.create(ChunkedArray.compact(indices, true) as Uint32Array), normalBuffer: ValueCell.create(ChunkedArray.compact(normals, true) as Float32Array), + colorBuffer: ValueCell.create(ChunkedArray.compact(colors, true) as Float32Array), normalsComputed: true, } + if (colors.elementCount) { + mesh.colorBuffer = ValueCell.create(ChunkedArray.compact(colors, true) as Float32Array) + } + return mesh } } } diff --git a/src/mol-gl/renderable/mesh.ts b/src/mol-gl/renderable/mesh.ts index df2dce63fadae68cf6612f1952fe2f4130b4cf36..ffc996b9ff2c7983cc0a4ad2af17e9439aaf32e7 100644 --- a/src/mol-gl/renderable/mesh.ts +++ b/src/mol-gl/renderable/mesh.ts @@ -17,12 +17,17 @@ type Mesh = 'mesh' type Uniforms = { [k: string]: REGL.Uniform | REGL.Texture } +type AttributeColor = { '@type': 'attribute', value: ValueCell<Float32Array> } +type InstanceColor = { '@type': 'instance', value: ValueCell<ColorTexture> } +type ElementColor = { '@type': 'element', value: ValueCell<ColorTexture> } +type Color = AttributeColor | InstanceColor | ElementColor + namespace Mesh { export type Data = { position: ValueCell<Float32Array> normal: ValueCell<Float32Array> + readonly color: Color transform: ValueCell<Float32Array> - color: ValueCell<ColorTexture> elements: ValueCell<Uint32Array> instanceCount: number @@ -30,22 +35,29 @@ namespace Mesh { positionCount: number } - export function create(regl: REGL.Regl, data: Data, uniforms: Uniforms): Renderable { + export function create(regl: REGL.Regl, data: Data, _uniforms: Uniforms): Renderable { const instanceId = ValueCell.create(fillSerial(new Float32Array(data.instanceCount))) + const uniforms = { + objectId: _uniforms.objectId || 0, + instanceCount: data.instanceCount, + ..._uniforms + } + if (data.color['@type'] === 'instance' || data.color['@type'] === 'element') { + Object.assign(uniforms, createColorUniforms(regl, data.color.value as ValueCell<ColorTexture>)) + } + const attributes = getBuffers({ + instanceId: Attribute.create(regl, instanceId, data.instanceCount, { size: 1, divisor: 1 }), + position: Attribute.create(regl, data.position, data.positionCount, { size: 3 }), + normal: Attribute.create(regl, data.normal, data.positionCount, { size: 3 }), + ...createTransformAttributes(regl, data.transform, data.instanceCount) + }) + if (data.color['@type'] === 'attribute') { + attributes.color = Attribute.create(regl, data.color.value as ValueCell<Float32Array>, data.positionCount, { size: 3 }).buffer + } const command = regl({ ...MeshShaders, - uniforms: { - objectId: uniforms.objectId || 0, - instanceCount: data.instanceCount, - ...createColorUniforms(regl, data.color), - ...uniforms - }, - attributes: getBuffers({ - instanceId: Attribute.create(regl, instanceId, data.instanceCount, { size: 1, divisor: 1 }), - position: Attribute.create(regl, data.position, data.positionCount, { size: 3 }), - normal: Attribute.create(regl, data.normal, data.positionCount, { size: 3 }), - ...createTransformAttributes(regl, data.transform, data.instanceCount) - }), + uniforms, + attributes, elements: regl.elements({ data: data.elements.ref.value, primitive: 'triangles', diff --git a/src/mol-gl/renderable/point.ts b/src/mol-gl/renderable/point.ts index ab3fd0bf4770e04db3d698818661f37b617e502b..4d354adbf0d1b82247508f2c331c97ed64a51b12 100644 --- a/src/mol-gl/renderable/point.ts +++ b/src/mol-gl/renderable/point.ts @@ -17,6 +17,7 @@ type Point = 'point' namespace Point { export type Data = { position: ValueCell<Float32Array> + size?: ValueCell<Float32Array> transform: ValueCell<Float32Array> instanceCount: number @@ -25,13 +26,17 @@ namespace Point { export function create(regl: REGL.Regl, data: Data): Renderable { const instanceId = ValueCell.create(fillSerial(new Float32Array(data.instanceCount))) + const attributes = getBuffers({ + instanceId: Attribute.create(regl, instanceId, data.instanceCount, { size: 1, divisor: 1 }), + position: Attribute.create(regl, data.position, data.positionCount, { size: 3 }), + ...createTransformAttributes(regl, data.transform, data.positionCount) + }) + if (data.size) { + attributes.size = Attribute.create(regl, data.size, data.positionCount, { size: 1 }).buffer + } const command = regl({ ...PointShaders, - attributes: getBuffers({ - instanceId: Attribute.create(regl, instanceId, data.instanceCount, { size: 1, divisor: 1 }), - position: Attribute.create(regl, data.position, data.positionCount, { size: 3 }), - ...createTransformAttributes(regl, data.transform, data.positionCount) - }), + attributes, count: data.positionCount, instances: data.instanceCount, primitive: 'points' diff --git a/src/mol-gl/shader/mesh.frag b/src/mol-gl/shader/mesh.frag index 9007473408e9ecd4b91c4198bc84c76a87b86db0..2f679b9e90a746e1281ad5a434aa1254cc037640 100644 --- a/src/mol-gl/shader/mesh.frag +++ b/src/mol-gl/shader/mesh.frag @@ -60,7 +60,7 @@ void main() { vec3 L = normalize(lightVector); // light direction vec3 V = normalize(vViewPosition); // eye direction - vec3 N = normalize(vNormal); // surface normal + vec3 N = normalize(-vNormal); // surface normal // compute our diffuse & specular terms float specular = phongSpecular(L, V, N, shininess) * specularScale * falloff; @@ -70,7 +70,8 @@ void main() { // add the lighting vec3 color = vColor * (diffuse + ambient) + specular; - gl_FragColor.rgb = N; + // gl_FragColor.rgb = N; // gl_FragColor.rgb = vec3(1.0, 0.0, 0.0); + gl_FragColor.rgb = color; gl_FragColor.a = 1.0; } \ No newline at end of file diff --git a/src/mol-gl/shader/mesh.vert b/src/mol-gl/shader/mesh.vert index 7396279ec1b08e5d51e9b49d856e343727fac928..5f508ecb8e1e05ad7e0b2d0361ee88dd72f78689 100644 --- a/src/mol-gl/shader/mesh.vert +++ b/src/mol-gl/shader/mesh.vert @@ -4,7 +4,8 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -#define INSTANCE_COLOR +#define ATTRIBUTE_COLOR +// #define INSTANCE_COLOR precision highp float; @@ -52,7 +53,6 @@ void main(){ vViewPosition = mvPosition.xyz; gl_Position = projection * mvPosition; - // TODO do on CPU side mat3 normalMatrix = transpose(inverse(mat3(modelView))); vNormal = normalize(normalMatrix * normal); } \ No newline at end of file diff --git a/src/mol-math/linear-algebra/3d/mat4.ts b/src/mol-math/linear-algebra/3d/mat4.ts index d43158781865855521e1b9015f710cf35d93fbd4..4cb713b77ef6a535822e628f8d3e96ef3ded3c9a 100644 --- a/src/mol-math/linear-algebra/3d/mat4.ts +++ b/src/mol-math/linear-algebra/3d/mat4.ts @@ -293,6 +293,7 @@ namespace Mat4 { return mul(out, mul(out, a, b), c); } + /** Translate a Mat4 by the given Vec3 */ export function translate(out: Mat4, a: Mat4, v: Vec3) { const x = v[0], y = v[1], z = v[2]; let a00: number, a01: number, a02: number, a03: number,