Browse Source

refactored representation-to-renderItem pipeline

Alexander Rose 6 years ago
parent
commit
f3846af55c
41 changed files with 718 additions and 762 deletions
  1. 1 1
      src/mol-geo/representation/index.ts
  2. 1 1
      src/mol-geo/representation/structure/index.ts
  3. 27 19
      src/mol-geo/representation/structure/point.ts
  4. 38 26
      src/mol-geo/representation/structure/spacefill.ts
  5. 1 1
      src/mol-geo/representation/volume/index.ts
  6. 35 27
      src/mol-geo/representation/volume/surface.ts
  7. 3 3
      src/mol-geo/shape/mesh.ts
  8. 3 4
      src/mol-geo/shape/vertex-map.ts
  9. 43 17
      src/mol-geo/util/color-data.ts
  10. 2 2
      src/mol-geo/util/marching-cubes/algorithm.ts
  11. 22 0
      src/mol-geo/util/mesh-data.ts
  12. 18 7
      src/mol-geo/util/size-data.ts
  13. 1 0
      src/mol-gl/TODO
  14. 32 21
      src/mol-gl/_spec/renderer.spec.ts
  15. 33 0
      src/mol-gl/render-object.ts
  16. 8 12
      src/mol-gl/renderable.ts
  17. 35 93
      src/mol-gl/renderable/mesh.ts
  18. 34 77
      src/mol-gl/renderable/point.ts
  19. 131 0
      src/mol-gl/renderable/schema.ts
  20. 2 213
      src/mol-gl/renderable/util.ts
  21. 21 19
      src/mol-gl/renderer.ts
  22. 6 29
      src/mol-gl/scene.ts
  23. 15 12
      src/mol-gl/shader-code.ts
  24. 3 3
      src/mol-gl/shader/chunks/color-assign-material.glsl
  25. 8 8
      src/mol-gl/shader/chunks/color-assign-varying.glsl
  26. 3 3
      src/mol-gl/shader/chunks/color-frag-params.glsl
  27. 7 7
      src/mol-gl/shader/chunks/color-vert-params.glsl
  28. 12 12
      src/mol-gl/shader/mesh.frag
  29. 16 16
      src/mol-gl/shader/mesh.vert
  30. 2 2
      src/mol-gl/shader/point.frag
  31. 26 20
      src/mol-gl/shader/point.vert
  32. 9 4
      src/mol-gl/webgl/buffer.ts
  33. 1 0
      src/mol-gl/webgl/context.ts
  34. 22 27
      src/mol-gl/webgl/program.ts
  35. 40 46
      src/mol-gl/webgl/render-item.ts
  36. 15 8
      src/mol-gl/webgl/texture.ts
  37. 35 17
      src/mol-gl/webgl/uniform.ts
  38. 1 0
      src/mol-util/index.ts
  39. 1 1
      src/mol-view/stage.ts
  40. 2 1
      src/mol-view/state/transform.ts
  41. 3 3
      src/mol-view/viewer.ts

+ 1 - 1
src/mol-geo/representation/index.ts

@@ -5,7 +5,7 @@
  */
 
 import { Task } from 'mol-task'
-import { RenderObject } from 'mol-gl/scene';
+import { RenderObject } from 'mol-gl/render-object';
 
 export interface RepresentationProps {}
 

+ 1 - 1
src/mol-geo/representation/structure/index.ts

@@ -7,7 +7,7 @@
 
 import { Structure, StructureSymmetry, Unit } from 'mol-model/structure';
 import { Task } from 'mol-task'
-import { RenderObject } from 'mol-gl/scene';
+import { RenderObject } from 'mol-gl/render-object';
 import { Representation, RepresentationProps } from '..';
 import { ColorTheme } from '../../theme';
 

+ 27 - 19
src/mol-geo/representation/structure/point.ts

@@ -6,7 +6,7 @@
  */
 
 import { ValueCell } from 'mol-util/value-cell'
-import { createPointRenderObject, RenderObject, PointRenderObject } from 'mol-gl/scene'
+import { createPointRenderObject, RenderObject, PointRenderObject } from 'mol-gl/render-object'
 import { Unit, Element } from 'mol-model/structure';
 import { Task } from 'mol-task'
 import { fillSerial } from 'mol-gl/renderable/util';
@@ -15,8 +15,9 @@ import { UnitsRepresentation } from './index';
 import VertexMap from '../../shape/vertex-map';
 import { ColorTheme, SizeTheme } from '../../theme';
 import { createTransforms, createColors, createSizes } from './utils';
-import { deepEqual } from 'mol-util';
+import { deepEqual, defaults } from 'mol-util';
 import { SortedArray } from 'mol-data/int';
+import { RenderableState, PointValues } from 'mol-gl/renderable';
 
 export const DefaultPointProps = {
     colorTheme: { name: 'instance-index' } as ColorTheme,
@@ -64,9 +65,8 @@ export default function Point(): UnitsRepresentation<PointProps> {
                 _units = group.units
                 _elements = group.elements;
 
-                const { colorTheme, sizeTheme, alpha, visible, depthMask } = curProps
+                const { colorTheme, sizeTheme } = curProps
                 const elementCount = _elements.length
-                const unitCount = _units.length
 
                 const vertexMap = VertexMap.create(
                     elementCount,
@@ -87,24 +87,32 @@ export default function Point(): UnitsRepresentation<PointProps> {
                 await ctx.update('Computing point sizes');
                 const size = createSizes(group, vertexMap, sizeTheme)
 
-                points = createPointRenderObject({
-                    objectId: 0,
-                    alpha,
-                    visible,
-                    depthMask,
+                const instanceCount = group.units.length
 
-                    position: ValueCell.create(vertices),
-                    id: ValueCell.create(fillSerial(new Float32Array(elementCount))),
-                    size: size,
-                    color: color,
-                    transform: ValueCell.create(transforms),
+                const values: PointValues = {
+                    aPosition: ValueCell.create(vertices),
+                    aElementId: ValueCell.create(fillSerial(new Float32Array(elementCount))),
+                    aTransform: ValueCell.create(transforms),
+                    aInstanceId: ValueCell.create(fillSerial(new Float32Array(instanceCount))),
+                    ...color,
+                    ...size,
 
-                    instanceCount: unitCount,
-                    elementCount,
-                    positionCount: vertices.length / 3,
+                    uAlpha: ValueCell.create(defaults(props.alpha, 1.0)),
+                    uObjectId: ValueCell.create(0),
+                    uInstanceCount: ValueCell.create(instanceCount),
+                    uElementCount: ValueCell.create(group.elements.length),
+
+                    drawCount: ValueCell.create(vertices.length / 3),
+                    instanceCount: ValueCell.create(instanceCount),
+
+                    dPointSizeAttenuation: ValueCell.create(true)
+                }
+                const state: RenderableState = {
+                    depthMask: defaults(props.depthMask, true),
+                    visible: defaults(props.visible, true)
+                }
 
-                    usePointSizeAttenuation: true
-                })
+                points = createPointRenderObject(values, state)
                 renderObjects.push(points)
             })
         },

+ 38 - 26
src/mol-geo/representation/structure/spacefill.ts

@@ -7,7 +7,7 @@
 
 import { ValueCell } from 'mol-util/value-cell'
 
-import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/scene'
+import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
 // import { createColorTexture } from 'mol-gl/util';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra'
 import { Unit, Element, Queries } from 'mol-model/structure';
@@ -17,7 +17,10 @@ import { MeshBuilder } from '../../shape/mesh-builder';
 import { createTransforms, createColors } from './utils';
 import VertexMap from '../../shape/vertex-map';
 import { icosahedronVertexCount } from '../../primitive/icosahedron';
-import { deepEqual } from 'mol-util';
+import { deepEqual, defaults } from 'mol-util';
+import { fillSerial } from 'mol-gl/renderable/util';
+import { RenderableState, MeshValues } from 'mol-gl/renderable';
+import { getMeshData } from '../../util/mesh-data';
 
 export const DefaultSpacefillProps = {
     ...DefaultStructureProps,
@@ -81,7 +84,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
             return Task.create('Spacefill.create', async ctx => {
                 renderObjects.length = 0 // clear
 
-                const { detail, colorTheme, alpha, visible, doubleSided, depthMask } = { ...DefaultSpacefillProps, ...props }
+                const { detail, colorTheme } = { ...DefaultSpacefillProps, ...props }
 
                 const mesh = await createSpacefillMesh(group.units[0], detail).runAsChild(ctx, 'Computing spacefill mesh')
                 // console.log(mesh)
@@ -94,25 +97,34 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
                 await ctx.update('Computing spacefill colors');
                 const color = createColors(group, vertexMap, colorTheme)
 
-                spheres = createMeshRenderObject({
-                    objectId: 0,
-                    alpha,
-                    visible,
-                    doubleSided,
-                    depthMask,
-
-                    position: mesh.vertexBuffer,
-                    normal: mesh.normalBuffer as ValueCell<Float32Array>,
-                    color: color,
-                    id: mesh.idBuffer as ValueCell<Float32Array>,
-                    transform: ValueCell.create(transforms),
-                    index: mesh.indexBuffer,
-
-                    instanceCount: group.units.length,
-                    indexCount: mesh.triangleCount,
-                    elementCount: group.elements.length,
-                    positionCount: mesh.vertexCount
-                })
+                const instanceCount = group.units.length
+
+                const values: MeshValues = {
+                    ...getMeshData(mesh),
+                    aTransform: ValueCell.create(transforms),
+                    aInstanceId: ValueCell.create(fillSerial(new Float32Array(instanceCount))),
+                    ...color,
+
+                    uAlpha: ValueCell.create(defaults(props.alpha, 1.0)),
+                    uObjectId: ValueCell.create(0),
+                    uInstanceCount: ValueCell.create(instanceCount),
+                    uElementCount: ValueCell.create(group.elements.length),
+
+                    elements: mesh.indexBuffer,
+
+                    drawCount: ValueCell.create(mesh.triangleCount * 3),
+                    instanceCount: ValueCell.create(instanceCount),
+
+                    dDoubleSided: ValueCell.create(defaults(props.doubleSided, true)),
+                    dFlatShaded: ValueCell.create(false),
+                    dFlipSided: ValueCell.create(false),
+                }
+                const state: RenderableState = {
+                    depthMask: defaults(props.depthMask, true),
+                    visible: defaults(props.visible, true)
+                }
+
+                spheres = createMeshRenderObject(values, state)
                 renderObjects.push(spheres)
             })
         },
@@ -124,10 +136,10 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
                 if (newProps.detail !== currentProps.detail) return false
                 if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) return false
 
-                spheres.props.alpha = newProps.alpha
-                spheres.props.visible = newProps.visible
-                spheres.props.doubleSided = newProps.doubleSided
-                spheres.props.depthMask = newProps.depthMask
+                ValueCell.update(spheres.values.uAlpha, newProps.alpha)
+                ValueCell.update(spheres.values.dDoubleSided, newProps.doubleSided)
+                spheres.state.visible = newProps.visible
+                spheres.state.depthMask = newProps.depthMask
 
                 currentProps = newProps
                 return true

+ 1 - 1
src/mol-geo/representation/volume/index.ts

@@ -5,7 +5,7 @@
  */
 
 import { Task } from 'mol-task'
-import { RenderObject } from 'mol-gl/scene';
+import { RenderObject } from 'mol-gl/render-object';
 import { RepresentationProps, Representation } from '..';
 import { VolumeData } from 'mol-model/volume';
 

+ 35 - 27
src/mol-geo/representation/volume/surface.ts

@@ -10,11 +10,13 @@ import { Task } from 'mol-task'
 import { computeMarchingCubes } from '../../util/marching-cubes/algorithm';
 import { Mesh } from '../../shape/mesh';
 import { VolumeElementRepresentation } from '.';
-import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/scene';
+import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object';
 import { fillSerial } from 'mol-gl/renderable/util';
-import { ValueCell } from 'mol-util';
+import { ValueCell, defaults } from 'mol-util';
 import { Mat4 } from 'mol-math/linear-algebra';
 import { createUniformColor } from '../../util/color-data';
+import { getMeshData } from '../../util/mesh-data';
+import { RenderableState, MeshValues } from 'mol-gl/renderable';
 
 export function computeVolumeSurface(volume: VolumeData, isoValue: VolumeIsoValue) {
     return Task.create<Mesh>('Volume Surface', async ctx => {
@@ -54,36 +56,42 @@ export default function Surface(): VolumeElementRepresentation<SurfaceProps> {
         create(volume: VolumeData, props: SurfaceProps = {}) {
             return Task.create('Point.create', async ctx => {
                 renderObjects.length = 0 // clear
-                curProps = { ...DefaultSurfaceProps, ...props }
-                const { alpha, visible, flatShaded, flipSided, doubleSided, depthMask } = curProps
+                props = { ...DefaultSurfaceProps, ...props }
 
                 const mesh = await computeVolumeSurface(volume, curProps.isoValue).runAsChild(ctx)
-                if (!flatShaded) {
+                if (!props.flatShaded) {
                     Mesh.computeNormalsImmediate(mesh)
                 }
 
-                surface = createMeshRenderObject({
-                    objectId: 0,
-                    alpha,
-                    visible,
-                    depthMask,
-
-                    position: mesh.vertexBuffer,
-                    normal: mesh.normalBuffer,
-                    id: ValueCell.create(fillSerial(new Float32Array(mesh.vertexCount / 3))),
-                    color: createUniformColor({ value: 0x7ec0ee }),
-                    transform: ValueCell.create(new Float32Array(Mat4.identity())),
-                    index: mesh.indexBuffer,
-
-                    instanceCount: 1,
-                    indexCount: mesh.triangleCount,
-                    elementCount: mesh.triangleCount,
-                    positionCount: mesh.vertexCount / 3,
-
-                    flatShaded,
-                    doubleSided,
-                    flipSided
-                })
+                const instanceCount = 1
+                const color = createUniformColor({ value: 0x7ec0ee })
+
+                const values: MeshValues = {
+                    ...getMeshData(mesh),
+                    aTransform: ValueCell.create(new Float32Array(Mat4.identity())),
+                    aInstanceId: ValueCell.create(fillSerial(new Float32Array(instanceCount))),
+                    ...color,
+
+                    uAlpha: ValueCell.create(defaults(props.alpha, 1.0)),
+                    uObjectId: ValueCell.create(0),
+                    uInstanceCount: ValueCell.create(instanceCount),
+                    uElementCount: ValueCell.create(mesh.triangleCount),
+
+                    elements: mesh.indexBuffer,
+
+                    drawCount: ValueCell.create(mesh.triangleCount * 3),
+                    instanceCount: ValueCell.create(instanceCount),
+
+                    dDoubleSided: ValueCell.create(defaults(props.doubleSided, true)),
+                    dFlatShaded: ValueCell.create(defaults(props.flatShaded, true)),
+                    dFlipSided: ValueCell.create(false),
+                }
+                const state: RenderableState = {
+                    depthMask: defaults(props.depthMask, true),
+                    visible: defaults(props.visible, true)
+                }
+
+                surface = createMeshRenderObject(values, state)
                 renderObjects.push(surface)
             })
         },

+ 3 - 3
src/mol-geo/shape/mesh.ts

@@ -21,9 +21,9 @@ export interface Mesh {
     /** Index buffer as array of vertex index triplets wrapped in a value cell */
     indexBuffer: ValueCell<Uint32Array>,
     /** Normal buffer as array of xyz values for each vertex wrapped in a value cell */
-    normalBuffer: ValueCell<Float32Array | undefined>,
+    normalBuffer: ValueCell<Float32Array>,
     /** Id buffer as array of ids for each vertex wrapped in a value cell */
-    idBuffer: ValueCell<Float32Array | undefined>,
+    idBuffer: ValueCell<Float32Array>,
 
     /** Flag indicating if normals are computed for the current set of vertices */
     normalsComputed: boolean,
@@ -36,7 +36,7 @@ export namespace Mesh {
     export function computeNormalsImmediate(surface: Mesh) {
         if (surface.normalsComputed) return;
 
-        const normals = surface.normalBuffer.ref.value && surface.normalBuffer.ref.value.length >= surface.vertexCount * 3
+        const normals = surface.normalBuffer.ref.value.length >= surface.vertexCount * 3
             ? surface.normalBuffer.ref.value : new Float32Array(surface.vertexBuffer.ref.value.length);
 
         const v = surface.vertexBuffer.ref.value, triangles = surface.indexBuffer.ref.value;

+ 3 - 4
src/mol-geo/shape/vertex-map.ts

@@ -11,12 +11,11 @@ import { Mesh } from './mesh';
 interface VertexMap {
     idCount: number,
     offsetCount: number,
-    ids: Helpers.NumberArray | undefined
+    ids: Helpers.NumberArray
     offsets: Uint32Array,
 }
 
-function createOffsets(idCount: number, ids: Helpers.NumberArray | undefined) {
-    if (!ids) return new Uint32Array(0)
+function createOffsets(idCount: number, ids: Helpers.NumberArray) {
     const offsets = ChunkedArray.create(Uint32Array, 1, 1024, 2048);
     let prevId = ids[0]
     ChunkedArray.add(offsets, 0)
@@ -31,7 +30,7 @@ function createOffsets(idCount: number, ids: Helpers.NumberArray | undefined) {
 }
 
 namespace VertexMap {
-    export function create(idCount: number, offsetCount: number, ids: Helpers.NumberArray | undefined, offsets: Uint32Array): VertexMap {
+    export function create(idCount: number, offsetCount: number, ids: Helpers.NumberArray, offsets: Uint32Array): VertexMap {
         return {
             idCount,
             offsetCount,

+ 43 - 17
src/mol-geo/util/color-data.ts

@@ -5,24 +5,34 @@
  */
 
 import { ValueCell } from 'mol-util';
-import { TextureImage, createColorTexture } from 'mol-gl/renderable/util';
+import { TextureImage, createColorTexture, emptyTexture } from 'mol-gl/renderable/util';
 import { Color } from 'mol-util/color';
 import VertexMap from '../shape/vertex-map';
+import { Vec2, Vec3 } from 'mol-math/linear-algebra';
 
-export type UniformColor = { type: 'uniform', data: number[] }
-export type AttributeColor = { type: 'attribute', data: ValueCell<Float32Array> }
-export type InstanceColor = { type: 'instance', data: ValueCell<TextureImage> }
-export type ElementColor = { type: 'element', data: ValueCell<TextureImage> }
-export type ElementInstanceColor = { type: 'element-instance', data: ValueCell<TextureImage> }
-export type ColorData = UniformColor | AttributeColor | InstanceColor | ElementColor | ElementInstanceColor
+export type ColorType = 'uniform' | 'attribute' | 'instance' | 'element' | 'elementInstance'
+
+export type ColorData = {
+    uColor: ValueCell<Vec3>,
+    aColor: ValueCell<Float32Array>,
+    tColor: ValueCell<TextureImage>,
+    uColorTexSize: ValueCell<Vec2>,
+    dColorType: ValueCell<string>,
+}
 
 export interface UniformColorProps {
     value: Color
 }
 
 /** Creates color uniform */
-export function createUniformColor(props: UniformColorProps): UniformColor {
-    return { type: 'uniform', data: Color.toRgbNormalized(props.value) }
+export function createUniformColor(props: UniformColorProps): ColorData {
+    return {
+        uColor: ValueCell.create(Color.toRgbNormalized(props.value) as Vec3),
+        aColor: ValueCell.create(new Float32Array(0)),
+        tColor: ValueCell.create(emptyTexture),
+        uColorTexSize: ValueCell.create(Vec2.zero()),
+        dColorType: ValueCell.create('uniform'),
+    }
 }
 
 export interface AttributeColorProps {
@@ -31,7 +41,7 @@ export interface AttributeColorProps {
 }
 
 /** Creates color attribute with color for each element (i.e. shared across instances/units) */
-export function createAttributeColor(props: AttributeColorProps): AttributeColor {
+export function createAttributeColor(props: AttributeColorProps): ColorData {
     const { colorFn, vertexMap } = props
     const { idCount, offsetCount, offsets } = vertexMap
     const colors = new Float32Array(idCount * 3);
@@ -43,7 +53,23 @@ export function createAttributeColor(props: AttributeColorProps): AttributeColor
             Color.toArrayNormalized(hexColor, colors, i * 3)
         }
     }
-    return { type: 'attribute', data: ValueCell.create(colors) }
+    return {
+        uColor: ValueCell.create(Vec3.zero()),
+        aColor: ValueCell.create(colors),
+        tColor: ValueCell.create(emptyTexture),
+        uColorTexSize: ValueCell.create(Vec2.zero()),
+        dColorType: ValueCell.create('attribute'),
+    }
+}
+
+export function createTextureColor(colors: TextureImage, type: ColorType): ColorData {
+    return {
+        uColor: ValueCell.create(Vec3.zero()),
+        aColor: ValueCell.create(new Float32Array(0)),
+        tColor: ValueCell.create(colors),
+        uColorTexSize: ValueCell.create(Vec2.create(colors.width, colors.height)),
+        dColorType: ValueCell.create(type),
+    }
 }
 
 export interface InstanceColorProps {
@@ -52,13 +78,13 @@ export interface InstanceColorProps {
 }
 
 /** Creates color texture with color for each instance/unit */
-export function createInstanceColor(props: InstanceColorProps): InstanceColor {
+export function createInstanceColor(props: InstanceColorProps): ColorData {
     const { colorFn, instanceCount} = props
     const colors = createColorTexture(instanceCount)
     for (let i = 0; i < instanceCount; i++) {
         Color.toArray(colorFn(i), colors.array, i * 3)
     }
-    return { type: 'instance', data: ValueCell.create(colors) }
+    return createTextureColor(colors, 'instance')
 }
 
 export interface ElementColorProps {
@@ -67,14 +93,14 @@ export interface ElementColorProps {
 }
 
 /** Creates color texture with color for each element (i.e. shared across instances/units) */
-export function createElementColor(props: ElementColorProps): ElementColor {
+export function createElementColor(props: ElementColorProps): ColorData {
     const { colorFn, vertexMap } = props
     const elementCount = vertexMap.offsetCount - 1
     const colors = createColorTexture(elementCount)
     for (let i = 0, il = elementCount; i < il; ++i) {
         Color.toArray(colorFn(i), colors.array, i * 3)
     }
-    return { type: 'element', data: ValueCell.create(colors) }
+    return createTextureColor(colors, 'element')
 }
 
 export interface ElementInstanceColorProps {
@@ -84,7 +110,7 @@ export interface ElementInstanceColorProps {
 }
 
 /** Creates color texture with color for each element instance (i.e. for each unit) */
-export function createElementInstanceColor(props: ElementInstanceColorProps): ElementInstanceColor {
+export function createElementInstanceColor(props: ElementInstanceColorProps): ColorData {
     const { colorFn, instanceCount, vertexMap } = props
     const elementCount = vertexMap.offsetCount - 1
     const count = instanceCount * elementCount
@@ -96,7 +122,7 @@ export function createElementInstanceColor(props: ElementInstanceColorProps): El
             colorOffset += 3
         }
     }
-    return { type: 'element-instance', data: ValueCell.create(colors) }
+    return createTextureColor(colors, 'elementInstance')
 }
 
 /** Create color attribute or texture, depending on the vertexMap */

+ 2 - 2
src/mol-geo/util/marching-cubes/algorithm.ts

@@ -78,12 +78,12 @@ class MarchingCubesComputation {
             triangleCount: this.state.triangleCount,
             vertexBuffer: os ? ValueCell.update(os.vertexBuffer, vb) : ValueCell.create(vb),
             indexBuffer: os ? ValueCell.update(os.indexBuffer, ib) : ValueCell.create(ib),
-            normalBuffer: os ? os.normalBuffer : ValueCell.create(void 0),
+            normalBuffer: os ? os.normalBuffer : ValueCell.create(new Float32Array(0)),
             idBuffer: this.state.assignIds
                 ? os && os.idBuffer
                     ? ValueCell.update(os.idBuffer, ChunkedArray.compact(this.state.idBuffer) as Float32Array)
                     : ValueCell.create(ChunkedArray.compact(this.state.idBuffer) as Float32Array)
-                : ValueCell.create(void 0),
+                : ValueCell.create(new Float32Array(0)),
             normalsComputed: false
         }
 

+ 22 - 0
src/mol-geo/util/mesh-data.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 { ValueCell } from 'mol-util/value-cell'
+import { Mesh } from '../shape/mesh';
+
+type MeshData = {
+    aPosition: ValueCell<Float32Array>,
+    aNormal: ValueCell<Float32Array>,
+    aElementId: ValueCell<Float32Array>,
+}
+
+export function getMeshData(mesh: Mesh): MeshData {
+    return {
+        aPosition: mesh.vertexBuffer,
+        aNormal: mesh.normalBuffer,
+        aElementId: mesh.idBuffer,
+    }
+}

+ 18 - 7
src/mol-geo/util/size-data.ts

@@ -7,16 +7,23 @@
 import { ValueCell } from 'mol-util';
 import VertexMap from '../shape/vertex-map';
 
-export type UniformSize = { type: 'uniform', value: number }
-export type AttributeSize = { type: 'attribute', value: ValueCell<Float32Array> }
-export type SizeData = UniformSize | AttributeSize
+export type SizeData = {
+    uSize: ValueCell<number>,
+    aSize: ValueCell<Float32Array>,
+    dSizeType: ValueCell<string>,
+}
+
 export interface UniformSizeProps {
     value: number
 }
 
 /** Creates size uniform */
-export function createUniformSize(props: UniformSizeProps): UniformSize {
-    return { type: 'uniform', value: props.value }
+export function createUniformSize(props: UniformSizeProps): SizeData {
+    return {
+        uSize: ValueCell.create(props.value),
+        aSize: ValueCell.create(new Float32Array(0)),
+        dSizeType: ValueCell.create('uniform'),
+    }
 }
 
 export interface AttributeSizeProps {
@@ -25,7 +32,7 @@ export interface AttributeSizeProps {
 }
 
 /** Creates size attribute with size for each element (i.e. shared across indtances/units) */
-export function createAttributeSize(props: AttributeSizeProps): AttributeSize {
+export function createAttributeSize(props: AttributeSizeProps): SizeData {
     const { sizeFn, vertexMap } = props
     const { idCount, offsetCount, offsets } = vertexMap
     const sizes = new Float32Array(idCount);
@@ -37,5 +44,9 @@ export function createAttributeSize(props: AttributeSizeProps): AttributeSize {
             sizes[i] = size
         }
     }
-    return { type: 'attribute', value: ValueCell.create(sizes) }
+    return {
+        uSize: ValueCell.create(0),
+        aSize: ValueCell.create(sizes),
+        dSizeType: ValueCell.create('attribute'),
+    }
 }

+ 1 - 0
src/mol-gl/TODO

@@ -0,0 +1 @@
+- handle webgl loose context

+ 32 - 21
src/mol-gl/_spec/renderer.spec.ts

@@ -11,11 +11,13 @@ import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { ValueCell } from 'mol-util';
 
 import Renderer from '../renderer';
-import { createPointRenderObject } from '../scene';
 import { fillSerial } from '../renderable/util';
 import { createUniformColor } from 'mol-geo/util/color-data';
 import { createUniformSize } from 'mol-geo/util/size-data';
 import { createContext } from '../webgl/context';
+import { RenderableState } from '../renderable';
+import { createPointRenderObject } from '../render-object';
+import { PointValues } from '../renderable/point';
 
 // function writeImage(gl: WebGLRenderingContext, width: number, height: number) {
 //     const pixels = new Uint8Array(width * height * 4)
@@ -40,31 +42,40 @@ function createRenderer(gl: WebGLRenderingContext) {
 }
 
 function createPoints() {
-    const position = ValueCell.create(new Float32Array([0, -1, 0, -1, 0, 0, 1, 1, 0]))
-    const id = ValueCell.create(fillSerial(new Float32Array(3)))
+    const aPosition = ValueCell.create(new Float32Array([0, -1, 0, -1, 0, 0, 1, 1, 0]))
+    const aElementId = ValueCell.create(fillSerial(new Float32Array(3)))
+    const aInstanceId = ValueCell.create(fillSerial(new Float32Array(1)))
     const color = createUniformColor({ value: 0xFF0000 })
     const size = createUniformSize({ value: 1 })
 
-    const transform = ValueCell.create(new Float32Array(16))
+    const aTransform = ValueCell.create(new Float32Array(16))
     const m4 = Mat4.identity()
-    Mat4.toArray(m4, transform.ref.value, 0)
-
-    return createPointRenderObject({
-        objectId: 0,
-        alpha: 1.0,
+    Mat4.toArray(m4, aTransform.ref.value, 0)
+
+    const values: PointValues = {
+        aPosition,
+        aElementId,
+        aTransform,
+        aInstanceId,
+        ...color,
+        ...size,
+
+        uAlpha: ValueCell.create(1.0),
+        uObjectId: ValueCell.create(0),
+        uInstanceCount: ValueCell.create(1),
+        uElementCount: ValueCell.create(3),
+
+        drawCount: ValueCell.create(3),
+        instanceCount: ValueCell.create(1),
+
+        dPointSizeAttenuation: ValueCell.create(true)
+    }
+    const state: RenderableState = {
         visible: true,
         depthMask: true,
+    }
 
-        position,
-        id,
-        color,
-        size,
-        transform,
-
-        instanceCount: 1,
-        elementCount: 3,
-        positionCount: 3
-    })
+    return createPointRenderObject(values, state)
 }
 
 describe('renderer', () => {
@@ -95,8 +106,8 @@ describe('renderer', () => {
         const points = createPoints()
 
         renderer.add(points)
-        expect(ctx.bufferCount).toBe(4);
-        expect(ctx.textureCount).toBe(0);
+        expect(ctx.bufferCount).toBe(6);
+        expect(ctx.textureCount).toBe(1);
         expect(ctx.vaoCount).toBe(1);
         expect(ctx.programCache.count).toBe(1);
         expect(ctx.shaderCache.count).toBe(2);

+ 33 - 0
src/mol-gl/render-object.ts

@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { PointRenderable, MeshRenderable, RenderableState } from './renderable'
+import { RenderableValues } from './renderable/schema';
+import { idFactory } from 'mol-util/id-factory';
+import { Context } from './webgl/context';
+import { MeshValues } from './renderable/mesh';
+import { PointValues } from './renderable/point';
+
+const getNextId = idFactory(0, 0x7FFFFFFF)
+
+export interface BaseRenderObject { id: number, type: string, values: RenderableValues, state: RenderableState }
+export interface MeshRenderObject extends BaseRenderObject { type: 'mesh', values: MeshValues }
+export interface PointRenderObject extends BaseRenderObject { type: 'point', values: PointValues }
+export type RenderObject = MeshRenderObject | PointRenderObject
+
+export function createMeshRenderObject(values: MeshValues, state: RenderableState): MeshRenderObject {
+    return { id: getNextId(), type: 'mesh', values, state }
+}
+export function createPointRenderObject(values: PointValues, state: RenderableState): PointRenderObject {
+    return { id: getNextId(), type: 'point', values, state }
+}
+
+export function createRenderable(ctx: Context, o: RenderObject) {
+    switch (o.type) {
+        case 'mesh': return MeshRenderable(ctx, o.values, o.state)
+        case 'point': return PointRenderable(ctx, o.values, o.state)
+    }
+}

+ 8 - 12
src/mol-gl/renderable.ts

@@ -4,27 +4,23 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import PointRenderable from './renderable/point'
-import MeshRenderable from './renderable/mesh'
 import { Program } from './webgl/program';
+import { RenderableValues } from './renderable/schema';
 
-export type BaseProps = {
-    objectId: number
-    alpha: number
+export type RenderableState = {
     visible: boolean
     depthMask: boolean
-
-    flatShaded?: boolean
-    doubleSided?: boolean
-    flipSided?: boolean
 }
 
-export interface Renderable<T> {
+export interface Renderable<T extends RenderableValues> {
     draw: () => void
+    values: T
+    state: RenderableState
     name: string
     program: Program
-    update: (newProps: T) => void
+    update: () => void
     dispose: () => void
 }
 
-export { PointRenderable, MeshRenderable }
+export { PointRenderable, PointSchema, PointValues } from './renderable/point'
+export { MeshRenderable, MeshSchema, MeshValues } from './renderable/mesh'

+ 35 - 93
src/mol-gl/renderable/mesh.ts

@@ -4,99 +4,41 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ValueCell } from 'mol-util/value-cell'
-import { ColorData } from 'mol-geo/util/color-data';
-
-import { Renderable, BaseProps } from '../renderable'
-import { getBaseDefs, getBaseValues, getBaseDefines, updateBaseValues } from './util'
-import { MeshShaderCode, addShaderDefines } from '../shader-code'
+import { Renderable, RenderableState } from '../renderable'
 import { Context } from '../webgl/context';
-import { createRenderItem, RenderItemProps, RenderItemState } from '../webgl/render-item';
-import { deepEqual } from 'mol-util';
-
-type Mesh = 'mesh'
-
-namespace Mesh {
-    export type Props = {
-        position: ValueCell<Float32Array>
-        normal: ValueCell<Float32Array | undefined>
-        id: ValueCell<Float32Array>
-
-        color: ColorData
-        transform: ValueCell<Float32Array>
-        index: ValueCell<Uint32Array>
-
-        indexCount: number
-        instanceCount: number
-        elementCount: number
-        positionCount: number
-    } & BaseProps
-
-    function getDefs(props: Props) {
-        const defines = getBaseDefines(props)
-        if (props.flatShaded) defines.FLAT_SHADED = ''
-        if (props.doubleSided) defines.DOUBLE_SIDED = ''
-        if (props.flipSided) defines.FLIP_SIDED = ''
-
-        const defs: RenderItemProps = {
-            ...getBaseDefs(props),
-            shaderCode: addShaderDefines(defines, MeshShaderCode),
-            drawMode: 'triangles',
-            elementsKind: 'uint32'
-        }
-        return defs
-    }
-
-    function getVals(props: Props) {
-        const vals: RenderItemState = {
-            ...getBaseValues(props),
-            drawCount: ValueCell.create(props.indexCount * 3),
-            instanceCount: ValueCell.create(props.instanceCount),
-            elements: props.index.ref.value
-        }
-        return vals
-    }
-
-    function updateVals(vals: RenderItemState, props: Props) {
-        updateBaseValues(vals, props)
-        if (props.instanceCount !== vals.instanceCount.ref.value) {
-            ValueCell.update(vals.instanceCount, props.instanceCount)
-        }
-        const drawCount = props.indexCount * 3
-        if (drawCount !== vals.drawCount.ref.value) {
-            ValueCell.update(vals.drawCount, drawCount)
-        }
-    }
-
-    export function create(ctx: Context, props: Props): Renderable<Props> {
-        let curDefs = getDefs(props)
-        let curVals = getVals(props)
-        let renderItem = createRenderItem(ctx, curDefs, curVals)
-
-        return {
-            draw: () => {
-                renderItem.draw()
-            },
-            name: 'mesh',
-            get program () { return renderItem.program },
-            update: (newProps: Props) => {
-                const newDefs = getDefs(props)
-                if (deepEqual(curDefs, newDefs)) {
-                    updateVals(curVals, props)
-                    renderItem.update()
-                } else {
-                    console.log('mesh defs changed, destroy and rebuild render-item')
-                    renderItem.destroy()
-                    curVals = getVals(props)
-                    curDefs = newDefs
-                    renderItem = createRenderItem(ctx, curDefs, curVals)
-                }
-            },
-            dispose: () => {
-                renderItem.destroy()
-            }
+import { createRenderItem } from '../webgl/render-item';
+import { GlobalUniformSchema, BaseSchema, AttributeSpec, ElementsSpec, DefineSpec, Values } from '../renderable/schema';
+import { MeshShaderCode } from '../shader-code';
+
+export const MeshSchema = {
+    ...BaseSchema,
+    aNormal: AttributeSpec('float32', 3, 0),
+    elements: ElementsSpec('uint32'),
+    dFlatShaded: DefineSpec('boolean'),
+    dDoubleSided: DefineSpec('boolean'),
+    dFlipSided: DefineSpec('boolean'),
+}
+export type MeshSchema = typeof MeshSchema
+export type MeshValues = Values<MeshSchema>
+
+export function MeshRenderable(ctx: Context, values: MeshValues, state: RenderableState): Renderable<MeshValues> {
+    const schema = { ...GlobalUniformSchema, ...MeshSchema }
+    const schaderCode = MeshShaderCode
+    const renderItem = createRenderItem(ctx, 'triangles', schaderCode, schema, values)
+
+    return {
+        draw: () => {
+            renderItem.draw()
+        },
+        get values () { return values },
+        get state () { return state },
+        name: 'mesh',
+        get program () { return renderItem.program },
+        update: () => {
+            renderItem.update()
+        },
+        dispose: () => {
+            renderItem.destroy()
         }
     }
-}
-
-export default Mesh
+}

+ 34 - 77
src/mol-gl/renderable/point.ts

@@ -4,83 +4,40 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ValueCell } from 'mol-util/value-cell'
-
-import { Renderable, BaseProps } from '../renderable'
-import { getBaseValues, getBaseDefs, getBaseDefines } from './util'
-import { PointShaderCode, addShaderDefines } from '../shader-code'
-import { ColorData } from 'mol-geo/util/color-data';
-import { SizeData } from 'mol-geo/util/size-data';
+import { Renderable, RenderableState } from '../renderable'
 import { Context } from '../webgl/context';
-import { createRenderItem, RenderItemState, RenderItemProps } from '../webgl/render-item';
-
-type Point = 'point'
-
-namespace Point {
-    export type Props = {
-        position: ValueCell<Float32Array>
-        id: ValueCell<Float32Array>
-
-        size: SizeData
-        color: ColorData
-        transform: ValueCell<Float32Array>
-
-        instanceCount: number
-        elementCount: number
-        positionCount: number,
-
-        usePointSizeAttenuation?: boolean
-    } & BaseProps
-
-    function getDefs(props: Props) {
-        const defines = getBaseDefines(props)
-        if (props.usePointSizeAttenuation) defines.POINT_SIZE_ATTENUATION = ''
-
-        const defs: RenderItemProps = {
-            ...getBaseDefs(props),
-            shaderCode: addShaderDefines(defines, PointShaderCode),
-            drawMode: 'points'
-        }
-        return defs
-    }
-
-    function getVals(props: Props) {
-        const vals: RenderItemState = {
-            ...getBaseValues(props),
-            drawCount: ValueCell.create(props.positionCount),
-            instanceCount: ValueCell.create(props.instanceCount)
-        }
-        return vals
-    }
-
-    function getRenderItem(ctx: Context, props: Props) {
-        const defs = getDefs(props)
-        const vals = getVals(props)
-        return createRenderItem(ctx, defs, vals)
-    }
-
-    export function create<T = Props>(ctx: Context, props: Props): Renderable<Props> {
-        // const defs = getDefs(props)
-
-        let renderItem = getRenderItem(ctx, props)
-        // let curProps = props
-
-        return {
-            draw: () => {
-                renderItem.draw()
-            },
-            name: 'point',
-            get program () { return renderItem.program },
-            update: (newProps: Props) => {
-                console.log('Updating point renderable')
-                renderItem.destroy()
-                renderItem = getRenderItem(ctx, { ...props, ...newProps })
-            },
-            dispose: () => {
-                renderItem.destroy()
-            }
+import { createRenderItem } from '../webgl/render-item';
+import { GlobalUniformSchema, BaseSchema, AttributeSpec, UniformSpec, DefineSpec, Values } from '../renderable/schema';
+import { PointShaderCode } from '../shader-code';
+
+export const PointSchema = {
+    ...BaseSchema,
+    aSize: AttributeSpec('float32', 1, 0),
+    uSize: UniformSpec('f'),
+    dSizeType: DefineSpec('string', ['uniform', 'attribute']),
+    dPointSizeAttenuation: DefineSpec('boolean'),
+}
+export type PointSchema = typeof PointSchema
+export type PointValues = Values<PointSchema>
+
+export function PointRenderable(ctx: Context, values: PointValues, state: RenderableState): Renderable<PointValues> {
+    const schema = { ...GlobalUniformSchema, ...PointSchema }
+    const schaderCode = PointShaderCode
+    const renderItem = createRenderItem(ctx, 'points', schaderCode, schema, values)
+
+    return {
+        draw: () => {
+            renderItem.draw()
+        },
+        get values () { return values },
+        get state () { return state },
+        name: 'point',
+        get program () { return renderItem.program },
+        update: () => {
+            renderItem.update()
+        },
+        dispose: () => {
+            renderItem.destroy()
         }
     }
-}
-
-export default Point
+}

+ 131 - 0
src/mol-gl/renderable/schema.ts

@@ -0,0 +1,131 @@
+/**
+ * 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 { ArrayKind, BufferItemSize, ElementsKind } from '../webgl/buffer';
+import { UniformKind } from '../webgl/uniform';
+import { DefineKind } from '../shader-code';
+import { Vec2, Vec3, Vec4, Mat3, Mat4 } from 'mol-math/linear-algebra';
+import { TextureImage } from './util';
+
+export type ValueKindType = {
+    'number': number
+    'string': string
+    'any': any
+}
+export type ValueKind = keyof ValueKindType
+
+//
+
+export type KindValue = {
+    'f': number
+    'i': number
+    'v2': Vec2
+    'v3': Vec3
+    'v4': Vec4
+    'm3': Mat3
+    'm4': Mat4
+    't2': number
+
+    'uint8': Uint8Array
+    'int8': Int8Array
+    'uint16': Uint16Array
+    'int16': Int16Array
+    'uint32': Uint32Array
+    'int32': Int32Array
+    'float32': Float32Array
+
+    'image': TextureImage
+
+    'number': number
+    'string': string
+    'boolean': boolean
+    'any': any
+}
+
+export type Values<S extends RenderableSchema> = { [k in keyof S]: ValueCell<KindValue[S[k]['kind']]> }
+
+//
+
+export type AttributeSpec<K extends ArrayKind> = { type: 'attribute', kind: K, itemSize: BufferItemSize, divisor: number }
+export function AttributeSpec<K extends ArrayKind>(kind: K, itemSize: BufferItemSize, divisor: number): AttributeSpec<K> {
+    return { type: 'attribute', kind, itemSize, divisor }
+}
+
+export type UniformSpec<K extends UniformKind> = { type: 'uniform', kind: K }
+export function UniformSpec<K extends UniformKind>(kind: K): UniformSpec<K> {
+    return { type: 'uniform', kind }
+}
+
+export type TextureSpec = { type: 'texture', kind: 'image' }
+export function TextureSpec(): TextureSpec {
+    return { type: 'texture', kind: 'image' }
+}
+
+export type ElementsSpec<K extends ElementsKind> = { type: 'elements', kind: K }
+export function ElementsSpec<K extends ElementsKind>(kind: K): ElementsSpec<K> {
+    return { type: 'elements', kind }
+}
+
+export type DefineSpec<K extends DefineKind> = { type: 'define', kind: K, options?: string[] }
+export function DefineSpec<K extends DefineKind>(kind: K, options?: string[]): DefineSpec<K> {
+    return { type: 'define', kind, options }
+}
+
+export type ValueSpec<K extends ValueKind> = { type: 'value', kind: K }
+export function ValueSpec<K extends ValueKind>(kind: K): ValueSpec<K> {
+    return { type: 'value', kind }
+}
+
+//
+
+export type RenderableSchema = {
+    [k: string]: (
+        AttributeSpec<ArrayKind> | UniformSpec<UniformKind> | TextureSpec |
+        ValueSpec<ValueKind> | DefineSpec<DefineKind> | ElementsSpec<ElementsKind>
+    )
+}
+export type RenderableValues = { [k: string]: ValueCell<any> }
+
+//
+
+export const GlobalUniformSchema = {
+    uModel: UniformSpec('m4'),
+    uView: UniformSpec('m4'),
+    uProjection: UniformSpec('m4'),
+    // uLightPosition: Uniform('v3'),
+    uLightColor: UniformSpec('v3'),
+    uLightAmbient: UniformSpec('v3'),
+
+    uPixelRatio: UniformSpec('f'),
+    uViewportHeight: UniformSpec('f'),
+}
+export type GlobalUniformSchema = typeof GlobalUniformSchema
+export type GlobalUniformValues = { [k in keyof GlobalUniformSchema]: ValueCell<any> }
+
+export const BaseSchema = {
+    aInstanceId: AttributeSpec('float32', 1, 1),
+    aPosition: AttributeSpec('float32', 3, 0),
+    aElementId: AttributeSpec('float32', 1, 0),
+    aTransform: AttributeSpec('float32', 16, 1),
+    aColor: AttributeSpec('float32', 3, 0),
+
+    uAlpha: UniformSpec('f'),
+    uObjectId: UniformSpec('i'),
+    uInstanceCount: UniformSpec('i'),
+    uElementCount: UniformSpec('i'),
+    uColorTexSize: UniformSpec('v2'),
+    uColor: UniformSpec('v3'),
+
+    tColor: TextureSpec(),
+
+    drawCount: ValueSpec('number'),
+    instanceCount: ValueSpec('number'),
+
+    dColorType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'element', 'element_instance']),
+}
+export type BaseSchema = typeof BaseSchema
+export type BaseValues = Values<BaseSchema>

+ 2 - 213
src/mol-gl/renderable/util.ts

@@ -4,16 +4,6 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ValueCell } from 'mol-util/value-cell'
-import { ColorData } from 'mol-geo/util/color-data';
-import { SizeData } from 'mol-geo/util/size-data';
-
-import { ShaderDefines } from '../shader-code';
-import { UniformDefs, UniformValues } from '../webgl/uniform';
-import { AttributeDefs, AttributeValues } from '../webgl/buffer';
-import { Vec3, Vec2 } from 'mol-math/linear-algebra';
-import { TextureDefs, TextureValues } from '../webgl/texture';
-
 export function calculateTextureInfo (n: number, itemSize: number) {
     const sqN = Math.sqrt(n * itemSize)
     let width = Math.ceil(sqN)
@@ -33,211 +23,10 @@ export function createColorTexture (n: number): TextureImage {
     return { array: new Uint8Array(length), width, height }
 }
 
-export 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
-}
-
-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 const emptyTexture = { array: new Uint8Array(0), width: 0, height: 0 }
 
 export function fillSerial<T extends Helpers.NumberArray> (array: T) {
     const n = array.length
     for (let i = 0; i < n; ++i) array[ i ] = i
     return array
-}
-
-interface BaseProps {
-    objectId: number,
-    instanceCount: number,
-    elementCount: number,
-    positionCount: number,
-    alpha: number,
-
-    position: ValueCell<Float32Array>
-    normal?: ValueCell<Float32Array | undefined>
-    id: ValueCell<Float32Array>
-    transform: ValueCell<Float32Array>
-
-    size?: SizeData
-    color: ColorData
-}
-
-export function getBaseUniformDefs(props: BaseProps) {
-    const uniformDefs: UniformDefs = {
-        model: 'm4',
-        view: 'm4',
-        projection: 'm4',
-
-        pixelRatio: 'f',
-        viewportHeight: 'f',
-
-        // lightPosition: 'v3',
-        lightColor: 'v3',
-        lightAmbient: 'v3',
-        alpha: 'f',
-
-        objectId: 'i',
-        instanceCount: 'i',
-        elementCount: 'i'
-    }
-    const color = props.color
-    if (color.type === 'instance' || color.type === 'element' || color.type === 'element-instance') {
-        uniformDefs.colorTexSize = 'v2'
-    } else if (color.type === 'uniform') {
-        uniformDefs.color = 'v3'
-    }
-    const size = props.size
-    if (size && size.type === 'uniform') {
-        uniformDefs.size = 'f'
-    }
-    return uniformDefs
-}
-
-export function getBaseUniformValues(props: BaseProps) {
-    const { objectId, instanceCount, elementCount, alpha } = props
-    const uniformValues: UniformValues = {
-        objectId: ValueCell.create(objectId),
-        instanceCount: ValueCell.create(instanceCount),
-        elementCount: ValueCell.create(elementCount),
-        alpha: ValueCell.create(alpha)
-    }
-    const color = props.color
-    if (color.type === 'instance' || color.type === 'element' || color.type === 'element-instance') {
-        const { width, height } = color.data.ref.value
-        uniformValues.colorTexSize = ValueCell.create(Vec2.create(width, height))
-    } else if (color.type === 'uniform') {
-        uniformValues.color = ValueCell.create(color.data as Vec3)
-    }
-    const size = props.size
-    if (size && size.type === 'uniform') {
-        uniformValues.size = ValueCell.create(size.value)
-    }
-    return uniformValues
-}
-
-export function getBaseAttributeDefs(props: BaseProps) {
-    const attributeDefs: AttributeDefs = {
-        instanceId: { kind: 'float32', itemSize: 1, divisor: 1 },
-        position: { kind: 'float32', itemSize: 3, divisor: 0 },
-        elementId: { kind: 'float32', itemSize: 1, divisor: 0 },
-        transform: { kind: 'float32', itemSize: 16, divisor: 1 },
-    }
-    if (props.normal && props.normal.ref.value) {
-        attributeDefs.normal = { kind: 'float32', itemSize: 3, divisor: 0 }
-    }
-    const color = props.color
-    if (color.type === 'attribute') {
-        attributeDefs.color = { kind: 'float32', itemSize: 3, divisor: 0 }
-    }
-    const size = props.size
-    if (size && size.type === 'attribute') {
-        attributeDefs.size = { kind: 'float32', itemSize: 1, divisor: 0 }
-    }
-    return attributeDefs
-}
-
-export function getBaseAttributeValues(props: BaseProps) {
-    const { instanceCount, position, id, normal, transform } = props
-    const instanceId = ValueCell.create(fillSerial(new Float32Array(instanceCount)))
-    const attributeValues: AttributeValues = {
-        instanceId: instanceId.ref.value,
-        position: position.ref.value,
-        elementId: id.ref.value,
-        transform: transform.ref.value
-    }
-    if (normal && normal.ref.value) {
-        attributeValues.normal = normal.ref.value
-    }
-    const color = props.color
-    if (color.type === 'attribute') {
-        attributeValues.color = color.data.ref.value
-    }
-    const size = props.size
-    if (size && size.type === 'attribute') {
-        attributeValues.size = size.value.ref.value
-    }
-    return attributeValues
-}
-
-export function getBaseTextureDefs(props: BaseProps) {
-    const textureDefs: TextureDefs = {}
-    const color = props.color
-    if (color.type === 'instance' || color.type === 'element' || color.type === 'element-instance') {
-        textureDefs.colorTex = true
-    }
-    return textureDefs
-}
-
-export function getBaseTextureValues(props: BaseProps) {
-    const textureValues: TextureValues = {}
-    const color = props.color
-    if (color.type === 'instance' || color.type === 'element' || color.type === 'element-instance') {
-        textureValues.colorTex = color.data.ref.value
-    }
-    return textureValues
-}
-
-export function getBaseDefines(props: BaseProps): ShaderDefines {
-    return {
-        ...getColorDefines(props.color),
-        ...(props.size ? getSizeDefines(props.size) : undefined)
-    }
-}
-
-export function getBaseDefs(props: BaseProps) {
-    return {
-        uniformDefs: getBaseUniformDefs(props),
-        attributeDefs: getBaseAttributeDefs(props),
-        textureDefs: getBaseTextureDefs(props),
-    }
-}
-
-export function getBaseValues(props: BaseProps) {
-    return {
-        uniformValues: getBaseUniformValues(props),
-        attributeValues: getBaseAttributeValues(props),
-        textureValues: getBaseTextureValues(props),
-    }
-}
-
-export interface BaseValues {
-    uniformValues: UniformValues
-    attributeValues: AttributeValues
-    textureValues: TextureValues
-}
-
-export function updateBaseValues(vals: BaseValues, props: BaseProps) {
-    ValueCell.update(vals.uniformValues.objectId, props.objectId)
-    ValueCell.update(vals.uniformValues.instanceCount, props.instanceCount)
-    ValueCell.update(vals.uniformValues.elementCount, props.elementCount)
-    ValueCell.update(vals.uniformValues.alpha, props.alpha)
-
-    const color = props.color
-    if (color.type === 'instance' || color.type === 'element' || color.type === 'element-instance') {
-        const { width, height } = color.data.ref.value
-        ValueCell.update(
-            vals.uniformValues.colorTexSize,
-            Vec2.set(vals.uniformValues.colorTexSize.ref.value as Vec2, width, height)
-        )
-    } else if (color.type === 'uniform') {
-        ValueCell.update(vals.uniformValues.color, color.data as Vec3)
-    }
-    const size = props.size
-    if (size && size.type === 'uniform') {
-        ValueCell.update(vals.uniformValues.size, size.value)
-    }
-}
+}

+ 21 - 19
src/mol-gl/renderer.ts

@@ -8,12 +8,14 @@
 import { Viewport } from 'mol-view/camera/util';
 import { Camera } from 'mol-view/camera/base';
 
-import Scene, { RenderObject } from './scene';
+import Scene from './scene';
 import { Context } from './webgl/context';
 import { Mat4, Vec3 } from 'mol-math/linear-algebra';
 import { Renderable } from './renderable';
 import { Color } from 'mol-util/color';
 import { ValueCell } from 'mol-util';
+import { RenderableValues, GlobalUniformValues } from './renderable/schema';
+import { RenderObject } from './render-object';
 
 export interface RendererStats {
     renderableCount: number
@@ -68,33 +70,33 @@ namespace Renderer {
         }
         setClearColor(clearColor)
 
-        const baseUniforms = {
-            model: ValueCell.create(Mat4.clone(model)),
-            view: ValueCell.create(Mat4.clone(camera.view)),
-            projection: ValueCell.create(Mat4.clone(camera.projection)),
+        const globalUniforms: GlobalUniformValues = {
+            uModel: ValueCell.create(Mat4.clone(model)),
+            uView: ValueCell.create(Mat4.clone(camera.view)),
+            uProjection: ValueCell.create(Mat4.clone(camera.projection)),
 
-            pixelRatio: ValueCell.create(pixelRatio),
-            viewportHeight: ValueCell.create(viewport.height),
+            uPixelRatio: ValueCell.create(pixelRatio),
+            uViewportHeight: ValueCell.create(viewport.height),
 
-            lightColor: ValueCell.create(Vec3.clone(lightColor)),
-            lightAmbient: ValueCell.create(Vec3.clone(lightAmbient))
+            uLightColor: ValueCell.create(Vec3.clone(lightColor)),
+            uLightAmbient: ValueCell.create(Vec3.clone(lightAmbient))
         }
 
         let currentProgramId = -1
-        const drawObject = (r: Renderable<any>, o: RenderObject) => {
-            if (o.props.visible) {
+        const drawObject = (r: Renderable<RenderableValues>) => {
+            if (r.state.visible) {
                 if (currentProgramId !== r.program.id) {
                     r.program.use()
-                    r.program.setUniforms(baseUniforms)
+                    r.program.setUniforms(globalUniforms)
                     currentProgramId = r.program.id
                 }
-                if (o.props.doubleSided) {
+                if (r.values.dDoubleSided.ref.value) {
                     gl.disable(gl.CULL_FACE)
                 } else {
                     gl.enable(gl.CULL_FACE)
                 }
 
-                if (o.props.flipSided) {
+                if (r.values.dFlipSided.ref.value) {
                     gl.frontFace(gl.CW)
                     gl.cullFace(gl.FRONT)
                 } else {
@@ -102,15 +104,15 @@ namespace Renderer {
                     gl.cullFace(gl.BACK)
                 }
 
-                gl.depthMask(o.props.depthMask)
+                gl.depthMask(r.state.depthMask)
 
                 r.draw()
             }
         }
 
         const draw = () => {
-            ValueCell.update(baseUniforms.view, camera.view)
-            ValueCell.update(baseUniforms.projection, camera.projection)
+            ValueCell.update(globalUniforms.uView, camera.view)
+            ValueCell.update(globalUniforms.uProjection, camera.projection)
 
             currentProgramId = -1
 
@@ -134,7 +136,7 @@ namespace Renderer {
                 scene.remove(o)
             },
             update: () => {
-                scene.forEach((r, o) => r.update(o.props))
+                scene.forEach((r, o) => r.update())
             },
             clear: () => {
                 scene.clear()
@@ -145,7 +147,7 @@ namespace Renderer {
             setViewport: (newViewport: Viewport) => {
                 Viewport.copy(viewport, newViewport)
                 gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height)
-                ValueCell.update(baseUniforms.viewportHeight, viewport.height)
+                ValueCell.update(globalUniforms.uViewportHeight, viewport.height)
             },
 
             get stats(): RendererStats {

+ 6 - 29
src/mol-gl/scene.ts

@@ -4,34 +4,11 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { PointRenderable, MeshRenderable, Renderable } from './renderable'
-
-import { ValueCell } from 'mol-util';
+import { Renderable } from './renderable'
 import { Context } from './webgl/context';
-import { idFactory } from 'mol-util/id-factory';
-
-const getNextId = idFactory(0, 0x7FFFFFFF)
-
-export type RenderData = { [k: string]: ValueCell<Helpers.TypedArray> }
+import { RenderableValues } from './renderable/schema';
+import { RenderObject, createRenderable } from './render-object';
 
-export interface BaseRenderObject { id: number, type: string, props: {} }
-export interface MeshRenderObject extends BaseRenderObject { type: 'mesh', props: MeshRenderable.Props }
-export interface PointRenderObject extends BaseRenderObject { type: 'point', props: PointRenderable.Props }
-export type RenderObject = MeshRenderObject | PointRenderObject
-
-export function createMeshRenderObject(props: MeshRenderable.Props): MeshRenderObject {
-    return { id: getNextId(), type: 'mesh', props }
-}
-export function createPointRenderObject(props: PointRenderable.Props): PointRenderObject {
-    return { id: getNextId(), type: 'point', props }
-}
-
-export function createRenderable(ctx: Context, o: RenderObject) {
-    switch (o.type) {
-        case 'mesh': return MeshRenderable.create(ctx, o.props)
-        case 'point': return PointRenderable.create(ctx, o.props)
-    }
-}
 
 interface Scene {
     add: (o: RenderObject) => void
@@ -45,7 +22,7 @@ interface Scene {
 
 namespace Scene {
     export function create(ctx: Context): Scene {
-        const renderableMap = new Map<RenderObject, Renderable<any>>()
+        const renderableMap = new Map<RenderObject, Renderable<RenderableValues>>()
 
         return {
             add: (o: RenderObject) => {
@@ -71,12 +48,12 @@ namespace Scene {
             },
             eachOpaque: (callbackFn: (value: Renderable<any>, key: RenderObject) => void) => {
                 renderableMap.forEach((r, o) => {
-                    if (o.props.alpha === 1) callbackFn(r, o)
+                    if (o.values.uAlpha.ref.value === 1) callbackFn(r, o)
                 })
             },
             eachTransparent: (callbackFn: (value: Renderable<any>, key: RenderObject) => void) => {
                 renderableMap.forEach((r, o) => {
-                    if (o.props.alpha < 1) callbackFn(r, o)
+                    if (o.values.uAlpha.ref.value < 1) callbackFn(r, o)
                 })
             },
             get count() {

+ 15 - 12
src/mol-gl/shader-code.ts

@@ -5,6 +5,12 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
+import { ValueCell } from 'mol-util';
+
+export type DefineKind = 'boolean' | 'string'
+export type DefineType = boolean | string
+export type DefineValues = { [k: string]: ValueCell<DefineType> }
+
 export interface ShaderCode {
     vert: string
     frag: string
@@ -20,25 +26,22 @@ export const MeshShaderCode: ShaderCode = {
     frag: require('mol-gl/shader/mesh.frag')
 }
 
-type ShaderDefine = (
-    'UNIFORM_COLOR' | 'ATTRIBUTE_COLOR' | 'INSTANCE_COLOR' | 'ELEMENT_COLOR' | 'ELEMENT_INSTANCE_COLOR' |
-    'UNIFORM_SIZE' | 'ATTRIBUTE_SIZE' |
-    'POINT_SIZE_ATTENUATION' |
-    'FLAT_SHADED' | 'DOUBLE_SIDED' | 'FLIP_SIDED'
-)
 export type ShaderDefines = {
-    [k in ShaderDefine]?: number|string
+    [k: string]: ValueCell<DefineType>
 }
 
 function getDefinesCode (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}`)
+        const define = defines[name]
+        const v = define.ref.value
+        if (v) {
+            if (typeof v === 'string') {
+                lines.push(`#define ${name}_${v}`)
+            } else {
+                lines.push(`#define ${name}`)
+            }
         }
     }
     return lines.join('\n') + '\n'

+ 3 - 3
src/mol-gl/shader/chunks/color-assign-material.glsl

@@ -1,5 +1,5 @@
-#if defined(UNIFORM_COLOR)
-    vec3 material = color;
-#elif defined(ATTRIBUTE_COLOR) || defined(INSTANCE_COLOR) || defined(ELEMENT_COLOR) || defined(ELEMENT_INSTANCE_COLOR)
+#if defined(dColorType_uniform)
+    vec3 material = uColor;
+#elif defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_element) || defined(dColorType_elementInstance)
     vec3 material = vColor;
 #endif

+ 8 - 8
src/mol-gl/shader/chunks/color-assign-varying.glsl

@@ -1,9 +1,9 @@
-#if defined(ATTRIBUTE_COLOR)
-    vColor = color;
-#elif defined(INSTANCE_COLOR)
-    vColor = read_vec3(colorTex, instanceId, colorTexSize);
-#elif defined(ELEMENT_COLOR)
-    vColor = read_vec3(colorTex, elementId, colorTexSize);
-#elif defined(ELEMENT_INSTANCE_COLOR)
-    vColor = read_vec3(colorTex, instanceId * float(elementCount) + elementId, colorTexSize);
+#if defined(dColorType_attribute)
+    vColor = aColor;
+#elif defined(dColorType_instance)
+    vColor = read_vec3(tColor, aInstanceId, uColorTexSize);
+#elif defined(dColorType_element)
+    vColor = read_vec3(tColor, aElementId, uColorTexSize);
+#elif defined(dColorType_elementInstance)
+    vColor = read_vec3(tColor, aInstanceId * float(uElementCount) + aElementId, uColorTexSize);
 #endif

+ 3 - 3
src/mol-gl/shader/chunks/color-frag-params.glsl

@@ -1,5 +1,5 @@
-#if defined(UNIFORM_COLOR)
-    uniform vec3 color;
-#elif defined(ATTRIBUTE_COLOR) || defined(INSTANCE_COLOR) || defined(ELEMENT_COLOR) || defined(ELEMENT_INSTANCE_COLOR)
+#if defined(dColorType_uniform)
+    uniform vec3 uColor;
+#elif defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_element) || defined(dColorType_elementInstance)
     varying vec3 vColor;
 #endif

+ 7 - 7
src/mol-gl/shader/chunks/color-vert-params.glsl

@@ -1,12 +1,12 @@
-#if defined(UNIFORM_COLOR)
-    uniform vec3 color;
-#elif defined(ATTRIBUTE_COLOR)
+#if defined(dColorType_uniform)
+    uniform vec3 uColor;
+#elif defined(dColorType_attribute)
     varying vec3 vColor;
-    attribute vec3 color;
-#elif defined(INSTANCE_COLOR) || defined(ELEMENT_COLOR) || defined(ELEMENT_INSTANCE_COLOR)
+    attribute vec3 aColor;
+#elif defined(dColorType_instance) || defined(dColorType_element) || defined(dColorType_elementInstance)
     varying vec3 vColor;
-    uniform vec2 colorTexSize;
-    uniform sampler2D colorTex;
+    uniform vec2 uColorTexSize;
+    uniform sampler2D tColor;
 #endif
 
 #pragma glslify: read_vec3 = require(../utils/read-from-texture.glsl)

+ 12 - 12
src/mol-gl/shader/mesh.frag

@@ -4,19 +4,19 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-#ifdef FLAT_SHADED
+#ifdef dFlatShaded
     #extension GL_OES_standard_derivatives : enable
 #endif
 
 precision highp float;
 
-// uniform vec3 lightPosition;
-uniform vec3 lightColor;
-uniform vec3 lightAmbient;
-uniform mat4 view;
-uniform float alpha;
+// uniform vec3 uLightPosition;
+uniform vec3 uLightColor;
+uniform vec3 uLightAmbient;
+uniform mat4 uView;
+uniform float uAlpha;
 
-#ifndef FLAT_SHADED
+#ifndef dFlatShaded
     varying vec3 vNormal;
 #endif
 varying vec3 vViewPosition;
@@ -45,21 +45,21 @@ void main() {
     vec3 V = normalize(vViewPosition); // eye direction
 
     // surface normal
-    #ifdef FLAT_SHADED
+    #ifdef dFlatShaded
         vec3 fdx = dFdx(vViewPosition);
         vec3 fdy = dFdy(vViewPosition);
         vec3 N = -normalize(cross(fdx, fdy));
     #else
         vec3 N = -normalize(vNormal);
-        #ifdef DOUBLE_SIDED
+        #ifdef dDoubleSided
             N = N * (float(gl_FrontFacing) * 2.0 - 1.0);
         #endif
     #endif
 
     // compute our diffuse & specular terms
     float specular = calculateSpecular(L, V, N, shininess) * specularScale;
-    vec3 diffuse = lightColor * calculateDiffuse(L, V, N, roughness, albedo);
-    vec3 ambient = lightAmbient;
+    vec3 diffuse = uLightColor * calculateDiffuse(L, V, N, roughness, albedo);
+    vec3 ambient = uLightAmbient;
 
     // add the lighting
     vec3 finalColor = material * (diffuse + ambient) + specular;
@@ -68,5 +68,5 @@ void main() {
     // gl_FragColor.a = 1.0;
     // gl_FragColor.rgb = vec3(1.0, 0.0, 0.0);
     gl_FragColor.rgb = finalColor;
-    gl_FragColor.a = alpha;
+    gl_FragColor.a = uAlpha;
 }

+ 16 - 16
src/mol-gl/shader/mesh.vert

@@ -6,21 +6,21 @@
 
 precision highp float;
 
-uniform mat4 projection, model, view;
+uniform mat4 uProjection, uModel, uView;
 
-uniform int objectId;
-uniform int instanceCount;
-uniform int elementCount;
+uniform int uObjectId;
+uniform int uInstanceCount;
+uniform int uElementCount;
 
 #pragma glslify: import('./chunks/color-vert-params.glsl')
 
-attribute vec3 position;
-attribute mat4 transform;
-attribute float instanceId;
-attribute float elementId;
+attribute vec3 aPosition;
+attribute mat4 aTransform;
+attribute float aInstanceId;
+attribute float aElementId;
 
-#ifndef FLAT_SHADED
-    attribute vec3 normal;
+#ifndef dFlatShaded
+    attribute vec3 aNormal;
     varying vec3 vNormal;
 #endif
 
@@ -32,15 +32,15 @@ varying vec3 vViewPosition;
 void main(){
     #pragma glslify: import('./chunks/color-assign-varying.glsl')
 
-    mat4 modelView = view * model * transform;
-    vec4 mvPosition = modelView * vec4(position, 1.0);
+    mat4 modelView = uView * uModel * aTransform;
+    vec4 mvPosition = modelView * vec4(aPosition, 1.0);
     vViewPosition = mvPosition.xyz;
-    gl_Position = projection * mvPosition;
+    gl_Position = uProjection * mvPosition;
 
-    #ifndef FLAT_SHADED
+    #ifndef dFlatShaded
         mat3 normalMatrix = transpose(inverse(mat3(modelView)));
-        vec3 transformedNormal = normalize(normalMatrix * normalize(normal));
-        #if defined(FLIP_SIDED) && !defined(DOUBLE_SIDED) // TODO checking DOUBLE_SIDED should not be required, ASR
+        vec3 transformedNormal = normalize(normalMatrix * normalize(aNormal));
+        #if defined(dFlipSided) && !defined(dDoubleSided) // TODO checking dDoubleSided should not be required, ASR
             transformedNormal = -transformedNormal;
         #endif
         vNormal = transformedNormal;

+ 2 - 2
src/mol-gl/shader/point.frag

@@ -6,11 +6,11 @@
 
 precision highp float;
 
-uniform float alpha;
+uniform float uAlpha;
 
 #pragma glslify: import('./chunks/color-frag-params.glsl')
 
 void main(){
     #pragma glslify: import('./chunks/color-assign-material.glsl')
-    gl_FragColor = vec4(material, alpha);
+    gl_FragColor = vec4(material, uAlpha);
 }

+ 26 - 20
src/mol-gl/shader/point.vert

@@ -6,39 +6,45 @@
 
 precision highp float;
 
-uniform mat4 projection, model, view;
+uniform mat4 uProjection, uModel, uView;
 
-uniform int objectId;
-uniform int instanceCount;
-uniform int elementCount;
+uniform int uObjectId;
+uniform int uInstanceCount;
+uniform int uElementCount;
 
-uniform float pixelRatio;
-uniform float viewportHeight;
+uniform float uPixelRatio;
+uniform float uViewportHeight;
 
 #pragma glslify: import('./chunks/color-vert-params.glsl')
 
-#if defined(UNIFORM_SIZE)
-    uniform float size;
-#elif defined(ATTRIBUTE_SIZE)
-    attribute float size;
+#if defined(dSizeType_uniform)
+    uniform float uSize;
+#elif defined(dSizeType_attribute)
+    attribute float aSize;
 #endif
 
-attribute vec3 position;
-attribute mat4 transform;
-attribute float instanceId;
-attribute float elementId;
+attribute vec3 aPosition;
+attribute mat4 aTransform;
+attribute float aInstanceId;
+attribute float aElementId;
 
 void main(){
     #pragma glslify: import('./chunks/color-assign-varying.glsl')
 
-    mat4 modelView = view * model * transform;
-    vec4 mvPosition = modelView * vec4(position, 1.0);
+    mat4 modelView = uView * uModel * aTransform;
+    vec4 mvPosition = modelView * vec4(aPosition, 1.0);
 
-    #ifdef POINT_SIZE_ATTENUATION
-        gl_PointSize = size * pixelRatio * ((viewportHeight / 2.0) / -mvPosition.z) * 5.0;
+    #if defined(dSizeType_uniform)
+        float size = uSize;
+    #elif defined(dSizeType_attribute)
+        float size = aSize;
+    #endif
+
+    #ifdef dPointSizeAttenuation
+        gl_PointSize = size * uPixelRatio * ((uViewportHeight / 2.0) / -mvPosition.z) * 5.0;
     #else
-        gl_PointSize = size * pixelRatio;
+        gl_PointSize = size * uPixelRatio;
     #endif
 
-    gl_Position = projection * mvPosition;
+    gl_Position = uProjection * mvPosition;
 }

+ 9 - 4
src/mol-gl/webgl/buffer.ts

@@ -5,6 +5,8 @@
  */
 
 import { Context } from './context'
+import { ValueCell } from 'mol-util';
+import { RenderableSchema } from '../renderable/schema';
 
 export type UsageHint = 'static' | 'dynamic' | 'stream'
 export type DataType = 'uint8' | 'int8' | 'uint16' | 'int16' | 'uint32' | 'int32' | 'float32'
@@ -146,7 +148,7 @@ export function createBuffer(ctx: Context, array: ArrayType, itemSize: BufferIte
 export type AttributeDefs = {
     [k: string]: { kind: ArrayKind, itemSize: BufferItemSize, divisor: number }
 }
-export type AttributeValues = { [k: string]: ArrayType }
+export type AttributeValues = { [k: string]: ValueCell<ArrayType> }
 export type AttributeBuffers = { [k: string]: AttributeBuffer }
 
 export interface AttributeBuffer extends Buffer {
@@ -179,10 +181,13 @@ export function createAttributeBuffer<T extends ArrayType, S extends BufferItemS
     }
 }
 
-export function createAttributeBuffers<T extends AttributeDefs>(ctx: Context, props: T, state: AttributeValues) {
+export function createAttributeBuffers(ctx: Context, schema: RenderableSchema, values: AttributeValues) {
     const buffers: AttributeBuffers = {}
-    Object.keys(props).forEach(k => {
-        buffers[k] = createAttributeBuffer(ctx, state[k], props[k].itemSize, props[k].divisor)
+    Object.keys(schema).forEach(k => {
+        const spec = schema[k]
+        if (spec.type === 'attribute') {
+            buffers[k] = createAttributeBuffer(ctx, values[k].ref.value, spec.itemSize, spec.divisor)
+        }
     })
     return buffers as AttributeBuffers
 }

+ 1 - 0
src/mol-gl/webgl/context.ts

@@ -38,6 +38,7 @@ type Extensions = {
     oesVertexArrayObject: OES_vertex_array_object | null
 }
 
+/** A WebGL context object, including the rendering context, resource caches and counts */
 export interface Context {
     gl: WebGLRenderingContext
     extensions: Extensions

+ 22 - 27
src/mol-gl/webgl/program.ts

@@ -6,11 +6,12 @@
 
 import { ShaderCode } from '../shader-code'
 import { Context } from './context';
-import { getUniformUpdaters, UniformDefs, UniformValues } from './uniform';
-import {AttributeDefs, AttributeBuffers } from './buffer';
-import { TextureId, TextureDefs, TextureUniformDefs, Textures } from './texture';
+import { getUniformUpdaters, getTextureUniformUpdaters, UniformValues } from './uniform';
+import { AttributeBuffers } from './buffer';
+import { TextureId, Textures } from './texture';
 import { createReferenceCache, ReferenceCache } from 'mol-util/reference-cache';
 import { idFactory } from 'mol-util/id-factory';
+import { RenderableSchema } from '../renderable/schema';
 
 const getNextProgramId = idFactory()
 
@@ -27,36 +28,31 @@ export interface Program {
 
 type AttributeLocations = { [k: string]: number }
 
-function getAttributeLocations(ctx: Context, program: WebGLProgram, attributeDefs: AttributeDefs) {
+function getAttributeLocations(ctx: Context, program: WebGLProgram, schema: RenderableSchema) {
     const { gl } = ctx
     const locations: AttributeLocations = {}
     gl.useProgram(program)
-    Object.keys(attributeDefs).forEach(k => {
-        const loc = gl.getAttribLocation(program, k)
-        if (loc === -1) {
-            console.info(`Could not get attribute location for '${k}'`)
+    Object.keys(schema).forEach(k => {
+        const spec = schema[k]
+        if (spec.type === 'attribute') {
+            const loc = gl.getAttribLocation(program, k)
+            if (loc === -1) {
+                console.info(`Could not get attribute location for '${k}'`)
+            }
+            locations[k] = loc
         }
-        locations[k] = loc
     })
     return locations
 }
 
-function getTextureUniformDefs(textureDefs: TextureDefs) {
-    const textureUniformDefs: TextureUniformDefs = {}
-    Object.keys(textureDefs).forEach(k => textureUniformDefs[k] = 't2')
-    return textureUniformDefs
-}
-
 export interface ProgramProps {
     shaderCode: ShaderCode,
-    uniformDefs: UniformDefs,
-    attributeDefs: AttributeDefs,
-    textureDefs: TextureDefs
+    schema: RenderableSchema
 }
 
 export function createProgram(ctx: Context, props: ProgramProps): Program {
     const { gl, shaderCache } = ctx
-    const { shaderCode, uniformDefs, attributeDefs, textureDefs } = props
+    const { shaderCode, schema } = props
 
     const program = gl.createProgram()
     if (program === null) {
@@ -70,10 +66,9 @@ export function createProgram(ctx: Context, props: ProgramProps): Program {
     fragShaderRef.value.attach(program)
     gl.linkProgram(program)
 
-    const uniformUpdaters = getUniformUpdaters(ctx, program, uniformDefs)
-    const attributeLocations = getAttributeLocations(ctx, program, attributeDefs)
-    const textureUniformDefs = getTextureUniformDefs(textureDefs)
-    const textureUniformUpdaters = getUniformUpdaters(ctx, program, textureUniformDefs)
+    const uniformUpdaters = getUniformUpdaters(ctx, program, schema)
+    const attributeLocations = getAttributeLocations(ctx, program, schema)
+    const textureUniformUpdaters = getTextureUniformUpdaters(ctx, program, schema)
 
     let destroyed = false
 
@@ -81,14 +76,14 @@ export function createProgram(ctx: Context, props: ProgramProps): Program {
         id: getNextProgramId(),
 
         use: () => {
-            Object.keys(uniformDefs).forEach(k => uniformUpdaters[k].clear())
-            Object.keys(textureUniformDefs).forEach(k => textureUniformUpdaters[k].clear())
+            Object.keys(uniformUpdaters).forEach(k => uniformUpdaters[k].clear())
+            Object.keys(textureUniformUpdaters).forEach(k => textureUniformUpdaters[k].clear())
             gl.useProgram(program)
         },
         setUniforms: (uniformValues: UniformValues) => {
             Object.keys(uniformValues).forEach(k => {
                 const uv = uniformValues[k]
-                if (uv !== undefined) uniformUpdaters[k].set(uv.ref.value, uv.ref.version)
+                if (uv !== undefined) uniformUpdaters[k].set(uv.ref.value)
             })
         },
         bindAttributes: (attribueBuffers: AttributeBuffers) => {
@@ -100,7 +95,7 @@ export function createProgram(ctx: Context, props: ProgramProps): Program {
         bindTextures: (textures: Textures) => {
             Object.keys(textures).forEach((k, i) => {
                 textures[k].bind(i as TextureId)
-                textureUniformUpdaters[k].set(i, i)
+                textureUniformUpdaters[k].set(i)
             })
         },
 

+ 40 - 46
src/mol-gl/webgl/render-item.ts

@@ -4,13 +4,13 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { UniformDefs, UniformValues } from './uniform';
-import { AttributeDefs, AttributeValues, createAttributeBuffers, createElementsBuffer, ElementsKind, ElementsBuffer } from './buffer';
-import { TextureDefs, TextureValues, createTextures } from './texture';
+import { UniformValues } from './uniform';
+import { AttributeValues, createAttributeBuffers, createElementsBuffer, ElementsBuffer } from './buffer';
+import { TextureValues, createTextures } from './texture';
 import { Context } from './context';
-import { ShaderCode } from '../shader-code';
+import { ShaderCode, addShaderDefines, DefineValues } from '../shader-code';
 import { Program } from './program';
-import { ValueCell } from 'mol-util';
+import { RenderableSchema, RenderableValues } from '../renderable/schema';
 
 export type DrawMode = 'points' | 'lines' | 'line-strip' | 'line-loop' | 'triangles' | 'triangle-strip' | 'triangle-fan'
 
@@ -27,29 +27,21 @@ export function getDrawMode(ctx: Context, drawMode: DrawMode) {
     }
 }
 
-export type RenderItemProps = {
-    shaderCode: ShaderCode
-
-    uniformDefs: UniformDefs
-    attributeDefs: AttributeDefs
-    textureDefs: TextureDefs
-
-    elementsKind?: ElementsKind
-    drawMode: DrawMode
-}
-
-export type RenderItemState = {
-    uniformValues: UniformValues
-    attributeValues: AttributeValues
-    textureValues: TextureValues
-
-    elements?: Uint32Array
-    drawCount: ValueCell<number>
-    instanceCount: ValueCell<number>
+function splitValues(schema: RenderableSchema, values: RenderableValues) {
+    const attributeValues: AttributeValues = {}
+    const defineValues: DefineValues = {}
+    const textureValues: TextureValues = {}
+    const uniformValues: UniformValues = {}
+    Object.keys(values).forEach(k => {
+        if (schema[k].type === 'attribute') attributeValues[k] = values[k]
+        if (schema[k].type === 'define') defineValues[k] = values[k]
+        if (schema[k].type === 'texture') textureValues[k] = values[k]
+        if (schema[k].type === 'uniform') uniformValues[k] = values[k]
+    })
+    return { attributeValues, defineValues, textureValues, uniformValues }
 }
 
 export interface RenderItem {
-    readonly hash: string
     readonly programId: number
     readonly program: Program
 
@@ -58,19 +50,23 @@ export interface RenderItem {
     destroy: () => void
 }
 
-export function createRenderItem(ctx: Context, props: RenderItemProps, state: RenderItemState): RenderItem {
+export function createRenderItem(ctx: Context, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues): RenderItem {
     const { programCache } = ctx
     const { angleInstancedArrays, oesVertexArrayObject } = ctx.extensions
-    const { shaderCode, uniformDefs, attributeDefs, textureDefs, elementsKind } = props
-    const { attributeValues, textureValues, uniformValues, elements } = state
 
-    const hash = JSON.stringify(props)
-    const drawMode = getDrawMode(ctx, props.drawMode)
-    const programRef = programCache.get(ctx, { shaderCode, uniformDefs, attributeDefs, textureDefs })
+    const { attributeValues, defineValues, textureValues, uniformValues } = splitValues(schema, values)
+
+    const glDrawMode = getDrawMode(ctx, drawMode)
+    const programRef = programCache.get(ctx, {
+        shaderCode: addShaderDefines(defineValues, shaderCode),
+        schema
+    })
     const program = programRef.value
 
-    const textures = createTextures(ctx, textureDefs, textureValues)
-    const attributeBuffers = createAttributeBuffers(ctx, attributeDefs, attributeValues)
+    const textures = createTextures(ctx, schema, textureValues)
+    const attributeBuffers = createAttributeBuffers(ctx, schema, attributeValues)
+
+    const elements = values.elements
 
     let vertexArray: WebGLVertexArrayObjectOES
     if (oesVertexArrayObject) {
@@ -81,8 +77,8 @@ export function createRenderItem(ctx: Context, props: RenderItemProps, state: Re
     }
 
     let elementsBuffer: ElementsBuffer
-    if (elements && elementsKind) {
-        elementsBuffer = createElementsBuffer(ctx, elements)
+    if (elements && elements.ref.value) {
+        elementsBuffer = createElementsBuffer(ctx, elements.ref.value)
     }
 
     // needs to come after elements buffer creation to include it in the vao
@@ -90,13 +86,12 @@ export function createRenderItem(ctx: Context, props: RenderItemProps, state: Re
         oesVertexArrayObject.bindVertexArrayOES(null!)
     }
 
-    let drawCount = state.drawCount.ref
-    let instanceCount = state.instanceCount.ref
+    let drawCount = values.drawCount.ref
+    let instanceCount = values.instanceCount.ref
 
     let destroyed = false
 
     return {
-        hash,
         programId: program.id,
         program,
 
@@ -110,22 +105,21 @@ export function createRenderItem(ctx: Context, props: RenderItemProps, state: Re
             }
             program.bindTextures(textures)
             if (elementsBuffer) {
-                angleInstancedArrays.drawElementsInstancedANGLE(drawMode, drawCount.value, elementsBuffer._dataType, 0, instanceCount.value);
+                angleInstancedArrays.drawElementsInstancedANGLE(glDrawMode, drawCount.value, elementsBuffer._dataType, 0, instanceCount.value);
             } else {
-                angleInstancedArrays.drawArraysInstancedANGLE(drawMode, 0, drawCount.value, instanceCount.value)
+                angleInstancedArrays.drawArraysInstancedANGLE(glDrawMode, 0, drawCount.value, instanceCount.value)
             }
         },
         update: () => {
-            if (state.drawCount.ref.version !== drawCount.version) {
+            if (values.drawCount.ref.version !== drawCount.version) {
                 console.log('drawCount version changed')
-                drawCount = state.drawCount.ref
+                drawCount = values.drawCount.ref
             }
-            if (state.instanceCount.ref.version !== instanceCount.version) {
+            if (values.instanceCount.ref.version !== instanceCount.version) {
                 console.log('instanceCount version changed')
-                instanceCount = state.instanceCount.ref
+                instanceCount = values.instanceCount.ref
             }
 
-            // const { attributeValues } = state
             // Object.keys(attributeValues).forEach(k => {
             //     const value = attributeValues[k]
             //     if (value === undefined) return
@@ -142,7 +136,7 @@ export function createRenderItem(ctx: Context, props: RenderItemProps, state: Re
             programRef.free()
             Object.keys(textures).forEach(k => textures[k].destroy())
             Object.keys(attributeBuffers).forEach(k => attributeBuffers[k].destroy())
-            if (elements && elementsKind) {
+            if (elements) {
                 elementsBuffer.destroy()
             }
             if (oesVertexArrayObject) {

+ 15 - 8
src/mol-gl/webgl/texture.ts

@@ -6,8 +6,13 @@
 
 import { Context } from './context'
 import { TextureImage } from '../renderable/util';
+import { ValueCell } from 'mol-util';
+import { RenderableSchema } from '../renderable/schema';
+import { idFactory } from 'mol-util/id-factory';
 
+const getNextTextureId = idFactory()
 export interface Texture {
+    id: number
     load: (image: TextureImage) => void
     bind: (id: TextureId) => void
     unbind: (id: TextureId) => void
@@ -16,9 +21,7 @@ export interface Texture {
 
 export type TextureId = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15
 
-export type TextureDefs = { [k: string]: true }
-export type TextureUniformDefs = { [k: string]: 't2' }
-export type TextureValues = { [k: string]: TextureImage }
+export type TextureValues = { [k: string]: ValueCell<TextureImage> }
 export type Textures = { [k: string]: Texture }
 
 export function createTexture(ctx: Context): Texture {
@@ -38,6 +41,7 @@ export function createTexture(ctx: Context): Texture {
     ctx.textureCount += 1
 
     return {
+        id: getNextTextureId(),
         load: (image: TextureImage) => {
             const { array, width, height } = image
             gl.bindTexture(_textureType, texture)
@@ -69,12 +73,15 @@ export function createTexture(ctx: Context): Texture {
     }
 }
 
-export function createTextures(ctx: Context, props: TextureDefs, state: TextureValues) {
+export function createTextures(ctx: Context, schema: RenderableSchema, values: TextureValues) {
     const textures: Textures = {}
-    Object.keys(props).forEach((k, i) => {
-        const texture = createTexture(ctx)
-        texture.load(state[k])
-        textures[k] = texture
+    Object.keys(schema).forEach((k, i) => {
+        const spec = schema[k]
+        if (spec.type === 'texture') {
+            const texture = createTexture(ctx)
+            texture.load(values[k].ref.value)
+            textures[k] = texture
+        }
     })
     return textures
 }

+ 35 - 17
src/mol-gl/webgl/uniform.ts

@@ -8,6 +8,7 @@ import { Mat3, Mat4, Vec2, Vec3, Vec4 } from 'mol-math/linear-algebra'
 import { Context } from './context';
 import { TextureImage } from '../renderable/util';
 import { ValueCell } from 'mol-util';
+import { RenderableSchema } from '../renderable/schema';
 
 export type UniformKindValue = {
     'f': number
@@ -22,15 +23,14 @@ export type UniformKindValue = {
 export type UniformKind = keyof UniformKindValue
 export type UniformType = number | Vec2 | Vec3 | Vec4 | Mat3 | Mat4 | TextureImage
 export interface UniformUpdater {
-    set: (value: UniformType, version: number) => void,
+    set: (value: UniformType) => void,
     clear: () => void
 }
 
-export type UniformDefs = { [k: string]: UniformKind }
 export type UniformValues = { [k: string]: ValueCell<UniformType> }
 export type UniformUpdaters = { [k: string]: UniformUpdater }
 
-export function createUniformSetter(ctx: Context, program: WebGLProgram, name: string, kind: UniformKind): (value: any) => void {
+function createUniformSetter(ctx: Context, program: WebGLProgram, name: string, kind: UniformKind): (value: any) => void {
     const { gl } = ctx
     const location = gl.getUniformLocation(program, name)
     if (location === null) {
@@ -47,21 +47,39 @@ export function createUniformSetter(ctx: Context, program: WebGLProgram, name: s
     }
 }
 
-export function getUniformUpdaters(ctx: Context, program: WebGLProgram, uniforms: UniformDefs) {
-    const updaters: UniformUpdaters = {}
-    Object.keys(uniforms).forEach(k => {
-        const setter = createUniformSetter(ctx, program, k, uniforms[k])
-        let _version = -1
-        updaters[k] = {
-            set: (value, version) => {
-                if (_version !== version) {
-                    setter(value)
-                    _version = version
-                }
-            },
-            clear: () => {
-                _version = -1
+function createUniformUpdater(ctx: Context, program: WebGLProgram, name: string, kind: UniformKind): UniformUpdater {
+    const setter = createUniformSetter(ctx, program, name, kind)
+    let _value: UniformType | undefined = undefined
+    return {
+        set: value => {
+            if (_value !== value) {
+                setter(value)
+                _value = value
             }
+        },
+        clear: () => {
+            _value = undefined
+        }
+    }
+}
+
+export function getUniformUpdaters(ctx: Context, program: WebGLProgram, schema: RenderableSchema) {
+    const updaters: UniformUpdaters = {}
+    Object.keys(schema).forEach(k => {
+        const spec = schema[k]
+        if (spec.type === 'uniform') {
+            updaters[k] = createUniformUpdater(ctx, program, k, spec.kind)
+        }
+    })
+    return updaters
+}
+
+export function getTextureUniformUpdaters(ctx: Context, program: WebGLProgram, schema: RenderableSchema) {
+    const updaters: UniformUpdaters = {}
+    Object.keys(schema).forEach(k => {
+        const spec = schema[k]
+        if (spec.type === 'texture') {
+            updaters[k] = createUniformUpdater(ctx, program, k, 't2')
         }
     })
     return updaters

+ 1 - 0
src/mol-util/index.ts

@@ -93,6 +93,7 @@ export function shallowEqual<T>(a: T, b: T) {
     return true;
 }
 
+/** Returns `value` if not `undefined`, otherwise returns `defaultValue` */
 export function defaults(value: any, defaultValue: any) {
     return value !== undefined ? value : defaultValue
 }

+ 1 - 1
src/mol-view/stage.ts

@@ -23,7 +23,7 @@ import { SpacefillProps } from 'mol-geo/representation/structure/spacefill';
 const spacefillProps: SpacefillProps = {
     doubleSided: true,
     detail: 2,
-    colorTheme: { name: 'element-symbol' }
+    colorTheme: { name: 'instance-index' }
 }
 
 export class Stage {

+ 2 - 1
src/mol-view/state/transform.ts

@@ -97,6 +97,7 @@ export const StructureToSpacefill: StructureToSpacefill = StateTransform.create(
         await spacefillRepr.create(structureEntity.value, props).run(ctx.log)
         ctx.viewer.add(spacefillRepr)
         ctx.viewer.requestDraw()
+        console.log('stats', ctx.viewer.stats)
         // ctx.viewer.input.drag.subscribe(async () => {
         //     console.log('drag')
         //     console.time('spacefill update')
@@ -117,7 +118,7 @@ export const SpacefillUpdate: SpacefillUpdate = StateTransform.create('spacefill
         ctx.viewer.add(spacefillRepr)
         ctx.viewer.update()
         ctx.viewer.requestDraw()
-        // console.log(ctx.viewer.stats, props)
+        console.log('stats', ctx.viewer.stats)
         return NullEntity
     })
 

+ 3 - 3
src/mol-view/viewer.ts

@@ -10,7 +10,7 @@ import { Vec3, Mat4, EPSILON } from 'mol-math/linear-algebra'
 import InputObserver from 'mol-util/input/input-observer'
 import * as SetUtils from 'mol-util/set'
 import Renderer, { RendererStats } from 'mol-gl/renderer'
-import { RenderObject } from 'mol-gl/scene'
+import { RenderObject } from 'mol-gl/render-object'
 
 import TrackballControls from './controls/trackball'
 import { Viewport } from './camera/util'
@@ -119,11 +119,11 @@ namespace Viewer {
 
             hide: (repr: Representation<any>) => {
                 const renderObjectSet = reprMap.get(repr)
-                if (renderObjectSet) renderObjectSet.forEach(o => o.props.visible = false)
+                if (renderObjectSet) renderObjectSet.forEach(o => o.state.visible = false)
             },
             show: (repr: Representation<any>) => {
                 const renderObjectSet = reprMap.get(repr)
-                if (renderObjectSet) renderObjectSet.forEach(o => o.props.visible = true)
+                if (renderObjectSet) renderObjectSet.forEach(o => o.state.visible = true)
             },
 
             add: (repr: Representation<any>) => {