Browse Source

wip, coloring

Alexander Rose 7 years ago
parent
commit
2d21bd7cfb

+ 4 - 4
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)

+ 13 - 0
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
+}

+ 19 - 0
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
+}

+ 5 - 0
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),
 

+ 13 - 6
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,
 

+ 33 - 8
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
             }
         }
     }

+ 26 - 14
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',

+ 10 - 5
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'

+ 3 - 2
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;
 }

+ 2 - 2
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);
 }

+ 1 - 0
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,