From aa480d5a997dfff3010082cb10919522c8187a95 Mon Sep 17 00:00:00 2001
From: Alexander Rose <alex.rose@rcsb.org>
Date: Mon, 2 Apr 2018 19:27:06 -0700
Subject: [PATCH] wip, instances: transform, color texture, ...

---
 package-lock.json                 | Bin 418006 -> 418423 bytes
 package.json                      |   8 ++++-
 src/apps/render-test/state.ts     |  50 +++++++++++++++++++++++-------
 src/helpers.d.ts                  |   1 +
 src/mol-gl/attribute.ts           |  18 +++++++----
 src/mol-gl/renderable.ts          |   5 +--
 src/mol-gl/renderable/mesh.ts     |  34 +++++++++++++++++---
 src/mol-gl/renderable/point.ts    |   9 ++++--
 src/mol-gl/renderable/util.ts     |  15 +++++++++
 src/mol-gl/shader/mesh.frag       |   6 +++-
 src/mol-gl/shader/mesh.vert       |  34 +++++++++++++++++---
 src/mol-gl/shader/point.vert      |  12 +++++--
 src/mol-gl/shader/read-vec3.glsl  |  13 ++++++++
 src/mol-gl/util.ts                |  13 ++++++++
 src/mol-math/linear-algebra/3d.ts |  39 +++++++++++++++++++++++
 15 files changed, 221 insertions(+), 36 deletions(-)
 create mode 100644 src/mol-gl/shader/read-vec3.glsl
 create mode 100644 src/mol-gl/util.ts

diff --git a/package-lock.json b/package-lock.json
index 7e1a9c34a146012eade43cdda8be7b5b746cc60f..6c4f12199baa2df134069e76ed5d5989b2d20e99 100644
GIT binary patch
delta 186
zcmcb1Q1bf`$%YojElf+wSTl1A@{1;4<dK-}zlF(cx<D}#ha_06L|<1wJ*PM)Gp$k=
zBA{zLU9pN;Y4QeZmQdq7^K?)1T$kj6>;iXRx1uCxSGSC$%Fs}AgJQp;%nVciqENGZ
zKQq_l^pN6^u&kmakAlFktSonPi;zU4?5Zet*TTRE1AiAIr*yaU^qgEHm#BcrjFM*4
duaz=sPq&X`@@-#G#stL7K+LjzK^d#rD*%$5LRkO+

delta 31
kcmex<MDp4}$%YojElf+w+FdJ{fS4JGS+=`Yuo}Gr0N)V|!~g&Q

diff --git a/package.json b/package.json
index 03c7cac97..0eac8431a 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,7 @@
   "scripts": {
     "lint": "tslint src/**/*.ts",
     "build": "tsc",
-    "postbuild": "copyfiles --up 1 src/mol-gl/shader/*.vert src/mol-gl/shader/*.frag build/node_modules/",
+    "postbuild": "copyfiles --up 1 src/mol-gl/shader/*.vert src/mol-gl/shader/*.frag src/mol-gl/shader/*.glsl build/node_modules/",
     "watch": "tsc -watch",
     "test": "jest",
     "script": "node build/node_modules/script.js",
@@ -44,6 +44,11 @@
     },
     "testRegex": "\\.spec\\.ts$"
   },
+  "glslify": {
+    "transform": [
+      "glslify-import"
+    ]
+  },
   "author": "",
   "license": "MIT",
   "devDependencies": {
@@ -57,6 +62,7 @@
     "@types/react-dom": "^16.0.4",
     "benchmark": "^2.1.4",
     "copyfiles": "^2.0.0",
+    "glslify-import": "^3.1.0",
     "glslify-loader": "^1.0.2",
     "jest": "^22.4.2",
     "raw-loader": "^0.5.1",
diff --git a/src/apps/render-test/state.ts b/src/apps/render-test/state.ts
index 49bf950d7..a1c108c6e 100644
--- a/src/apps/render-test/state.ts
+++ b/src/apps/render-test/state.ts
@@ -8,10 +8,11 @@ import REGL = require('regl');
 import * as glContext from 'mol-gl/context'
 import { Camera } from 'mol-gl/camera'
 import { Vec3, Mat4 } from 'mol-math/linear-algebra'
-import PointRenderable from 'mol-gl/renderable/point'
-import MeshRenderable from 'mol-gl/renderable/mesh'
+import { PointRenderable, MeshRenderable } from 'mol-gl/renderable'
 import Attribute from 'mol-gl/attribute';
 import Model from 'mol-gl/model';
+import { createTransformAttributes } from 'mol-gl/renderable/util';
+import { calculateTextureInfo } from 'mol-gl/util';
 // import { positionFromModel } from 'mol-geo/shape/point'
 
 export default class State {
@@ -42,28 +43,54 @@ export default class State {
         const model2 = Model(regl, { position: p1 })
         const model3 = Model(regl, { position: p2 })
 
-        const position = Attribute.create(regl, new Float32Array([0, -1, 0, -1, 0, 0, 1, 1, 0]), 3)
+        const position = Attribute.create(regl, new Float32Array([0, -1, 0, -1, 0, 0, 1, 1, 0]), { size: 3 })
 
         const transformArray1 = new Float32Array(16)
         const transformArray2 = new Float32Array(16 * 3)
         const m4 = Mat4.identity()
-        Mat4.toArray(m4, transformArray1)
-        Mat4.toArray(m4, transformArray2)
+        Mat4.toArray(m4, transformArray1, 0)
+        Mat4.toArray(m4, transformArray2, 0)
         Mat4.setTranslation(m4, p1)
         Mat4.toArray(m4, transformArray2, 16)
         Mat4.setTranslation(m4, p2)
         Mat4.toArray(m4, transformArray2, 32)
-        const transform1 = Attribute.create(regl, transformArray1, 16, 1)
-        const transform2 = Attribute.create(regl, transformArray2, 16, 1)
 
-        // TODO use https://github.com/substack/glsl-matrix-texture
+        const colorTexInfo = calculateTextureInfo(3, 3)
+        const color = new Uint8Array(colorTexInfo.length)
+        color.set([
+            0, 0, 255,
+            0, 255, 0,
+            255, 0, 0
+        ])
+        console.log(color, colorTexInfo)
+        const colorTex = regl.texture({
+            width: colorTexInfo.width,
+            height: colorTexInfo.height,
+            format: 'rgb',
+            type: 'uint8',
+            wrapS: 'clamp',
+            wrapT: 'clamp',
+            data: color
+        })
 
         // position.update((array: Float32Array) => {
         //     positionFromModel({}, array, 0)
         // })
 
-        const points = PointRenderable.create(regl, { position, transform: transform1 })
-        const mesh = MeshRenderable.create(regl, { position, transform: transform2 })
+        const points = PointRenderable.create(regl, {
+            position,
+            ...createTransformAttributes(regl, transformArray1)
+        })
+        const mesh = MeshRenderable.create(regl,
+            {
+                position,
+                ...createTransformAttributes(regl, transformArray2)
+            },
+            {
+                colorTex,
+                colorTexSize: [ colorTexInfo.width, colorTexInfo.height ]
+            }
+        )
 
         const baseContext = regl({
             context: {
@@ -80,11 +107,12 @@ export default class State {
             camera.update((state: any) => {
                 if (!camera.isDirty()) return
                 baseContext(() => {
-                    console.log(ctx)
+                    // console.log(ctx)
                     regl.clear({color: [0, 0, 0, 1]})
                     position.update(array => { array[0] = Math.random() })
                     // points.update(a => { a.position[0] = Math.random() })
                     mesh.draw()
+                    points.draw()
                     model1({}, ({ transform }) => {
                         points.draw()
                     })
diff --git a/src/helpers.d.ts b/src/helpers.d.ts
index 2b82c6a8c..6eb695ff0 100644
--- a/src/helpers.d.ts
+++ b/src/helpers.d.ts
@@ -10,4 +10,5 @@ declare module Helpers {
         -readonly [P in keyof T]: T[P]
     }
     export type TypedArray = Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array | Uint8ClampedArray | Float32Array | Float64Array
+    export type NumberArray = TypedArray | number[]
 }
\ No newline at end of file
diff --git a/src/mol-gl/attribute.ts b/src/mol-gl/attribute.ts
index cb6e52cfb..1133d5e7b 100644
--- a/src/mol-gl/attribute.ts
+++ b/src/mol-gl/attribute.ts
@@ -48,19 +48,25 @@ interface Attribute<T extends Helpers.TypedArray> {
     reload(): void
 }
 
+interface AttributeProps {
+    size: 1 | 2 | 3 | 4,
+    divisor?: number,
+    offset?: number,
+    stride?: number
+}
+
 namespace Attribute {
     export type Mutator<T extends Helpers.TypedArray> = (data: T) => (UpdateInfo | void)
     export type UpdateInfo = boolean | { offset: number, count: number }
 
-    export function create<T extends Helpers.TypedArray>(regl: REGL.Regl, array: T, itemSize: number, divisor = 0): Attribute<T> {
+    export function create<T extends Helpers.TypedArray>(regl: REGL.Regl, array: T, props: AttributeProps): Attribute<T> {
+        const itemSize = props.size
         let _array = array
         let _count = _array.length / itemSize
+        if (props.stride) _count = _array.length / (props.stride / _array.BYTES_PER_ELEMENT)
+        console.log(_array.length, props.stride)
         const buffer = regl.buffer(_array)
-        const attribute: REGL.AttributeConfig = {
-            size: itemSize,
-            buffer,
-            divisor
-        }
+        const attribute: REGL.AttributeConfig = { ...props, buffer }
         const growIfNeeded = function(count: number) {
             if (count * itemSize > _array.length) {
                 const newArray: T = new (_array as any).constructor(count * itemSize)
diff --git a/src/mol-gl/renderable.ts b/src/mol-gl/renderable.ts
index efe181718..2d85d0908 100644
--- a/src/mol-gl/renderable.ts
+++ b/src/mol-gl/renderable.ts
@@ -6,7 +6,8 @@
 
 import REGL = require('regl');
 import Attribute from './attribute'
-import Point from './renderable/point'
+import PointRenderable from './renderable/point'
+import MeshRenderable from './renderable/mesh'
 
 export type AttributesMutator<T extends AttributesData> = (data: T) => (boolean | void)
 export type AttributesData = { [k: string]: Helpers.TypedArray }
@@ -17,4 +18,4 @@ export interface Renderable<T extends AttributesData> {
     draw(): void
 }
 
-export { Point }
\ No newline at end of file
+export { PointRenderable, MeshRenderable }
\ No newline at end of file
diff --git a/src/mol-gl/renderable/mesh.ts b/src/mol-gl/renderable/mesh.ts
index 2faeebc5d..92f209c0c 100644
--- a/src/mol-gl/renderable/mesh.ts
+++ b/src/mol-gl/renderable/mesh.ts
@@ -18,24 +18,48 @@ interface Elements {
 
 }
 
+type Uniforms = { [k: string]: REGL.Uniform | REGL.Texture }
+
+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
+}
+
 namespace Mesh {
     export type DataType = {
         position: { type: Float32Array, itemSize: 3 }
-        offset: { type: Float32Array, itemSize: 3 }
+        transformColumn0: { type: Float32Array, itemSize: 4 }
+        transformColumn1: { type: Float32Array, itemSize: 4 }
+        transformColumn2: { type: Float32Array, itemSize: 4 }
+        transformColumn3: { type: Float32Array, itemSize: 4 }
     }
     export type Data = { [K in keyof DataType]: DataType[K]['type'] }
     export type Attributes = { [K in keyof Data]: Attribute<Data[K]> }
 
-    export function create(regl: REGL.Regl, attributes: Attributes, elements?: Elements): Renderable<Data> {
+    export function create(regl: REGL.Regl, attributes: Attributes, uniforms: Uniforms, elements?: Elements): Renderable<Data> {
         console.log('mesh', {
             count: attributes.position.getCount(),
-            instances: attributes.transform.getCount(),
+            instances: attributes.transformColumn0.getCount(),
+            attributes,
+            uniforms
         })
+        const instanceCount = attributes.transformColumn0.getCount()
+        const instanceId = fillSerial(new Float32Array(instanceCount))
+        console.log(instanceId)
         const command = regl({
             ...MeshShaders,
-            attributes: getBuffers(attributes),
+            uniforms: {
+                objectId: uniforms.objectId || 0,
+                instanceCount,
+                ...uniforms
+            },
+            attributes: getBuffers({
+                instanceId: Attribute.create(regl, instanceId, { size: 1, divisor: 1 }),
+                ...attributes
+            }),
             count: attributes.position.getCount(),
-            instances: attributes.transform.getCount(),
+            instances: instanceCount,
             primitive: 'triangles'
         })
         return {
diff --git a/src/mol-gl/renderable/point.ts b/src/mol-gl/renderable/point.ts
index 98616af60..4a075fedf 100644
--- a/src/mol-gl/renderable/point.ts
+++ b/src/mol-gl/renderable/point.ts
@@ -15,7 +15,10 @@ type Point = 'point'
 namespace Point {
     export type DataType = {
         position: { type: Float32Array, itemSize: 3 }
-        transform: { type: Float32Array, itemSize: 16 }
+        transformColumn0: { type: Float32Array, itemSize: 4 }
+        transformColumn1: { type: Float32Array, itemSize: 4 }
+        transformColumn2: { type: Float32Array, itemSize: 4 }
+        transformColumn3: { type: Float32Array, itemSize: 4 }
     }
     export type Data = { [K in keyof DataType]: DataType[K]['type'] }
     export type Attributes = { [K in keyof Data]: Attribute<Data[K]> }
@@ -23,13 +26,13 @@ namespace Point {
     export function create(regl: REGL.Regl, attributes: Attributes): Renderable<Data> {
         console.log('point', {
             count: attributes.position.getCount(),
-            instances: attributes.transform.getCount(),
+            instances: attributes.transformColumn0.getCount(),
         }, attributes)
         const command = regl({
             ...PointShaders,
             attributes: getBuffers(attributes),
             count: attributes.position.getCount(),
-            instances: attributes.transform.getCount(),
+            instances: attributes.transformColumn0.getCount(),
             primitive: 'points'
         })
         return {
diff --git a/src/mol-gl/renderable/util.ts b/src/mol-gl/renderable/util.ts
index a9d8a161a..9d7d6e328 100644
--- a/src/mol-gl/renderable/util.ts
+++ b/src/mol-gl/renderable/util.ts
@@ -4,7 +4,22 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
+import REGL = require('regl');
 import { Attributes, AttributesData, AttributesBuffers } from '../renderable'
+import Attribute from '../attribute'
+
+export function createTransformAttributes (regl: REGL.Regl, transform: Float32Array) {
+    const size = 4
+    const divisor = 1
+    const bpe = transform.BYTES_PER_ELEMENT
+    const stride = 16 * bpe
+    return {
+        transformColumn0: Attribute.create(regl, transform, { size, divisor, offset: 0, stride }),
+        transformColumn1: Attribute.create(regl, transform, { size, divisor, offset: 4 * bpe, stride }),
+        transformColumn2: Attribute.create(regl, transform, { size, divisor, offset: 8 * bpe, stride }),
+        transformColumn3: Attribute.create(regl, transform, { size, divisor, offset: 12 * bpe, stride })
+    }
+}
 
 export function getBuffers<T extends AttributesData>(attributes: Attributes<T>): AttributesBuffers<T> {
     const buffers: AttributesBuffers<any> = {}
diff --git a/src/mol-gl/shader/mesh.frag b/src/mol-gl/shader/mesh.frag
index 011155b61..1e94b10f5 100644
--- a/src/mol-gl/shader/mesh.frag
+++ b/src/mol-gl/shader/mesh.frag
@@ -4,6 +4,10 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
+precision mediump float;
+
+varying vec3 vColor;
+
 void main(){
-    gl_FragColor = vec4(0, 1, 0, 1);
+    gl_FragColor = vec4(vColor, 1);
 }
\ No newline at end of file
diff --git a/src/mol-gl/shader/mesh.vert b/src/mol-gl/shader/mesh.vert
index e6263a322..c240d9638 100644
--- a/src/mol-gl/shader/mesh.vert
+++ b/src/mol-gl/shader/mesh.vert
@@ -4,16 +4,42 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
+#define INSTANCE_COLOR
+
 precision mediump float;
+
 uniform mat4 projection, model, view;
+
+uniform int objectId;
+uniform int instanceCount;
+
+#if defined( ATTRIBUTE_COLOR )
+    attribute vec3 color;
+#elif defined( INSTANCE_COLOR ) || defined( ELEMENT_COLOR )
+    uniform vec2 colorTexSize;
+    uniform sampler2D colorTex;
+#endif
+
 attribute vec3 position;
+attribute vec4 transformColumn0, transformColumn1, transformColumn2, transformColumn3;
+attribute float instanceId;
+// attribute int elementId;
 
-// instanced
-// attribute mat4 transform;
-uniform mat4 transform;
+varying vec3 vColor;
 
-varying vec3 vPosition;
+#pragma glslify: read_vec3 = require(./read-vec3.glsl)
 
 void main(){
+    mat4 transform = mat4(transformColumn0, transformColumn1, transformColumn2, transformColumn3);
+    #if defined( ATTRIBUTE_COLOR )
+        vColor = color;
+    #elif defined( INSTANCE_COLOR )
+        vColor = read_vec3(colorTex, instanceId, colorTexSize);
+    // #elif defined( ELEMENT_COLOR )
+    //     vColor = read_vec3(colorTex, instanceId * instanceCount + elementId, colorTexSize);
+    #else
+        vColor = vec3(0.0, 1.0, 0.0);
+    #endif
+
     gl_Position = projection * view * model * transform * vec4(position, 1.0);
 }
\ No newline at end of file
diff --git a/src/mol-gl/shader/point.vert b/src/mol-gl/shader/point.vert
index db02cf47d..26566d293 100644
--- a/src/mol-gl/shader/point.vert
+++ b/src/mol-gl/shader/point.vert
@@ -5,16 +5,22 @@
  */
 
 precision mediump float;
+
 uniform mat4 projection, model, view;
-attribute vec3 position;
+
+attribute vec3 position; //, color;
+attribute vec4 transformColumn0, transformColumn1, transformColumn2, transformColumn3;
+// attribute int instanceId;
 
 // instanced
 // attribute mat4 transform;
-uniform mat4 transform;
+// uniform mat4 transform;
 
-varying vec3 vPosition;
+// varying vec3 vColor;
 
 void main(){
+    mat4 transform = mat4(transformColumn0, transformColumn1, transformColumn2, transformColumn3);
+    // vColor = color;
     gl_PointSize = 20.0;
     gl_Position = projection * view * model * transform * vec4(position, 1.0);
 }
\ No newline at end of file
diff --git a/src/mol-gl/shader/read-vec3.glsl b/src/mol-gl/shader/read-vec3.glsl
new file mode 100644
index 000000000..7a4b13105
--- /dev/null
+++ b/src/mol-gl/shader/read-vec3.glsl
@@ -0,0 +1,13 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+vec3 read_vec3 (sampler2D tex, float i, vec2 size) {
+    float x = mod(i, size.x);
+    float y = floor(i / size.x);
+    vec2 uv = (vec2(x, y) + 0.5) / size;
+    return texture2D(tex, uv).rgb;
+}
+#pragma glslify: export(read_vec3)
\ No newline at end of file
diff --git a/src/mol-gl/util.ts b/src/mol-gl/util.ts
new file mode 100644
index 000000000..c055b15c8
--- /dev/null
+++ b/src/mol-gl/util.ts
@@ -0,0 +1,13 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+export 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 }
+}
\ No newline at end of file
diff --git a/src/mol-math/linear-algebra/3d.ts b/src/mol-math/linear-algebra/3d.ts
index 6d0875ae4..6749c7428 100644
--- a/src/mol-math/linear-algebra/3d.ts
+++ b/src/mol-math/linear-algebra/3d.ts
@@ -155,6 +155,45 @@ export namespace Mat4 {
         return Mat4.copy(Mat4.zero(), a);
     }
 
+    export function transpose(out: Mat4, a: Mat4) {
+        // If we are transposing ourselves we can skip a few steps but have to cache some values
+        if (out === a) {
+            const a01 = a[1], a02 = a[2], a03 = a[3];
+            const a12 = a[6], a13 = a[7];
+            const a23 = a[11];
+            out[1] = a[4];
+            out[2] = a[8];
+            out[3] = a[12];
+            out[4] = a01;
+            out[6] = a[9];
+            out[7] = a[13];
+            out[8] = a02;
+            out[9] = a12;
+            out[11] = a[14];
+            out[12] = a03;
+            out[13] = a13;
+            out[14] = a23;
+        } else {
+            out[0] = a[0];
+            out[1] = a[4];
+            out[2] = a[8];
+            out[3] = a[12];
+            out[4] = a[1];
+            out[5] = a[5];
+            out[6] = a[9];
+            out[7] = a[13];
+            out[8] = a[2];
+            out[9] = a[6];
+            out[10] = a[10];
+            out[11] = a[14];
+            out[12] = a[3];
+            out[13] = a[7];
+            out[14] = a[11];
+            out[15] = a[15];
+        }
+        return out;
+    }
+
     export function invert(out: Mat4, a: Mat4) {
         const a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
             a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
-- 
GitLab