diff --git a/.vscode/settings.json b/.vscode/settings.json index 00ad71fba1a3d0ed26f404f386e5966e082327cd..55712c19f1dffd66d4fed7a406a354f215c43a14 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "typescript.tsdk": "node_modules\\typescript\\lib" + "typescript.tsdk": "node_modules/typescript/lib" } \ No newline at end of file diff --git a/package.json b/package.json index 8cf7a5c1afd0cb59266d874eb7e81acec2fb0cda..03c7cac9732118e293b1bd8ab4913208bae3b6a3 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "watch": "tsc -watch", "test": "jest", "script": "node build/node_modules/script.js", - "app-render-test": "webpack build/node_modules/apps/render-test/index.js --mode development -o web/render-test/index.js" + "app-render-test": "webpack build/node_modules/apps/render-test/index.js --mode development -o web/render-test/index.js", + "app-render-test-watch": "webpack build/node_modules/apps/render-test/index.js -w --mode development -o web/render-test/index.js" }, "jest": { "moduleFileExtensions": [ diff --git a/src/apps/render-test/state.ts b/src/apps/render-test/state.ts index b5d995ae342ffbc9b1f63c60896e95e1906d93e4..6286734446e253c3511255abb7798ccf8f630e64 100644 --- a/src/apps/render-test/state.ts +++ b/src/apps/render-test/state.ts @@ -8,9 +8,7 @@ import REGL = require('regl'); import * as glContext from 'mol-gl/context' import { Camera } from 'mol-gl/camera' import { Vec3 } from 'mol-math/linear-algebra' - -const pointVert = require('mol-gl/shader/point.vert') -const pointFrag = require('mol-gl/shader/point.frag') +import Point from 'mol-gl/renderable/point' export default class State { regl: REGL.Regl @@ -31,21 +29,16 @@ export default class State { center: Vec3.create(0, 0, 0) }) - const drawPoints = regl({ - vert: pointVert, - frag: pointFrag, - attributes: { - position: [[0, -1, 0], [-1, 0, 0], [1, 1, 0]] - }, - count: 3, - primitive: 'points' + const points = Point.create(regl, { + position: new Float32Array([0, -1, 0, -1, 0, 0, 1, 1, 0]) }) regl.frame(() => { camera.update((state: any) => { if (!camera.isDirty()) return regl.clear({color: [0, 0, 0, 1]}) - drawPoints() + points.update(a => { a.position[0] = Math.random() }) + points.draw() }, undefined) }) diff --git a/src/helpers.d.ts b/src/helpers.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..2b82c6a8c259923f095e10b0813c88f9df9b5c6c --- /dev/null +++ b/src/helpers.d.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> + * @author David Sehnal <david.sehnal@gmail.com> + */ + +declare module Helpers { + export type Mutable<T> = { + -readonly [P in keyof T]: T[P] + } + export type TypedArray = Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array | Uint8ClampedArray | Float32Array | Float64Array +} \ No newline at end of file diff --git a/src/mol-gl/attribute.ts b/src/mol-gl/attribute.ts new file mode 100644 index 0000000000000000000000000000000000000000..852640ce309576230a9ca684c5be746b708c85c8 --- /dev/null +++ b/src/mol-gl/attribute.ts @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import REGL = require('regl'); + +interface Attribute<T extends Helpers.TypedArray> { + readonly buffer: REGL.Buffer + readonly length: number + readonly data: T + set(value: number, index: number): void + growIfNeeded(size: number): void + update(mutator: Attribute.Mutator<T>): void + reload(): void +} + +namespace Attribute { + export type Mutator<T extends Helpers.TypedArray> = (data: T) => (boolean | void) + + export function create<T extends Helpers.TypedArray>(regl: REGL.Regl, data: T): Attribute<T> { + let _data = data + let _length = _data.length + const buffer = regl.buffer(_data) + const growIfNeeded = function(length: number) { + if (length > _data.length) { + _data = new (_data as any).constructor(_data) + _length = _data.length + buffer(_data) + } + } + return { + buffer, + get length() { return _length }, + get data() { return _data }, + set: (value: number, index: number) => { + growIfNeeded(index) + _data[index] = value + buffer.subdata([value], index * data.BYTES_PER_ELEMENT) + }, + growIfNeeded(size: number) { + growIfNeeded(size) + }, + update: (mutator: Mutator<T>) => { + mutator(_data) + buffer(_data) + }, + reload: () => { + buffer(_data) + } + } + } +} + +export default Attribute \ No newline at end of file diff --git a/src/mol-gl/renderable.ts b/src/mol-gl/renderable.ts new file mode 100644 index 0000000000000000000000000000000000000000..395762c592584646c90781b771f580d502a41c47 --- /dev/null +++ b/src/mol-gl/renderable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import REGL = require('regl'); +import Attribute from './attribute' +import Point from './renderable/point' + +export type AttributesMutator<T extends AttributesData> = (data: T) => (boolean | void) +export type AttributesData = { [k: string]: Helpers.TypedArray } +export type Attributes<T extends AttributesData> = { [K in keyof T]: Attribute<T[K]> } +export type AttributesBuffers<T extends AttributesData> = { [K in keyof T]: REGL.Buffer } + +export interface Renderable<T extends AttributesData> { + draw(): void, + update(mutator: AttributesMutator<T>): void +} + +export { Point } \ No newline at end of file diff --git a/src/mol-gl/renderable/point.ts b/src/mol-gl/renderable/point.ts new file mode 100644 index 0000000000000000000000000000000000000000..946af4f6a4f3d4277bc5cae3b6ed27042e8ebd74 --- /dev/null +++ b/src/mol-gl/renderable/point.ts @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import REGL = require('regl'); +import { Renderable, AttributesMutator, AttributesData } from '../renderable' +import { createAttributes, getBuffers, getData } from './util' + +const pointVert = require('mol-gl/shader/point.vert') +const pointFrag = require('mol-gl/shader/point.frag') + +type Point = 'point' + +namespace Point { + export interface Data extends AttributesData { + position: Float32Array + } + export function create(regl: REGL.Regl, data: Data): Renderable<Data> { + const attributes = createAttributes(regl, data) + const command = regl({ + vert: pointVert, + frag: pointFrag, + attributes: getBuffers(attributes), + count: data.position.length / 3, + primitive: 'points' + }) + return { + draw: () => command(), + update: (mutator: AttributesMutator<Data>) => { + mutator(getData(attributes)) + for (const k of Object.keys(attributes)) { + attributes[k].reload() + } + } + } + } +} + +export default Point \ No newline at end of file diff --git a/src/mol-gl/renderable/util.ts b/src/mol-gl/renderable/util.ts new file mode 100644 index 0000000000000000000000000000000000000000..c6801722c8426b0c01a6f9f71641ba54bab22c58 --- /dev/null +++ b/src/mol-gl/renderable/util.ts @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import REGL = require('regl'); + +import Attribute from '../attribute' +import { Attributes, AttributesData, AttributesBuffers } from '../renderable' + +export function getData<T extends AttributesData>(attributes: Attributes<T>): T { + const data: AttributesData = {} + for (const k of Object.keys(attributes)) { + data[k] = attributes[k].data + } + return data as T +} + +export function getBuffers<T extends AttributesData>(attributes: Attributes<T>): AttributesBuffers<T> { + const buffers: AttributesBuffers<any> = {} + for (const k of Object.keys(attributes)) { + buffers[k] = attributes[k].buffer + } + return buffers as AttributesBuffers<T> +} + +export function createAttributes<T extends AttributesData>(regl: REGL.Regl, data: T): Attributes<T> { + const attributes: Attributes<any> = {} + for (const k of Object.keys(data)) { + attributes[k] = Attribute.create(regl, data[k]) + } + return attributes as Attributes<T> +} \ No newline at end of file