Browse Source

wip, coloring

Alexander Rose 7 years ago
parent
commit
e59d69d274

+ 54 - 0
src/mol-geo/color/color.ts

@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+/** RGB color triplet expressed as a single number */
+type Color = number
+
+namespace Color {
+    export function toRgb(hexColor: Color) {
+        return [ hexColor >> 16 & 255, hexColor >> 8 & 255, hexColor & 255 ]
+    }
+
+    export function toRgbNormalized(hexColor: Color) {
+        return [ (hexColor >> 16 & 255) / 255, (hexColor >> 8 & 255) / 255, (hexColor & 255) / 255 ]
+    }
+
+    export function fromRgb(r: number, g: number, b: number ): Color {
+        return (r << 16) | (g << 8) | b
+    }
+
+    /** Copies hex color to rgb array */
+    export function toArray(hexColor: Color, array: Helpers.NumberArray, offset: number) {
+        array[ offset ] = (hexColor >> 16 & 255)
+        array[ offset + 1 ] = (hexColor >> 8 & 255)
+        array[ offset + 2 ] = (hexColor & 255)
+    }
+
+    /** Copies normalized (0 to 1) hex color to rgb array */
+    export function toArrayNormalized(hexColor: Color, array: Helpers.NumberArray, offset: number) {
+        array[ offset ] = (hexColor >> 16 & 255) / 255
+        array[ offset + 1 ] = (hexColor >> 8 & 255) / 255
+        array[ offset + 2 ] = (hexColor & 255) / 255
+    }
+
+    /** Linear interpolation between two colors */
+    export function interpolate(c1: Color, c2: Color, t: number): Color {
+        const r1 = c1 >> 16 & 255
+        const g1 = c1 >> 8 & 255
+        const b1 = c1 & 255
+        const r2 = c2 >> 16 & 255
+        const g2 = c2 >> 8 & 255
+        const b2 = c2 & 255
+
+        const r = r1 + (r2 - r1) * t
+        const g = g1 + (g2 - g1) * t
+        const b = b1 + (b2 - b1) * t
+
+        return (r << 16) | (g << 8) | b
+    }
+}
+
+export default Color

+ 130 - 0
src/mol-geo/color/data.ts

@@ -0,0 +1,130 @@
+/**
+ * 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 Color from './color';
+import { Mesh } from '../shape/mesh';
+
+function calculateTextureInfo (n: number, itemSize: number) {
+    const sqN = Math.sqrt(n * itemSize)
+    let width = Math.ceil(sqN)
+    width = width + (itemSize - (width % itemSize)) % itemSize
+    const height = width > 0 ? Math.ceil(n * itemSize / width) : 0
+    return { width, height, length: width * height * itemSize }
+}
+
+interface ColorTexture extends Uint8Array {
+    width: number,
+    height: number
+}
+
+function createColorTexture (n: number): ColorTexture {
+    const colorTexInfo = calculateTextureInfo(n, 3)
+    const colorTexture = new Uint8Array(colorTexInfo.length)
+    return Object.assign(colorTexture, {
+        width: colorTexInfo.width,
+        height: colorTexInfo.height
+    })
+}
+
+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 ColorData = UniformColor | AttributeColor | InstanceColor | ElementColor | ElementInstanceColor
+
+export interface UniformColorProps {
+    value: Color
+}
+
+/** Creates color uniform */
+export function createUniformColor(props: UniformColorProps): UniformColor {
+    return { type: 'uniform', value: Color.toRgbNormalized(props.value) }
+}
+
+export interface AttributeColorProps {
+    colorFn: (elementIdx: number) => Color
+    vertexCount: number,
+    offsetCount: number,
+    offsets: ValueCell<Uint32Array>
+}
+
+/** Creates color attribute with color for each element (i.e. shared across indtances/units) */
+export function createAttributeColor(props: AttributeColorProps): AttributeColor {
+    const { colorFn, vertexCount, offsetCount, offsets} = props
+    const colors = new Float32Array(vertexCount * 3);
+    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 hexColor = colorFn(i)
+        for (let i = start, il = end; i < il; ++i) {
+            Color.toArrayNormalized(hexColor, colors, i * 3)
+        }
+    }
+    return { type: 'attribute', value: ValueCell.create(colors) }
+}
+
+export interface InstanceColorProps {
+    colorFn: (unitIdx: number) => Color
+    unitCount: number
+}
+
+/** Creates color texture with color for each instance/unit */
+export function createInstanceColor(props: InstanceColorProps): InstanceColor {
+    const { colorFn, unitCount} = props
+    const colors = createColorTexture(unitCount)
+    for (let i = 0; i < unitCount; i++) {
+        Color.toArray(colorFn(i), colors, i * 3)
+    }
+    return { type: 'instance', value: ValueCell.create(colors) }
+}
+
+export interface ElementColorProps {
+    colorFn: (elementIdx: number) => Color
+    offsetCount: number,
+    offsets: ValueCell<Uint32Array>
+}
+
+/** Creates color texture with color for each element (i.e. shared across indtances/units) */
+export function createElementColor(props: ElementColorProps): ElementColor {
+    const { colorFn, offsetCount } = props
+    const elementCount = offsetCount - 1
+    const colors = createColorTexture(elementCount)
+    for (let i = 0, il = elementCount; i < il; ++i) {
+        Color.toArray(colorFn(i), colors, i * 3)
+    }
+    return { type: 'element', value: ValueCell.create(colors) }
+}
+
+export interface ElementInstanceColorProps {
+    colorFn: (unitIdx: number, elementIdx: number) => Color
+    unitCount: number,
+    offsetCount: number,
+    offsets: ValueCell<Uint32Array>
+}
+
+/** Creates color texture with color for each element instance (i.e. for each unit) */
+export function createElementInstanceColor(props: ElementInstanceColorProps): ElementInstanceColor {
+    const { colorFn, unitCount, offsetCount } = props
+    const elementCount = offsetCount - 1
+    const count = unitCount * elementCount
+    const colors = createColorTexture(count)
+    let colorOffset = 0
+    for (let i = 0; i < unitCount; i++) {
+        for (let j = 0, jl = elementCount; j < jl; ++j) {
+            Color.toArray(colorFn(i, j), colors, colorOffset)
+            colorOffset += 3
+        }
+    }
+    return { type: 'element-instance', value: ValueCell.create(colors) }
+}
+
+/** Create color attribute or texture, depending on the mesh */
+export function createAttributeOrElementColor(mesh: Mesh, props: AttributeColorProps) {
+    return mesh.vertexCount < 4 * mesh.offsetCount ? createAttributeColor(props) : createElementColor(props)
+}

+ 4 - 80
src/mol-geo/color/index.ts

@@ -4,84 +4,8 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ColorBrewer } from './tables'
+import Color from './color'
+import { ColorData } from './data'
 
-export { ElementColor } from './structure/element';
-
-export function colorToRgb(hexColor: number) {
-    return { r: hexColor >> 16 & 255, g: hexColor >> 8 & 255, b: hexColor & 255 }
-}
-
-/** Copies hex color to rgb array */
-export function colorToArray(hexColor: number, array: Helpers.NumberArray, offset: number) {
-
-    array[ offset ] = (hexColor >> 16 & 255)
-    array[ offset + 1 ] = (hexColor >> 8 & 255)
-    array[ offset + 2 ] = (hexColor & 255)
-}
-
-/** Copies normalized (0 to 1) hex color to rgb array */
-export function normalizedColorToArray(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
-}
-
-/** Linear interpolation between two colors */
-export function interpolate(c1: number, c2: number, t: number) {
-    const r1 = c1 >> 16 & 255
-    const g1 = c1 >> 8 & 255
-    const b1 = c1 & 255
-    const r2 = c2 >> 16 & 255
-    const g2 = c2 >> 8 & 255
-    const b2 = c2 & 255
-
-    const r = r1 + (r2 - r1) * t
-    const g = g1 + (g2 - g1) * t
-    const b = b1 + (b2 - b1) * t
-
-    return (r << 16) | (g << 8) | b
-}
-
-export type Color = number
-
-export interface ColorScale {
-    /** Returns hex color for given value */
-    color: (value: number) => Color
-    /** Copies color to rgb int8 array */
-    colorToArray: (value: number, array: Helpers.NumberArray, offset: number) => void
-    /** Copies normalized (0 to 1) hex color to rgb array */
-    normalizedColorToArray: (value: number, array: Helpers.NumberArray, offset: number) => void
-}
-
-export const DefaultColorScale = {
-    domain: [0, 1],
-    reverse: false,
-    colors: ColorBrewer.RdYlBu as Color[]
-}
-export type ColorScaleProps = Partial<typeof DefaultColorScale>
-
-export namespace ColorScale {
-    export function create(props: ColorScaleProps): ColorScale {
-        const { domain, reverse, colors } = { ...DefaultColorScale, ...props }
-        const [ min, max ] = reverse ? domain.slice().reverse() : domain
-        const count1 = colors.length - 1
-
-        function color(value: number) {
-            const t = ((value - min) / (max - min)) * count1
-            const tf = Math.floor(t)
-            const c1 = colors[tf]
-            const c2 = colors[Math.ceil(t)]
-            return interpolate(c1, c2, t - tf)
-        }
-        return {
-            color,
-            colorToArray: (value: number, array: Helpers.NumberArray, offset: number) => {
-                colorToArray(color(value), array, offset)
-            },
-            normalizedColorToArray: (value: number, array: Helpers.NumberArray, offset: number) => {
-                normalizedColorToArray(color(value), array, offset)
-            },
-        }
-    }
-}
+export { Color, ColorData }
+export { ColorBrewer, ColorNames } from './tables'

+ 49 - 0
src/mol-geo/color/scale.ts

@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import Color from './color'
+import { ColorBrewer } from './tables'
+
+export interface ColorScale {
+    /** Returns hex color for given value */
+    color: (value: number) => Color
+    /** Copies color to rgb int8 array */
+    colorToArray: (value: number, array: Helpers.NumberArray, offset: number) => void
+    /** Copies normalized (0 to 1) hex color to rgb array */
+    normalizedColorToArray: (value: number, array: Helpers.NumberArray, offset: number) => void
+}
+
+export const DefaultColorScale = {
+    domain: [0, 1],
+    reverse: false,
+    colors: ColorBrewer.RdYlBu as Color[]
+}
+export type ColorScaleProps = Partial<typeof DefaultColorScale>
+
+export namespace ColorScale {
+    export function create(props: ColorScaleProps): ColorScale {
+        const { domain, reverse, colors } = { ...DefaultColorScale, ...props }
+        const [ min, max ] = reverse ? domain.slice().reverse() : domain
+        const count1 = colors.length - 1
+
+        function color(value: number) {
+            const t = ((value - min) / (max - min)) * count1
+            const tf = Math.floor(t)
+            const c1 = colors[tf]
+            const c2 = colors[Math.ceil(t)]
+            return Color.interpolate(c1, c2, t - tf)
+        }
+        return {
+            color,
+            colorToArray: (value: number, array: Helpers.NumberArray, offset: number) => {
+                Color.toArray(color(value), array, offset)
+            },
+            normalizedColorToArray: (value: number, array: Helpers.NumberArray, offset: number) => {
+                Color.toArrayNormalized(color(value), array, offset)
+            },
+        }
+    }
+}

+ 23 - 5
src/mol-geo/color/structure/element.ts

@@ -5,15 +5,33 @@
  */
 
 import { ElementSymbol } from 'mol-model/structure/model/types';
+import Color from '../color';
+import { createAttributeOrElementColor } from '../data';
+import { StructureColorDataProps } from '.';
+import { OrderedSet } from 'mol-data/int';
 
 // from Jmol http://jmol.sourceforge.net/jscolors/ (or 0xFFFFFF)
-export const ElementColors: { [k: string]: number } = {
+export const ElementSymbolColors: { [k: string]: Color } = {
     '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
+const DefaultElementSymbolColor = 0xFFFFFF
 
-export function ElementColor(element: ElementSymbol): number {
-    const c = ElementColors[element as any as string];
-    return c === void 0 ? DefaultElementColor : c
+export function elementSymbolColor(element: ElementSymbol): Color {
+    const c = ElementSymbolColors[element as any as string];
+    return c === void 0 ? DefaultElementSymbolColor : c
+}
+
+export function elementSymbolColorData(props: StructureColorDataProps) {
+    const { units, elementGroup, mesh } = props
+    const { type_symbol } = units[0].model.hierarchy.atoms
+    return createAttributeOrElementColor(mesh, {
+        colorFn: (elementIdx: number) => {
+            const e = OrderedSet.getAt(elementGroup.elements, elementIdx)
+            return elementSymbolColor(type_symbol.value(e))
+        },
+        vertexCount: mesh.vertexCount,
+        offsetCount: mesh.offsetCount,
+        offsets: mesh.offsetBuffer as any
+    })
 }

+ 14 - 0
src/mol-geo/color/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 { Mesh } from '../../shape/mesh';
+
+export interface StructureColorDataProps {
+    units: ReadonlyArray<Unit>,
+    elementGroup: ElementGroup,
+    mesh: Mesh
+}

+ 31 - 54
src/mol-geo/representation/structure/spacefill.ts

@@ -15,24 +15,16 @@ 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, colorToArray, normalizedColorToArray, ColorScale } from '../../color';
-import { Color } from 'mol-gl/renderable/mesh';
-import { createColorTexture } from 'mol-gl/util';
+import { elementSymbolColorData } from '../../color/structure/element';
+import { ColorData } from '../../color';
+import { createInstanceColor, createUniformColor, createElementInstanceColor } from '../../color/data';
+import { ColorScale } from '../../color/scale';
 
 export const DefaultSpacefillProps = {
     detail: 0
 }
 export type SpacefillProps = Partial<typeof DefaultSpacefillProps>
 
-// function buildColorBuffer() {
-//     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)
-//         }
-//     }
-// }
-
 export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
     const renderObjects: RenderObject[] = []
 
@@ -70,52 +62,37 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
             // console.log(mesh)
             if (!mesh.offsetBuffer.ref.value) return
 
-            const unitsCount = units.length
-            const transformArray = new Float32Array(unitsCount * 16)
-            for (let i = 0; i < unitsCount; i++) {
+            const unitCount = units.length
+            const transformArray = new Float32Array(unitCount * 16)
+            for (let i = 0; i < unitCount; i++) {
                 Mat4.toArray(units[i].operator.matrix, transformArray, i * 16)
             }
 
-            // console.log({ unitsCount, elementCount })
-
-            let colorType = 'element'
-            let color: Color
-
-            if (colorType === 'attribute') {
-                const colors = new Float32Array(mesh.vertexCount * 3);
-                const offsets = mesh.offsetBuffer.ref.value
-                for (let i = 0, il = mesh.offsetCount - 1; i < il; ++i) {
-                    const start = offsets[i]
-                    const end = offsets[i + 1]
-                    const e = OrderedSet.getAt(elementGroup.elements, i)
-                    const hexColor = ElementColor(type_symbol.value(e))
-                    for (let i = start, il = end; i < il; ++i) {
-                        normalizedColorToArray(hexColor, colors, i * 3)
-                    }
-                }
-                color = { type: 'attribute', value: ValueCell.create(colors) }
+            // console.log({ unitCount, elementCount })
+
+            let colorType = 'element-instance'
+            let color: ColorData
+
+            if (colorType === 'uniform') {
+                color = createUniformColor({ value: 0xFF4411 })
+            } else if (colorType === 'attribute') {
+                color = elementSymbolColorData({ units, elementGroup, mesh })
             } else if (colorType === 'instance') {
-                const colors = createColorTexture(unitsCount)
-                const scale = ColorScale.create({ domain: [ 0, unitsCount - 1 ] })
-                for (let i = 0; i < unitsCount; i++) {
-                    scale.colorToArray(i, colors, i * 3)
-                }
-                color = { type: 'instance', value: ValueCell.create(colors) }
-            } else if (colorType === 'element') {
-                const elementCount = mesh.offsetCount - 1
-                const count = unitsCount * elementCount
-                const colors = createColorTexture(count)
-                const scale = ColorScale.create({ domain: [ 0, count - 1 ] })
-                let colorOffset = 0
-                for (let i = 0; i < unitsCount; i++) {
-                    for (let j = 0, jl = elementCount; j < jl; ++j) {
-                        const hexColor = scale.color(i * elementCount + j)
-                        colorToArray(hexColor, colors, colorOffset)
-                        colorOffset += 3
-                    }
-                }
-                color = { type: 'element', value: ValueCell.create(colors) }
+                const scale = ColorScale.create({ domain: [ 0, unitCount - 1 ] })
+                color = createInstanceColor({
+                    colorFn: scale.color,
+                    unitCount
+                })
+            } else if (colorType === 'element-instance') {
+                const scale = ColorScale.create({ domain: [ 0, unitCount * elementCount - 1 ] })
+                color = createElementInstanceColor({
+                    colorFn: (unitIdx, elementIdx) => scale.color(unitIdx * elementCount + elementIdx),
+                    unitCount,
+                    offsetCount: mesh.offsetCount,
+                    offsets: mesh.offsetBuffer as any
+                })
             }
+            console.log(color!)
 
             const spheres = createRenderObject('mesh', {
                 position: mesh.vertexBuffer,
@@ -125,7 +102,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
                 transform: ValueCell.create(transformArray),
                 index: mesh.indexBuffer,
 
-                instanceCount: unitsCount,
+                instanceCount: unitCount,
                 indexCount: mesh.triangleCount,
                 elementCount: mesh.offsetCount - 1,
                 positionCount: mesh.vertexCount

+ 21 - 11
src/mol-gl/renderable/mesh.ts

@@ -6,21 +6,28 @@
 
 import REGL = require('regl');
 import { ValueCell } from 'mol-util/value-cell'
+import { ColorData } from 'mol-geo/color';
 
 import { Renderable } from '../renderable'
-import { ColorTexture } from '../util'
 import { getBuffers, createTransformAttributes, fillSerial, createColorUniforms } from './util'
 import Attribute from '../attribute';
-import { MeshShaders } from '../shaders'
+import { MeshShaders, addDefines, ShaderDefines } from '../shaders'
 
 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> }
-export type Color = AttributeColor | InstanceColor | ElementColor
+function getColorDefines(color: ColorData) {
+    const defines: ShaderDefines = {}
+    switch (color.type) {
+        case 'uniform': defines.UNIFORM_COLOR = ''; break;
+        case 'attribute': defines.ATTRIBUTE_COLOR = ''; break;
+        case 'element': defines.ELEMENT_COLOR = ''; break;
+        case 'instance': defines.INSTANCE_COLOR = ''; break;
+        case 'element-instance': defines.ELEMENT_INSTANCE_COLOR = ''; break;
+    }
+    return defines
+}
 
 namespace Mesh {
     export type Data = {
@@ -28,7 +35,7 @@ namespace Mesh {
         normal: ValueCell<Float32Array>
         id: ValueCell<Float32Array>
 
-        readonly color: Color
+        readonly color: ColorData
         transform: ValueCell<Float32Array>
         index: ValueCell<Uint32Array>
 
@@ -39,6 +46,7 @@ namespace Mesh {
     }
 
     export function create(regl: REGL.Regl, data: Data, _uniforms: Uniforms): Renderable {
+        const defines = getColorDefines(data.color)
         const instanceId = ValueCell.create(fillSerial(new Float32Array(data.instanceCount)))
         const uniforms = {
             objectId: _uniforms.objectId || 0,
@@ -46,8 +54,10 @@ namespace Mesh {
             elementCount: data.elementCount,
             ..._uniforms
         }
-        if (data.color.type === 'instance' || data.color.type === 'element') {
-            Object.assign(uniforms, createColorUniforms(regl, data.color.value as ValueCell<ColorTexture>))
+        if (data.color.type === 'instance' || data.color.type === 'element' || data.color.type === 'element-instance') {
+            Object.assign(uniforms, createColorUniforms(regl, data.color.value))
+        } else if (data.color.type === 'uniform') {
+            Object.assign(uniforms, { color: data.color.value })
         }
         const attributes = getBuffers({
             instanceId: Attribute.create(regl, instanceId, data.instanceCount, { size: 1, divisor: 1 }),
@@ -58,10 +68,10 @@ namespace Mesh {
             ...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
+            attributes.color = Attribute.create(regl, data.color.value, data.positionCount, { size: 3 }).buffer
         }
         const command = regl({
-            ...MeshShaders,
+            ...addDefines(defines, MeshShaders),
             uniforms,
             attributes,
             elements: regl.elements({

+ 9 - 34
src/mol-gl/shader/mesh.frag

@@ -4,10 +4,6 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-// #define ATTRIBUTE_COLOR
-// #define INSTANCE_COLOR
-#define ELEMENT_COLOR
-
 precision highp float;
 
 struct Light {
@@ -23,36 +19,15 @@ uniform mat4 view;
 
 varying vec3 vNormal, vViewPosition;
 
-#if defined( UNIFORM_COLOR )
+#if defined(UNIFORM_COLOR)
     uniform vec3 color;
-#elif defined( ATTRIBUTE_COLOR ) || defined( INSTANCE_COLOR ) || defined( ELEMENT_COLOR )
+#elif defined(ATTRIBUTE_COLOR) || defined(INSTANCE_COLOR) || defined(ELEMENT_COLOR) || defined(ELEMENT_INSTANCE_COLOR)
     varying vec3 vColor;
 #endif
 
-float phongSpecular(vec3 lightDirection, vec3 viewDirection, vec3 surfaceNormal, float shininess) {
-    //Calculate Phong power
-    vec3 R = -reflect(lightDirection, surfaceNormal);
-    return pow(max(0.0, dot(viewDirection, R)), shininess);
-}
-
-#define PI 3.14159265
-
-float orenNayarDiffuse(vec3 lightDirection, vec3 viewDirection, vec3 surfaceNormal, float roughness, float albedo) {
-    float LdotV = dot(lightDirection, viewDirection);
-    float NdotL = dot(lightDirection, surfaceNormal);
-    float NdotV = dot(surfaceNormal, viewDirection);
-
-    float s = LdotV - NdotL * NdotV;
-    float t = mix(1.0, max(NdotL, NdotV), step(0.0, s));
-
-    float sigma2 = roughness * roughness;
-    float A = 1.0 + sigma2 * (albedo / (sigma2 + 0.13) + 0.5 / (sigma2 + 0.33));
-    float B = 0.45 * sigma2 / (sigma2 + 0.09);
-
-    return albedo * max(0.0, NdotL) * (A + B * s / t) / PI;
-}
-
-#pragma glslify: attenuation = require(./attenuation.glsl)
+#pragma glslify: attenuation = require(./util/attenuation.glsl)
+#pragma glslify: calculateSpecular = require(./util/phong-specular.glsl)
+#pragma glslify: calculateDiffuse = require(./util/oren-nayar-diffuse.glsl)
 
 const float specularScale = 0.65;
 const float shininess = 100.0;
@@ -61,9 +36,9 @@ const float albedo = 0.95;
 
 void main() {
     // material color
-    #if defined( UNIFORM_COLOR )
+    #if defined(UNIFORM_COLOR)
         vec3 material = color;
-    #elif defined( ATTRIBUTE_COLOR ) || defined( INSTANCE_COLOR ) || defined( ELEMENT_COLOR )
+    #elif defined(ATTRIBUTE_COLOR) || defined(INSTANCE_COLOR) || defined(ELEMENT_COLOR) || defined(ELEMENT_INSTANCE_COLOR)
         vec3 material = vColor;
     #endif
 
@@ -81,8 +56,8 @@ void main() {
     vec3 N = normalize(-vNormal); // surface normal
 
     // compute our diffuse & specular terms
-    float specular = phongSpecular(L, V, N, shininess) * specularScale * falloff;
-    vec3 diffuse = light.color * orenNayarDiffuse(L, V, N, roughness, albedo) * falloff;
+    float specular = calculateSpecular(L, V, N, shininess) * specularScale * falloff;
+    vec3 diffuse = light.color * calculateDiffuse(L, V, N, roughness, albedo) * falloff;
     vec3 ambient = light.ambient;
 
     // add the lighting

+ 11 - 13
src/mol-gl/shader/mesh.vert

@@ -4,10 +4,6 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-// #define ATTRIBUTE_COLOR
-// #define INSTANCE_COLOR
-#define ELEMENT_COLOR
-
 precision highp float;
 
 uniform mat4 projection, model, view;
@@ -16,11 +12,11 @@ uniform int objectId;
 uniform int instanceCount;
 uniform int elementCount;
 
-#if defined( UNIFORM_COLOR )
+#if defined(UNIFORM_COLOR)
     uniform vec3 color;
-#elif defined( ATTRIBUTE_COLOR )
+#elif defined(ATTRIBUTE_COLOR)
     attribute vec3 color;
-#elif defined( INSTANCE_COLOR ) || defined( ELEMENT_COLOR )
+#elif defined(INSTANCE_COLOR) || defined(ELEMENT_COLOR) || defined(ELEMENT_INSTANCE_COLOR)
     uniform vec2 colorTexSize;
     uniform sampler2D colorTex;
 #endif
@@ -35,16 +31,18 @@ varying vec3 vColor;
 varying vec3 vNormal;
 varying vec3 vViewPosition;
 
-#pragma glslify: inverse = require(./inverse.glsl)
-#pragma glslify: read_vec3 = require(./read-vec3.glsl)
-#pragma glslify: transpose = require(./transpose.glsl)
+#pragma glslify: inverse = require(./util/inverse.glsl)
+#pragma glslify: read_vec3 = require(./util/read-vec3.glsl)
+#pragma glslify: transpose = require(./util/transpose.glsl)
 
 void main(){
-    #if defined( ATTRIBUTE_COLOR )
+    #if defined(ATTRIBUTE_COLOR)
         vColor = color;
-    #elif defined( INSTANCE_COLOR )
+    #elif defined(INSTANCE_COLOR)
         vColor = read_vec3(colorTex, instanceId, colorTexSize);
-    #elif defined( ELEMENT_COLOR )
+    #elif defined(ELEMENT_COLOR)
+        vColor = read_vec3(colorTex, elementId, colorTexSize);
+    #elif defined(ELEMENT_INSTANCE_COLOR)
         vColor = read_vec3(colorTex, instanceId * float(elementCount) + elementId, colorTexSize);
     #endif
 

+ 0 - 0
src/mol-gl/shader/attenuation.glsl → src/mol-gl/shader/util/attenuation.glsl


+ 0 - 0
src/mol-gl/shader/inverse.glsl → src/mol-gl/shader/util/inverse.glsl


+ 21 - 0
src/mol-gl/shader/util/oren-nayar-diffuse.glsl

@@ -0,0 +1,21 @@
+// (c) 2014 Mikola Lysenko. MIT License
+// https://github.com/glslify/glsl-diffuse-oren-nayar
+
+#define PI 3.14159265
+
+float orenNayarDiffuse(vec3 lightDirection, vec3 viewDirection, vec3 surfaceNormal, float roughness, float albedo) {
+    float LdotV = dot(lightDirection, viewDirection);
+    float NdotL = dot(lightDirection, surfaceNormal);
+    float NdotV = dot(surfaceNormal, viewDirection);
+
+    float s = LdotV - NdotL * NdotV;
+    float t = mix(1.0, max(NdotL, NdotV), step(0.0, s));
+
+    float sigma2 = roughness * roughness;
+    float A = 1.0 + sigma2 * (albedo / (sigma2 + 0.13) + 0.5 / (sigma2 + 0.33));
+    float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+    return albedo * max(0.0, NdotL) * (A + B * s / t) / PI;
+}
+
+#pragma glslify: export(orenNayarDiffuse)

+ 10 - 0
src/mol-gl/shader/util/phong-specular.glsl

@@ -0,0 +1,10 @@
+// (c) 2014 Mikola Lysenko. MIT License
+// https://github.com/glslify/glsl-specular-phong
+
+float phongSpecular(vec3 lightDirection, vec3 viewDirection, vec3 surfaceNormal, float shininess) {
+    //Calculate Phong power
+    vec3 R = -reflect(lightDirection, surfaceNormal);
+    return pow(max(0.0, dot(viewDirection, R)), shininess);
+}
+
+#pragma glslify: export(phongSpecular)

+ 0 - 0
src/mol-gl/shader/read-vec3.glsl → src/mol-gl/shader/util/read-vec3.glsl


+ 0 - 0
src/mol-gl/shader/transpose.glsl → src/mol-gl/shader/util/transpose.glsl


+ 41 - 3
src/mol-gl/shaders.ts

@@ -1,12 +1,50 @@
 
-const PointShaders = {
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+interface Shaders {
+    vert: string
+    frag: string
+}
+
+export const PointShaders = {
     vert: require('mol-gl/shader/point.vert'),
     frag: require('mol-gl/shader/point.frag')
 }
 
-const MeshShaders = {
+export const MeshShaders = {
     vert: require('mol-gl/shader/mesh.vert'),
     frag: require('mol-gl/shader/mesh.frag')
 }
 
-export { PointShaders, MeshShaders }
+type ShaderDefine = (
+    'UNIFORM_COLOR' | 'ATTRIBUTE_COLOR' | 'INSTANCE_COLOR' | 'ELEMENT_COLOR' | 'ELEMENT_INSTANCE_COLOR'
+)
+export type ShaderDefines = {
+    [k in ShaderDefine]?: number|string
+}
+
+function getDefines (defines: ShaderDefines) {
+    if (defines === undefined) return ''
+    const lines = []
+    for (const name in defines) {
+        const value = defines[ name as keyof ShaderDefines ]
+        if (value) {
+            lines.push(`#define ${name} ${value}`)
+        } else {
+            lines.push(`#define ${name}`)
+        }
+    }
+    return lines.join('\n') + '\n'
+}
+
+export function addDefines(defines: ShaderDefines, shaders: Shaders) {
+    const header = getDefines(defines)
+    return {
+        vert: `${header}${shaders.vert}`,
+        frag: `${header}${shaders.frag}`
+    }
+}

+ 0 - 27
src/mol-gl/util.ts

@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
-
-export function calculateTextureInfo (n: number, itemSize: number) {
-    const sqN = Math.sqrt(n * itemSize)
-    let width = Math.ceil(sqN)
-    width = width + (itemSize - (width % itemSize)) % itemSize
-    const height = width > 0 ? Math.ceil(n * itemSize / width) : 0
-    return { width, height, length: width * height * itemSize }
-}
-
-export interface ColorTexture extends Uint8Array {
-    width: number,
-    height: number
-}
-
-export function createColorTexture (n: number): ColorTexture {
-    const colorTexInfo = calculateTextureInfo(n, 3)
-    const colorTexture = new Uint8Array(colorTexInfo.length)
-    return Object.assign(colorTexture, {
-        width: colorTexInfo.width,
-        height: colorTexInfo.height
-    })
-}