Skip to content
Snippets Groups Projects
Commit 9f936d9c authored by David Sehnal's avatar David Sehnal
Browse files

Merge gl-geo

parents d4ed6cc7 152b5219
Branches
Tags
No related merge requests found
Showing
with 965 additions and 18 deletions
...@@ -4,4 +4,6 @@ node_modules/ ...@@ -4,4 +4,6 @@ node_modules/
debug.log debug.log
npm-debug.log npm-debug.log
*.sublime-workspace *.sublime-workspace
\ No newline at end of file
web/render-test/index.js
\ No newline at end of file
{ {
"typescript.tsdk": "node_modules\\typescript\\lib" "typescript.tsdk": "node_modules/typescript/lib"
} }
\ No newline at end of file
...@@ -9,6 +9,11 @@ ...@@ -9,6 +9,11 @@
"problemMatcher": [ "problemMatcher": [
"$tsc" "$tsc"
] ]
},
{
"type": "npm",
"script": "app-render-test",
"problemMatcher": []
} }
] ]
} }
\ No newline at end of file
...@@ -12,10 +12,13 @@ ...@@ -12,10 +12,13 @@
}, },
"scripts": { "scripts": {
"lint": "tslint src/**/*.ts", "lint": "tslint src/**/*.ts",
"build": "tsc", "build": "cpx \"src/**/*.{vert,frag,glsl}\" build/node_modules/ && tsc",
"watch": "tsc -watch", "watch": "tsc -watch",
"watch-shader": "cpx \"src/**/*.{vert,frag,glsl}\" build/node_modules/ --watch",
"test": "jest", "test": "jest",
"script": "node build/node_modules/script.js" "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-watch": "webpack build/node_modules/apps/render-test/index.js -w --mode development -o web/render-test/index.js"
}, },
"jest": { "jest": {
"moduleFileExtensions": [ "moduleFileExtensions": [
...@@ -30,29 +33,41 @@ ...@@ -30,29 +33,41 @@
"build/node_modules" "build/node_modules"
], ],
"moduleNameMapper": { "moduleNameMapper": {
"mol-task($|/.*)": "<rootDir>/src/mol-task$1",
"mol-comp($|/.*)": "<rootDir>/src/mol-comp$1",
"mol-util($|/.*)": "<rootDir>/src/mol-util$1",
"mol-data($|/.*)": "<rootDir>/src/mol-data$1", "mol-data($|/.*)": "<rootDir>/src/mol-data$1",
"mol-math($|/.*)": "<rootDir>/src/mol-math$1", "mol-gl($|/.*)": "<rootDir>/src/mol-gl$1",
"mol-io($|/.*)": "<rootDir>/src/mol-io$1", "mol-io($|/.*)": "<rootDir>/src/mol-io$1",
"mol-model($|/.*)": "<rootDir>/src/mol-model$1" "mol-math($|/.*)": "<rootDir>/src/mol-math$1",
"mol-model($|/.*)": "<rootDir>/src/mol-model$1",
"mol-ql($|/.*)": "<rootDir>/src/mol-ql$1",
"mol-task($|/.*)": "<rootDir>/src/mol-task$1",
"mol-util($|/.*)": "<rootDir>/src/mol-util$1"
}, },
"testRegex": "\\.spec\\.ts$" "testRegex": "\\.spec\\.ts$"
}, },
"glslify": {
"transform": [
"glslify-import"
]
},
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@types/argparse": "^1.0.33", "@types/argparse": "^1.0.33",
"@types/benchmark": "^1.0.31", "@types/benchmark": "^1.0.31",
"@types/express": "^4.11.1", "@types/express": "^4.11.1",
"@types/jest": "^22.2.2", "@types/jest": "^22.1.3",
"@types/node": "^9.6.1", "@types/node": "^9.6.0",
"@types/node-fetch": "^1.6.7", "@types/node-fetch": "^1.6.7",
"@types/react": "^16.1.0", "@types/react": "^16.1.0",
"@types/react-dom": "^16.0.4", "@types/react-dom": "^16.0.4",
"benchmark": "^2.1.4", "benchmark": "^2.1.4",
"jest": "^22.4.3", "copyfiles": "^2.0.0",
"cpx": "^1.5.0",
"extra-watch-webpack-plugin": "^1.0.1",
"glslify-import": "^3.1.0",
"glslify-loader": "^1.0.2",
"jest": "^22.4.2",
"raw-loader": "^0.5.1",
"regl": "git+https://github.com/regl-project/regl.git#45c6ec570232420fca21567499c9c5a2a054432e", "regl": "git+https://github.com/regl-project/regl.git#45c6ec570232420fca21567499c9c5a2a054432e",
"rollup": "^0.56.5", "rollup": "^0.56.5",
"rollup-plugin-buble": "^0.19.2", "rollup-plugin-buble": "^0.19.2",
...@@ -63,8 +78,10 @@ ...@@ -63,8 +78,10 @@
"ts-jest": "^22.4.2", "ts-jest": "^22.4.2",
"tslint": "^5.9.1", "tslint": "^5.9.1",
"typescript": "^2.8.1", "typescript": "^2.8.1",
"uglify-js": "^3.3.16", "uglify-js": "^3.3.12",
"util.promisify": "^1.0.0" "util.promisify": "^1.0.0",
"webpack": "^4.2.0",
"webpack-cli": "^2.0.13"
}, },
"dependencies": { "dependencies": {
"argparse": "^1.0.10", "argparse": "^1.0.10",
......
...@@ -5,7 +5,9 @@ ...@@ -5,7 +5,9 @@
*/ */
import UI from './ui' import UI from './ui'
import State from './state'
import * as React from 'react' import * as React from 'react'
import * as ReactDOM from 'react-dom' import * as ReactDOM from 'react-dom'
ReactDOM.render(<UI/>, document.getElementById('app')); const state = new State()
\ No newline at end of file ReactDOM.render(<UI state={state} />, document.getElementById('app'));
\ No newline at end of file
/**
* 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 * as glContext from 'mol-gl/context'
import { Camera } from 'mol-gl/camera'
import { Vec3, Mat4 } from 'mol-math/linear-algebra'
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 Icosahedron from 'mol-geo/primitive/icosahedron'
import Box from 'mol-geo/primitive/box'
export default class State {
regl: REGL.Regl
initRegl (container: HTMLDivElement) {
const regl = glContext.create({
container,
extensions: [
'OES_texture_float',
'OES_texture_float_linear',
'OES_element_index_uint',
// 'ext_disjoint_timer_query',
'EXT_blend_minmax',
'ANGLE_instanced_arrays'
],
// profile: true
})
const camera = Camera.create(regl, container, {
center: Vec3.create(0, 0, 0)
})
const p1 = Vec3.create(0, 4, 0)
const p2 = Vec3.create(-3, 0, 0)
const model1 = Model(regl)
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]), { size: 3 })
const normal = Attribute.create(regl, new Float32Array([0, 0, 0, 0, 0, 0, 0, 0, 0]), { size: 3 })
const transformArray1 = new Float32Array(16)
const transformArray2 = new Float32Array(16 * 3)
const m4 = Mat4.identity()
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 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,
...createTransformAttributes(regl, transformArray1)
})
const mesh = MeshRenderable.create(regl,
{
position,
normal,
...createTransformAttributes(regl, transformArray2)
},
{
colorTex,
colorTexSize: [ colorTexInfo.width, colorTexInfo.height ]
}
)
const sphere = Icosahedron(1, 1)
console.log(sphere)
const box = Box(1, 1, 1, 1, 1, 1)
console.log(box)
const points2 = PointRenderable.create(regl, {
position: Attribute.create(regl, new Float32Array(box.vertices), { size: 3 }),
...createTransformAttributes(regl, transformArray1)
})
const mesh2 = MeshRenderable.create(regl,
{
position: Attribute.create(regl, new Float32Array(box.vertices), { size: 3 }),
normal: Attribute.create(regl, new Float32Array(box.normals), { size: 3 }),
...createTransformAttributes(regl, transformArray2)
},
{
colorTex,
colorTexSize: [ colorTexInfo.width, colorTexInfo.height ],
'light.position': Vec3.create(0, 0, -20),
'light.color': Vec3.create(1.0, 1.0, 1.0),
'light.ambient': Vec3.create(0.5, 0.5, 0.5),
'light.falloff': 0,
'light.radius': 500
},
box.indices
)
const baseContext = regl({
context: {
model: Mat4.identity(),
transform: Mat4.setTranslation(Mat4.identity(), Vec3.create(6, 0, 0))
},
uniforms: {
model: regl.context('model' as any),
transform: regl.context('transform' as any),
}
})
regl.frame((ctx) => {
camera.update((state: any) => {
if (!camera.isDirty()) return
baseContext(() => {
// 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()
mesh2.draw()
points2.draw()
// model1({}, ({ transform }) => {
// points.draw()
// })
// model2({}, ({ transform }) => {
// points.draw()
// model3({ transform }, () => {
// points.draw()
// })
// })
})
}, undefined)
})
this.regl = regl
}
constructor() {
}
}
...@@ -5,11 +5,18 @@ ...@@ -5,11 +5,18 @@
*/ */
import * as React from 'react' import * as React from 'react'
import State from './state'
export default class Root extends React.Component<{ state: State }, { }> {
private canvasContainer: HTMLDivElement | null = null;
componentDidMount() {
if (this.canvasContainer) this.props.state.initRegl(this.canvasContainer)
}
export default class Root extends React.Component {
render() { render() {
return <div style={{ position: 'absolute', top: 0, right: 0, left: 0, bottom: 0, overflow: 'hidden' }}> return <div ref={elm => this.canvasContainer = elm} style={{ position: 'absolute', top: 0, right: 0, left: 0, bottom: 0, overflow: 'hidden' }}>
</div> </div>
} }
} }
\ No newline at end of file
/**
* 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
export type NumberArray = TypedArray | number[]
export type UintArray = Uint8Array | Uint16Array | Uint32Array | number[]
}
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
// adapted from three.js, MIT License Copyright 2010-2018 three.js authors
import { Vec3 } from 'mol-math/linear-algebra'
export default function Box(width: number, height: number, depth: number, widthSegments: number, heightSegments: number, depthSegments: number) {
widthSegments = Math.floor(widthSegments)
heightSegments = Math.floor(heightSegments)
depthSegments = Math.floor(depthSegments)
// buffers
const indices: number[] = [];
const vertices: number[] = [];
const normals: number[] = [];
// helper variables
let numberOfVertices = 0;
// build each side of the box geometry
buildPlane(2, 1, 0, -1, -1, depth, height, width, depthSegments, heightSegments); // px
buildPlane(2, 1, 0, 1, -1, depth, height, -width, depthSegments, heightSegments); // nx
buildPlane(0, 2, 1, 1, 1, width, depth, height, widthSegments, depthSegments); // py
buildPlane(0, 2, 1, 1, -1, width, depth, -height, widthSegments, depthSegments); // ny
buildPlane(0, 1, 2, 1, -1, width, height, depth, widthSegments, heightSegments); // pz
buildPlane(0, 1, 2, -1, -1, width, height, -depth, widthSegments, heightSegments); // nz
return { vertices, indices, normals }
function buildPlane(u: number, v: number, w: number, udir: number, vdir: number, width: number, height: number, depth: number, gridX: number, gridY: number) {
const segmentWidth = width / gridX;
const segmentHeight = height / gridY;
const widthHalf = width / 2;
const heightHalf = height / 2;
const depthHalf = depth / 2;
const gridX1 = gridX + 1;
const gridY1 = gridY + 1;
let vertexCounter = 0;
const vector = Vec3.zero();
// generate vertices and normals
for (let iy = 0; iy < gridY1; ++iy) {
const y = iy * segmentHeight - heightHalf;
for (let ix = 0; ix < gridX1; ++ix) {
const x = ix * segmentWidth - widthHalf;
// set values to correct vector component
vector[ u ] = x * udir;
vector[ v ] = y * vdir;
vector[ w ] = depthHalf;
// now apply vector to vertex buffer
vertices.push(...vector);
// set values to correct vector component
vector[ u ] = 0;
vector[ v ] = 0;
vector[ w ] = depth > 0 ? 1 : -1;
// now apply vector to normal buffer
normals.push(...vector);
vertexCounter += 1;
}
}
// indices
// 1. you need three indices to draw a single face
// 2. a single segment consists of two faces
// 3. so we need to generate six (2*3) indices per segment
for (let iy = 0; iy < gridY; ++iy) {
for (let ix = 0; ix < gridX; ++ix) {
const a = numberOfVertices + ix + gridX1 * iy;
const b = numberOfVertices + ix + gridX1 * (iy + 1);
const c = numberOfVertices + (ix + 1) + gridX1 * (iy + 1);
const d = numberOfVertices + (ix + 1) + gridX1 * iy;
// faces
indices.push(a, b, d);
indices.push(b, c, d);
}
}
numberOfVertices += vertexCounter;
}
}
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
// adapted from three.js, MIT License Copyright 2010-2018 three.js authors
import Polyhedron from './polyhedron'
const t = ( 1 + Math.sqrt( 5 ) ) / 2;
const vertices = [
- 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0,
0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t,
t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1
];
const indices = [
0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11,
1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8,
3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9,
4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1
];
export default function Icosahedron(radius: number, detail: number) {
return Polyhedron(vertices, indices, radius, detail)
}
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
// adapted from three.js, MIT License Copyright 2010-2018 three.js authors
import { Vec3 } from 'mol-math/linear-algebra'
import { computeVertexNormals, appplyRadius } from '../util'
export default function Polyhedron(_vertices: Helpers.NumberArray, _indices: Helpers.NumberArray, radius: number, detail: number) {
radius = radius || 1;
detail = detail || 0;
const vertices: number[] = [];
const indices: number[] = [];
// the subdivision creates the vertex buffer data
subdivide(detail);
// all vertices should lie on a conceptual sphere with a given radius
appplyRadius(vertices, radius);
const normals = new Float32Array(vertices.length);
computeVertexNormals(vertices, normals)
// this.normalizeNormals(); // smooth normals
return { vertices, indices, normals }
// helper functions
function subdivide(detail: number) {
const a = Vec3.zero()
const b = Vec3.zero()
const c = Vec3.zero()
// iterate over all faces and apply a subdivison with the given detail value
for (let i = 0; i < _indices.length; i += 3) {
// get the vertices of the face
Vec3.fromArray(a, _vertices, _indices[ i + 0 ] * 3)
Vec3.fromArray(b, _vertices, _indices[ i + 1 ] * 3)
Vec3.fromArray(c, _vertices, _indices[ i + 2 ] * 3)
// perform subdivision
subdivideFace(a, b, c, detail)
}
}
function subdivideFace(a: Vec3, b: Vec3, c: Vec3, detail: number) {
const cols = Math.pow(2, detail)
// we use this multidimensional array as a data structure for creating the subdivision
const v: Vec3[][] = []
// construct all of the vertices for this subdivision
for (let i = 0; i <= cols; ++i) {
v[i] = []
const aj = Vec3.zero()
Vec3.lerp(aj, a, c, i / cols)
const bj = Vec3.zero()
Vec3.lerp(bj, b, c, i / cols)
const rows = cols - i
for (let j = 0; j <= rows; ++j) {
if (j === 0 && i === cols) {
v[i][j] = aj
} else {
const abj = Vec3.zero()
Vec3.lerp(abj, aj, bj, j / rows)
v[i][j] = abj
}
}
}
// construct all of the faces
for (let i = 0; i < cols; ++i) {
for (let j = 0; j < 2 * (cols - i) - 1; ++j) {
const k = Math.floor(j / 2)
if (j % 2 === 0) {
vertices.push(...v[i][k + 1], ...v[i + 1][k], ...v[i][k])
} else {
vertices.push(...v[i][k + 1], ...v[i + 1][k + 1], ...v[i + 1][k])
}
}
}
console.log(v)
}
}
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
export function countFromModel(model: any) {}
export function positionFromModel(model: any, array: Float32Array, offset: number) {
}
export function colorFromModel(model: any, params: any, array: Float32Array, offset: number) {
}
export function sizeFromModel(model: any, params: any, array: Float32Array, offset: number) {
}
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Vec3 } from 'mol-math/linear-algebra'
export function normalizeVec3array<T extends Helpers.NumberArray> (a: T) {
const n = a.length
for (let i = 0; i < n; i += 3) {
const x = a[ i ]
const y = a[ i + 1 ]
const z = a[ i + 2 ]
const s = 1 / Math.sqrt(x * x + y * y + z * z)
a[ i ] = x * s
a[ i + 1 ] = y * s
a[ i + 2 ] = z * s
}
}
export function setArrayZero(array: Helpers.NumberArray) {
const n = array.length
for (let i = 0; i < n; ++i) array[i] = 0
}
// iterate over the entire buffer and apply the radius to each vertex
export function appplyRadius(vertices: Helpers.NumberArray, radius: number) {
const v = Vec3.zero()
const n = vertices.length
for (let i = 0; i < n; i += 3) {
Vec3.fromArray(v, vertices, i)
Vec3.normalize(v, v)
Vec3.scale(v, v, radius)
Vec3.toArray(v, vertices, i)
}
}
// indexed vertex normals weighted by triangle areas http://www.iquilezles.org/www/articles/normals/normals.htm
// normal array must contain only zeros
export function computeIndexedVertexNormals<T extends Helpers.NumberArray> (vertices: Helpers.NumberArray, indices: Helpers.NumberArray, normals: T) {
const a = Vec3.zero()
const b = Vec3.zero()
const c = Vec3.zero()
const cb = Vec3.zero()
const ab = Vec3.zero()
for (let i = 0, il = indices.length; i < il; i += 3) {
const ai = indices[ i ] * 3
const bi = indices[ i + 1 ] * 3
const ci = indices[ i + 2 ] * 3
Vec3.fromArray(a, vertices, ai)
Vec3.fromArray(b, vertices, bi)
Vec3.fromArray(c, vertices, ci)
Vec3.sub(cb, c, b)
Vec3.sub(ab, a, b)
Vec3.cross(cb, cb, ab)
normals[ ai ] += cb[ 0 ]
normals[ ai + 1 ] += cb[ 1 ]
normals[ ai + 2 ] += cb[ 2 ]
normals[ bi ] += cb[ 0 ]
normals[ bi + 1 ] += cb[ 1 ]
normals[ bi + 2 ] += cb[ 2 ]
normals[ ci ] += cb[ 0 ]
normals[ ci + 1 ] += cb[ 1 ]
normals[ ci + 2 ] += cb[ 2 ]
}
normalizeVec3array(normals)
return normals
}
// vertex normals for unindexed triangle soup
// normal array must contain only zeros
export function computeVertexNormals<T extends Helpers.NumberArray> (vertices: Helpers.NumberArray, normals: T) {
setArrayZero(normals)
const a = Vec3.zero()
const b = Vec3.zero()
const c = Vec3.zero()
const cb = Vec3.zero()
const ab = Vec3.zero()
for (let i = 0, il = vertices.length; i < il; i += 9) {
Vec3.fromArray(a, vertices, i)
Vec3.fromArray(b, vertices, i + 3)
Vec3.fromArray(c, vertices, i + 6)
Vec3.sub(cb, c, b)
Vec3.sub(ab, a, b)
Vec3.cross(cb, cb, ab)
normals[ i ] = cb[ 0 ]
normals[ i + 1 ] = cb[ 1 ]
normals[ i + 2 ] = cb[ 2 ]
normals[ i + 3 ] = cb[ 0 ]
normals[ i + 4 ] = cb[ 1 ]
normals[ i + 5 ] = cb[ 2 ]
normals[ i + 6 ] = cb[ 0 ]
normals[ i + 7 ] = cb[ 1 ]
normals[ i + 8 ] = cb[ 2 ]
}
normalizeVec3array(normals)
return normals
}
\ No newline at end of file
/**
* 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');
// export type AttributeGroupMutator<T extends AttributesData> = (data: T) => (boolean | void)
// export type AttributeGroupData = { [k: string]: Helpers.TypedArray }
// export type AttributesBuffers<T extends AttributesData> = { [K in keyof T]: REGL.Buffer }
// interface AttributeGroup<T extends AttributeGroup.Data> {
// readonly buffer: REGL.Buffer
// readonly length: number
// readonly data: T
// setCount(size: number): void
// update(mutator: AttributeGroup.Mutator<T>): void
// }
// namespace AttributeGroup {
// export type Data = { [k: string]: Helpers.TypedArray }
// export type Mutator<T extends Data> = (data: T) => (UpdateInfo<T> | void)
// export type UpdateInfo<T extends Data> = boolean | { [k in keyof T]: Attribute.UpdateInfo }
// export type Attributes<T extends Data> = { [K in keyof T]: Attribute<T[K]> }
// export function create<T extends Data>(regl: REGL.Regl, data: T): AttributeGroup<T> {
// const attributes: Attributes<any> = {}
// for (const k of Object.keys(data)) {
// attributes[k] = Attribute.create(regl, data[k])
// }
// return {
// update: (mutator: Mutator<T>) => {
// }
// }
// }
// }
interface Attribute<T extends Helpers.TypedArray> {
readonly buffer: REGL.AttributeConfig
getCount(): number
setCount(count: number): void
getArray(): T
set(index: number, ...values: number[]): void
update(mutator: Attribute.Mutator<T>): void
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, 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 = { ...props, buffer }
const growIfNeeded = function(count: number) {
if (count * itemSize > _array.length) {
const newArray: T = new (_array as any).constructor(count * itemSize)
newArray.set(_array)
_array = newArray
buffer(_array)
}
_count = count
}
return {
buffer: attribute,
getCount: () => _count,
setCount: (count: number) => growIfNeeded(count),
getArray: () => _array,
set: (index: number, ...values: number[]) => {
if (values.length !== itemSize) throw new Error('wrong number of values given')
growIfNeeded(index)
for (let i = 0; i < itemSize; ++i) {
_array[index * itemSize + i] = values[i]
}
buffer.subdata(values, index * itemSize * _array.BYTES_PER_ELEMENT)
},
update: (mutator: Mutator<T>, offset?: number, count?: number) => {
if (offset && count) growIfNeeded(offset + count)
mutator(_array)
buffer(_array)
},
reload: () => buffer(_array)
}
}
}
export default Attribute
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
/*
* This code has been modified from https://github.com/regl-project/regl-camera,
* copyright (c) 2016 Mikola Lysenko. MIT License
*/
const isBrowser = typeof window !== 'undefined'
import REGL = require('regl');
import mouseChange, { MouseModifiers } from 'mol-util/mouse-change'
import mouseWheel from 'mol-util/mouse-wheel'
import { defaults } from 'mol-util'
import { Mat4, Vec3 } from 'mol-math/linear-algebra/3d'
import { clamp, damp } from 'mol-math/interpolate'
export interface CameraUniforms {
projection: Mat4,
}
export interface CameraState {
center: Vec3,
theta: number,
phi: number,
distance: number,
eye: Vec3,
up: Vec3,
fovy: number,
near: number,
far: number,
noScroll: boolean,
flipY: boolean,
dtheta: number,
dphi: number,
rotationSpeed: number,
zoomSpeed: number,
renderOnDirty: boolean,
damping: number,
minDistance: number,
maxDistance: number,
}
export interface Camera {
update: (props: any, block: any) => void,
setState: (newState: CameraState) => void,
getState: () => CameraState,
isDirty: () => boolean
}
export namespace Camera {
export function create (regl: REGL.Regl, element: HTMLElement, initialState: Partial<CameraState> = {}): Camera {
const state: CameraState = {
center: defaults(initialState.center, Vec3.zero()),
theta: defaults(initialState.theta, 0),
phi: defaults(initialState.phi, 0),
distance: Math.log(defaults(initialState.distance, 10.0)),
eye: Vec3.zero(),
up: defaults(initialState.up, Vec3.create(0, 1, 0)),
fovy: defaults(initialState.fovy, Math.PI / 4.0),
near: defaults(initialState.near, 0.01),
far: defaults(initialState.far, 1000.0),
noScroll: defaults(initialState.noScroll, false),
flipY: defaults(initialState.flipY, false),
dtheta: 0,
dphi: 0,
rotationSpeed: defaults(initialState.rotationSpeed, 1),
zoomSpeed: defaults(initialState.zoomSpeed, 1),
renderOnDirty: defaults(initialState.renderOnDirty, false),
damping: defaults(initialState.damping, 0.9),
minDistance: Math.log(defaults(initialState.minDistance, 0.1)),
maxDistance: Math.log(defaults(initialState.maxDistance, 1000))
}
const view = Mat4.identity()
const projection = Mat4.identity()
const right = Vec3.create(1, 0, 0)
const front = Vec3.create(0, 0, 1)
let dirty = false
let ddistance = 0
let prevX = 0
let prevY = 0
if (isBrowser) {
const source = element || regl._gl.canvas
const getWidth = function () {
return element ? element.offsetWidth : window.innerWidth
}
const getHeight = function () {
return element ? element.offsetHeight : window.innerHeight
}
mouseChange(source, function (buttons: number, x: number, y: number, mods: MouseModifiers) {
if (buttons & 1) {
const dx = (x - prevX) / getWidth()
const dy = (y - prevY) / getHeight()
state.dtheta += state.rotationSpeed * 4.0 * dx
state.dphi += state.rotationSpeed * 4.0 * dy
dirty = true;
}
prevX = x
prevY = y
})
mouseWheel(source, function (dx: number, dy: number) {
ddistance += dy / getHeight() * state.zoomSpeed
dirty = true;
}, state.noScroll)
}
function dampAndMarkDirty (x: number) {
const xd = damp(x, state.damping)
if (Math.abs(xd) < 0.1) return 0
dirty = true;
return xd
}
function setState (newState: Partial<CameraState> = {}) {
Object.assign(state, newState)
const { center, eye, up, dtheta, dphi } = state
state.theta += dtheta
state.phi = clamp(state.phi + dphi, -Math.PI / 2.0, Math.PI / 2.0)
state.distance = clamp(state.distance + ddistance, state.minDistance, state.maxDistance)
state.dtheta = dampAndMarkDirty(dtheta)
state.dphi = dampAndMarkDirty(dphi)
ddistance = dampAndMarkDirty(ddistance)
const theta = state.theta
const phi = state.phi
const r = Math.exp(state.distance)
const vf = r * Math.sin(theta) * Math.cos(phi)
const vr = r * Math.cos(theta) * Math.cos(phi)
const vu = r * Math.sin(phi)
for (let i = 0; i < 3; ++i) {
eye[i] = center[i] + vf * front[i] + vr * right[i] + vu * up[i]
}
Mat4.lookAt(view, eye, center, up)
}
const injectContext = regl({
context: {
view: () => view,
dirty: () => dirty,
projection: (context: REGL.DefaultContext) => {
Mat4.perspective(
projection,
state.fovy,
context.viewportWidth / context.viewportHeight,
state.near,
state.far
)
if (state.flipY) { projection[5] *= -1 }
return projection
}
},
uniforms: { // TODO
view: regl.context('view' as any),
projection: regl.context('projection' as any)
}
})
function update (props: any, block: any) {
setState()
injectContext(props, block)
dirty = false
}
return {
update,
setState,
getState: () => Object.assign({}, state),
isDirty: () => dirty
}
}
}
/**
* 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 { Mat4, Quat, Vec3 } from 'mol-math/linear-algebra'
import { defaults } from 'mol-util';
const tmpMat4 = Mat4()
type ModelProps = {
rotation?: Quat,
position?: Vec3,
scale?: Vec3
}
function createModel(regl: REGL.Regl, props: ModelProps = {}) {
const transform = Mat4.identity()
const rotation = defaults(props.rotation, Quat.identity())
const position = defaults(props.position, Vec3.zero())
const scale = defaults(props.scale, Vec3.create(1, 1, 1))
const draw = regl({
context: { transform, rotation, position, scale },
uniforms: {
model(ctx: REGL.DefaultContext, props: any = {}) {
const model = Mat4.identity()
if ('rotation' in props) Quat.copy(rotation, props.rotation)
if ('position' in props) Vec3.copy(position, props.position)
if ('scale' in props) Vec3.copy(scale, props.scale)
Mat4.translate(model, model, position)
Mat4.mul(model, model, Mat4.fromQuat(tmpMat4, rotation))
Mat4.scale(model, model, scale)
if ('transform' in props) Mat4.mul(model, props.transform, model)
Mat4.copy(transform, model)
return model
}
}
})
return Object.assign(draw, {
get transform() { return transform },
get position() { return position },
get rotation() { return rotation },
get scale() { return scale },
})
}
export default createModel
\ No newline at end of file
/**
* 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 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 }
export type Attributes<T extends AttributesData> = { [K in keyof T]: Attribute<T[K]> }
export type AttributesBuffers<T extends AttributesData> = { [K in keyof T]: REGL.AttributeConfig }
export interface Renderable<T extends AttributesData> {
draw(): void
}
export { PointRenderable, MeshRenderable }
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment