diff --git a/src/apps/render-test/state.ts b/src/apps/render-test/state.ts index 982a0284b71e9f41f0606af4fe00d8e287d6ba7a..097c967759cb612993c907a6edaeb6ae5ffcdeaa 100644 --- a/src/apps/render-test/state.ts +++ b/src/apps/render-test/state.ts @@ -15,18 +15,41 @@ 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' +import Spacefill from 'mol-geo/representation/structure/spacefill' + +import CIF from 'mol-io/reader/cif' +import Computation from 'mol-util/computation' +import { AtomSet, Structure } from 'mol-model/structure' +import { UnitRepresentation } from 'mol-geo/representation/structure'; + +async function parseCif(data: string|Uint8Array) { + const comp = CIF.parse(data) + const ctx = Computation.observable({ + updateRateMs: 250 + }) + const parsed = await comp(ctx); + if (parsed.isError) throw parsed; + return parsed +} + +async function getPdb(pdb: string) { + const data = await fetch(`https://files.rcsb.org/download/${pdb}.cif`) + const parsed = await parseCif(await data.text()) + const structure = Structure.ofData({ kind: 'mmCIF', data: CIF.schema.mmCIF(parsed.result.blocks[0]) }) + return structure +} export default class State { regl: REGL.Regl - initRegl (container: HTMLDivElement) { + async 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_disjoint_timer_query', 'EXT_blend_minmax', 'ANGLE_instanced_arrays' ], @@ -34,7 +57,9 @@ export default class State { }) const camera = Camera.create(regl, container, { - center: Vec3.create(0, 0, 0) + center: Vec3.create(0, 0, 0), + near: 0.01, + far: 1000 }) const p1 = Vec3.create(0, 4, 0) @@ -57,8 +82,6 @@ export default class State { Mat4.setTranslation(m4, p2) Mat4.toArray(m4, transformArray2, 32) - - const colorTexInfo = calculateTextureInfo(3, 3) const color = new Uint8Array(colorTexInfo.length) color.set([ @@ -110,22 +133,44 @@ export default class State { 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 }), + position: Attribute.create(regl, new Float32Array(sphere.vertices), { size: 3 }), + normal: Attribute.create(regl, new Float32Array(sphere.normals), { size: 3 }), ...createTransformAttributes(regl, transformArray2) }, { colorTex, colorTexSize: [ colorTexInfo.width, colorTexInfo.height ], - 'light.position': Vec3.create(0, 0, -20), + 'light.position': Vec3.create(0, 0, -100), '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 + // box.indices ) + function createSpacefills (structure: Structure) { + const spacefills: UnitRepresentation[] = [] + const { atoms, units } = structure; + const unitIds = AtomSet.unitIds(atoms); + for (let i = 0, _i = unitIds.length; i < _i; i++) { + const unitId = unitIds[i]; + const unit = units[unitId]; + const atomGroup = AtomSet.unitGetByIndex(atoms, i); + + const spacefill = Spacefill(regl) + spacefill.create(unit, atomGroup, {}) + console.log('spacefill', spacefill) + spacefills.push(spacefill) + } + return spacefills + } + + const structures = await getPdb('1crn') + const spacefills = createSpacefills(structures[0]) + + structures[0] + const baseContext = regl({ context: { model: Mat4.identity(), @@ -143,6 +188,7 @@ export default class State { baseContext(() => { // console.log(ctx) regl.clear({color: [0, 0, 0, 1]}) + spacefills.forEach(r => r.draw()) position.update(array => { array[0] = Math.random() }) // points.update(a => { a.position[0] = Math.random() }) // mesh.draw() diff --git a/src/apps/render-test/ui.tsx b/src/apps/render-test/ui.tsx index fd88f2f5f1473ab7ef998391be930a4e89ad6a03..ed518d906ce262bfe999d10f22f8cbb944f64c71 100644 --- a/src/apps/render-test/ui.tsx +++ b/src/apps/render-test/ui.tsx @@ -7,16 +7,16 @@ import * as React from 'react' import State from './state' -export default class Root extends React.Component<{ state: State }, { }> { +export default class Root extends React.Component<{ state: State }, { initialized: boolean }> { private canvasContainer: HTMLDivElement | null = null; + state = { initialized: false } componentDidMount() { - if (this.canvasContainer) this.props.state.initRegl(this.canvasContainer) + if (this.canvasContainer) this.props.state.initRegl(this.canvasContainer).then(() => this.setState({ initialized: true })) } render() { return <div ref={elm => this.canvasContainer = elm} style={{ position: 'absolute', top: 0, right: 0, left: 0, bottom: 0, overflow: 'hidden' }}> - </div> } } \ No newline at end of file diff --git a/src/mol-geo/primitive/polyhedron.ts b/src/mol-geo/primitive/polyhedron.ts index 11c2f9798541386cc755b2acba987b684901704c..db17d24242941bd782118e1770027c4a81288b6a 100644 --- a/src/mol-geo/primitive/polyhedron.ts +++ b/src/mol-geo/primitive/polyhedron.ts @@ -88,7 +88,5 @@ export default function Polyhedron(_vertices: Helpers.NumberArray, _indices: Hel } } } - - console.log(v) } } \ No newline at end of file diff --git a/src/mol-geo/representation/structure/index.ts b/src/mol-geo/representation/structure/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..d4d66ed595134b44b8d963d3de9338cca5f05b41 --- /dev/null +++ b/src/mol-geo/representation/structure/index.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 { AtomGroup, AtomSet, Structure, Unit } from 'mol-model/structure'; + +export interface RepresentationProps { + +} + +export interface UnitRepresentation { + create: (unit: Unit, atomGroup: AtomGroup, props: RepresentationProps) => boolean, + update: (props: RepresentationProps) => boolean, + draw: () => void +} + +// export interface StructureRepresentation { +// create: (structure: Structure, props: RepresentationProps) => boolean, +// update: (props: RepresentationProps) => boolean +// } + +export class StructureRepresentation { + constructor () { + + } + create (structure: Structure, props: RepresentationProps) { + const { atoms, units } = structure; + const unitIds = AtomSet.unitIds(atoms); + for (let i = 0, _i = unitIds.length; i < _i; i++) { + const unitId = unitIds[i]; + const unit = units[unitId]; + const atomGroup = AtomSet.unitGetByIndex(atoms, i); + + } + + return true + } + update: (props: RepresentationProps) => false +} \ No newline at end of file diff --git a/src/mol-geo/representation/structure/spacefill.ts b/src/mol-geo/representation/structure/spacefill.ts index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7f609e878e1d93f1b4a0a0b479bcff9b85cee719 100644 --- a/src/mol-geo/representation/structure/spacefill.ts +++ b/src/mol-geo/representation/structure/spacefill.ts @@ -0,0 +1,106 @@ +/** + * 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 { MeshRenderable, Renderable } from 'mol-gl/renderable' +import { createTransformAttributes } from 'mol-gl/renderable/util'; +import Attribute from 'mol-gl/attribute'; +import { calculateTextureInfo } from 'mol-gl/util'; +import Icosahedron from 'mol-geo/primitive/icosahedron' +import { Vec3, Mat4 } from 'mol-math/linear-algebra' +import { OrderedSet } from 'mol-data/int' +import { Atom, AtomGroup, Unit } from 'mol-model/structure'; +import P from 'mol-model/structure/query/properties'; +import { RepresentationProps, UnitRepresentation } from './index'; + +export default function Spacefill(regl: REGL.Regl): UnitRepresentation { + let vertices: Float32Array + let normals: Float32Array + + const renderables: Renderable<any>[] = [] + + return { + create: (unit: Unit, atomGroup: AtomGroup, props: RepresentationProps) => { + const atomCount = OrderedSet.size(atomGroup.atoms) + + const l = Atom.Location(); + l.unit = unit; + + const sphere = Icosahedron(1, 0) + const vertexCount = sphere.vertices.length / 3 + + vertices = new Float32Array(atomCount * vertexCount * 3) + normals = new Float32Array(atomCount * vertexCount * 3) + + const v = Vec3.zero() + const m = Mat4.identity() + + for (let i = 0; i < atomCount; i++) { + l.atom = OrderedSet.getAt(atomGroup.atoms, i) + + v[0] = P.atom.x(l) + v[1] = P.atom.y(l) + v[2] = P.atom.z(l) + Mat4.setTranslation(m, v) + + for (let j = 0; j < vertexCount; ++j) { + Vec3.fromArray(v, sphere.vertices, j * 3) + Vec3.transformMat4(v, v, m) + Vec3.toArray(v, vertices, i * vertexCount * 3 + j * 3) + } + + normals.set(sphere.normals, i * vertexCount * 3) + } + + const transformArray = new Float32Array(16) + const m4 = Mat4.identity() + Mat4.toArray(m4, transformArray, 0) + + 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 + }) + + const spheres = MeshRenderable.create(regl, + { + position: Attribute.create(regl, new Float32Array(vertices), { size: 3 }), + normal: Attribute.create(regl, new Float32Array(normals), { size: 3 }), + ...createTransformAttributes(regl, transformArray) + }, + { + colorTex, + colorTexSize: [ colorTexInfo.width, colorTexInfo.height ], + 'light.position': Vec3.create(0, 0, -100), + '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 + } + ) + + // console.log({ vertices, normals, vertexCount, atomCount }) + + renderables.push(spheres) + + return true + }, + update: (props: RepresentationProps) => false, + draw: () => renderables.forEach(r => r.draw()) + } +} diff --git a/src/mol-gl/attribute.ts b/src/mol-gl/attribute.ts index 1133d5e7b57adf73c649c132112d0e7a47433408..c2fc73eb2b37a22dcdf0e502660fd7c5c6d4e30e 100644 --- a/src/mol-gl/attribute.ts +++ b/src/mol-gl/attribute.ts @@ -46,6 +46,7 @@ interface Attribute<T extends Helpers.TypedArray> { set(index: number, ...values: number[]): void update(mutator: Attribute.Mutator<T>): void reload(): void + destroy(): void } interface AttributeProps { @@ -58,6 +59,8 @@ interface AttributeProps { namespace Attribute { export type Mutator<T extends Helpers.TypedArray> = (data: T) => (UpdateInfo | void) export type UpdateInfo = boolean | { offset: number, count: number } + export type ArrayCell<T> = { array: ReferenceCell<T> } + export type ReferenceCell<T> = { readonly version: number, readonly value: T } export function create<T extends Helpers.TypedArray>(regl: REGL.Regl, array: T, props: AttributeProps): Attribute<T> { const itemSize = props.size @@ -94,7 +97,8 @@ namespace Attribute { mutator(_array) buffer(_array) }, - reload: () => buffer(_array) + reload: () => buffer(_array), + destroy: () => buffer.destroy() } } } diff --git a/src/mol-gl/renderable/mesh.ts b/src/mol-gl/renderable/mesh.ts index e0caa9da7a40c50fe5543db7585885ca2ad67c6d..91fa3ffd70fe17b1082fe290b3abe65cc2a90f5d 100644 --- a/src/mol-gl/renderable/mesh.ts +++ b/src/mol-gl/renderable/mesh.ts @@ -34,15 +34,15 @@ namespace Mesh { export type Attributes = { [K in keyof Data]: Attribute<Data[K]> } export function create(regl: REGL.Regl, attributes: Attributes, uniforms: Uniforms, elements?: Helpers.UintArray): Renderable<Data> { - console.log('mesh', { - count: attributes.position.getCount(), - instances: attributes.transformColumn0.getCount(), - attributes, - uniforms - }) + // console.log('mesh', { + // count: attributes.position.getCount(), + // instances: attributes.transformColumn0.getCount(), + // attributes, + // uniforms + // }) const instanceCount = attributes.transformColumn0.getCount() const instanceId = fillSerial(new Float32Array(instanceCount)) - console.log(instanceId) + // console.log(instanceId) const command = regl({ ...MeshShaders, uniforms: { diff --git a/src/mol-gl/shader/mesh.frag b/src/mol-gl/shader/mesh.frag index 4a224d852dcf63e0c5f033f95b6d9997b8c0c87f..c542564e3e23c17c478218fde4e895fb556f130e 100644 --- a/src/mol-gl/shader/mesh.frag +++ b/src/mol-gl/shader/mesh.frag @@ -4,7 +4,7 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -precision mediump float; +precision highp float; struct Light { vec3 position; diff --git a/src/mol-gl/shader/mesh.vert b/src/mol-gl/shader/mesh.vert index 3c760d523329804a185ff3a0af346911afbed687..7396279ec1b08e5d51e9b49d856e343727fac928 100644 --- a/src/mol-gl/shader/mesh.vert +++ b/src/mol-gl/shader/mesh.vert @@ -6,7 +6,7 @@ #define INSTANCE_COLOR -precision mediump float; +precision highp float; uniform mat4 projection, model, view; diff --git a/src/mol-math/linear-algebra/3d.ts b/src/mol-math/linear-algebra/3d.ts index 727ef49ee3d61f75372de38bd44c84aafee1066c..fb76874a102916e6ac63dfe08038746f91757352 100644 --- a/src/mol-math/linear-algebra/3d.ts +++ b/src/mol-math/linear-algebra/3d.ts @@ -897,6 +897,9 @@ export namespace Vec3 { return out; } + /** + * Performs a linear interpolation between two Vec3's + */ export function lerp(out: Vec3, a: Vec3, b: Vec3, t: number) { const ax = a[0], ay = a[1], @@ -907,12 +910,65 @@ export namespace Vec3 { return out; } + /** + * Performs a hermite interpolation with two control points + */ + export function hermite(out: Vec3, a: Vec3, b: Vec3, c: Vec3, d: Vec3, t: number) { + const factorTimes2 = t * t; + const factor1 = factorTimes2 * (2 * t - 3) + 1; + const factor2 = factorTimes2 * (t - 2) + t; + const factor3 = factorTimes2 * (t - 1); + const factor4 = factorTimes2 * (3 - 2 * t); + + out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4; + out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4; + out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4; + + return out; + } + + /** + * Performs a bezier interpolation with two control points + */ + export function bezier(out: Vec3, a: Vec3, b: Vec3, c: Vec3, d: Vec3, t: number) { + const inverseFactor = 1 - t; + const inverseFactorTimesTwo = inverseFactor * inverseFactor; + const factorTimes2 = t * t; + const factor1 = inverseFactorTimesTwo * inverseFactor; + const factor2 = 3 * t * inverseFactorTimesTwo; + const factor3 = 3 * factorTimes2 * inverseFactor; + const factor4 = factorTimes2 * t; + + out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4; + out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4; + out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4; + + return out; + } + + /** + * Generates a random vector with the given scale + */ + export function random(out: Vec3, scale: number) { + const r = Math.random() * 2.0 * Math.PI; + const z = (Math.random() * 2.0) - 1.0; + const zScale = Math.sqrt(1.0-z*z) * scale; + + out[0] = Math.cos(r) * zScale; + out[1] = Math.sin(r) * zScale; + out[2] = z * scale; + return out; + } + + /** + * Transforms the Vec3 with a Mat4. 4th vector component is implicitly '1' + */ export function transformMat4(out: Vec3, a: Vec3, m: Mat4) { const x = a[0], y = a[1], z = a[2], - w = (m[3] * x + m[7] * y + m[11] * z + m[15]) || 1.0; - out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w; - out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w; - out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w; + w = 1 / ((m[3] * x + m[7] * y + m[11] * z + m[15]) || 1.0); + out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) * w; + out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) * w; + out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) * w; return out; }