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