diff --git a/src/apps/render-test/state.ts b/src/apps/render-test/state.ts index 5c519715b6f1c322db26a7c57fc2e8b22e99ab32..f4dc1f942e7d44ecebcbabc0696e85495e5ece15 100644 --- a/src/apps/render-test/state.ts +++ b/src/apps/render-test/state.ts @@ -4,23 +4,18 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import REGL = require('regl'); import { ValueBox } from 'mol-util/value-cell' -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 Model from 'mol-gl/model'; -import { calculateTextureInfo } from 'mol-gl/util'; +import { createRenderer, createRenderObject, RenderObject } from 'mol-gl/renderer' +import { createColorTexture } 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 { Run } from 'mol-task' -// 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) @@ -39,35 +34,12 @@ async function getPdb(pdb: string) { import mcubes from './mcubes' export default class State { - regl: REGL.Regl - - 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_blend_minmax', - 'ANGLE_instanced_arrays' - ], - // profile: true - }) - - const camera = Camera.create(regl, container, { - center: Vec3.create(0, 0, 0), - near: 0.01, - far: 1000 - }) + async initRenderer (container: HTMLDivElement) { + const renderer = createRenderer(container) 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 = ValueBox(new Float32Array([0, -1, 0, -1, 0, 0, 1, 1, 0])) const normal = ValueBox(new Float32Array([0, 0, 0, 0, 0, 0, 0, 0, 0])) @@ -81,43 +53,23 @@ export default class State { Mat4.setTranslation(m4, p2) Mat4.toArray(m4, transformArray2.value, 32) - const colorTexInfo = calculateTextureInfo(3, 3) - const color = new Uint8Array(colorTexInfo.length) - color.set([ + const color = ValueBox(createColorTexture(3)) + color.value.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, { + const points = createRenderObject('point', { position, transform: transformArray1 }) - const mesh = MeshRenderable.create(regl, - { - position, - normal, - transform: transformArray2 - }, - { - colorTex, - colorTexSize: [ colorTexInfo.width, colorTexInfo.height ] - } - ) + const mesh = createRenderObject('mesh', { + position, + normal, + color, + transform: transformArray2 + }) const sphere = Icosahedron(1, 1) console.log(sphere) @@ -125,7 +77,7 @@ export default class State { const box = Box(1, 1, 1, 1, 1, 1) console.log(box) - const points2 = PointRenderable.create(regl, { + const points2 = createRenderObject('point', { position: ValueBox(new Float32Array(box.vertices)), transform: transformArray1 }) @@ -133,61 +85,21 @@ export default class State { let rr = 1; function cubesF(x: number, y: number, z: number) { return x * x + y * y + z * z - rr * rr; - // const a = ca; - // const t = (x + y + z + a); - // return x * x * x + y * y * y + z * z * z + a * a * a - t * t * t; } let cubes = await mcubes(cubesF); - const makeCubesMesh = () => MeshRenderable.create(regl, - { - position: cubes.surface.vertexBuffer, - normal: cubes.surface.normalBuffer as any, - transform: transformArray1 - }, - { - 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 - }, - cubes.surface.indexBuffer.value - ); - - let mesh2 = makeCubesMesh(); - - // const makeCubes = async () => { - // rr = Math.random(); - // cubes = await mcubes(cubesF, cubes); - // mesh2 = makeCubesMesh(); - // setTimeout(makeCubes, 1000 / 15); - // }; - // makeCubes(); - - // 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, transformArray1) - // }, - // { - // 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 makeCubesMesh = () => createRenderObject('mesh', { + position: cubes.surface.vertexBuffer, + normal: cubes.surface.normalBuffer as any, + color, + transform: transformArray1, + }, cubes.surface.indexBuffer.value); + const mesh2 = makeCubesMesh(); + renderer.add(mesh2) function createSpacefills (structure: Structure) { - const spacefills: UnitRepresentation[] = [] + const spacefills: RenderObject[] = [] const { atoms, units } = structure; const unitIds = AtomSet.unitIds(atoms); for (let i = 0, _i = unitIds.length; i < _i; i++) { @@ -195,57 +107,16 @@ export default class State { 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) + const spacefill = Spacefill() + spacefills.push(...spacefill.create(unit, atomGroup, {})) } return spacefills } - const structures = await getPdb('1crn') const spacefills = createSpacefills(structures[0]) + spacefills.forEach(renderer.add) - structures[0] - - 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]}) - spacefills.forEach(r => r.draw()) - // 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 + renderer.frame() } constructor() { diff --git a/src/apps/render-test/ui.tsx b/src/apps/render-test/ui.tsx index ed518d906ce262bfe999d10f22f8cbb944f64c71..dbdf41f3b72e11bacaf22bf4055e17d29b2ffc4d 100644 --- a/src/apps/render-test/ui.tsx +++ b/src/apps/render-test/ui.tsx @@ -12,7 +12,7 @@ export default class Root extends React.Component<{ state: State }, { initialize state = { initialized: false } componentDidMount() { - if (this.canvasContainer) this.props.state.initRegl(this.canvasContainer).then(() => this.setState({ initialized: true })) + if (this.canvasContainer) this.props.state.initRenderer(this.canvasContainer).then(() => this.setState({ initialized: true })) } render() { diff --git a/src/mol-geo/representation/structure/index.ts b/src/mol-geo/representation/structure/index.ts index d4d66ed595134b44b8d963d3de9338cca5f05b41..ee42a362387558b5251b6388379709bb8b002877 100644 --- a/src/mol-geo/representation/structure/index.ts +++ b/src/mol-geo/representation/structure/index.ts @@ -5,15 +5,15 @@ */ import { AtomGroup, AtomSet, Structure, Unit } from 'mol-model/structure'; +import { RenderObject } from 'mol-gl/renderer'; export interface RepresentationProps { } export interface UnitRepresentation { - create: (unit: Unit, atomGroup: AtomGroup, props: RepresentationProps) => boolean, + create: (unit: Unit, atomGroup: AtomGroup, props: RepresentationProps) => RenderObject[], update: (props: RepresentationProps) => boolean, - draw: () => void } // export interface StructureRepresentation { diff --git a/src/mol-geo/representation/structure/spacefill.ts b/src/mol-geo/representation/structure/spacefill.ts index da271c975e8523cf6c4c82148d3cfd9478f51e85..c7135a5b0a1c2ba04d63ce61085ed9102269bf09 100644 --- a/src/mol-geo/representation/structure/spacefill.ts +++ b/src/mol-geo/representation/structure/spacefill.ts @@ -4,11 +4,10 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import REGL = require('regl'); import { ValueBox } from 'mol-util/value-cell' -import { MeshRenderable, Renderable } from 'mol-gl/renderable' -import { calculateTextureInfo } from 'mol-gl/util'; +import { createRenderObject, RenderObject } from 'mol-gl/renderer' +import { createColorTexture } 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' @@ -16,11 +15,11 @@ 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 { +export default function Spacefill(): UnitRepresentation { let vertices: Float32Array let normals: Float32Array - const renderables: Renderable<any>[] = [] + const renderObjects: RenderObject[] = [] return { create: (unit: Unit, atomGroup: AtomGroup, props: RepresentationProps) => { @@ -59,48 +58,25 @@ export default function Spacefill(regl: REGL.Regl): UnitRepresentation { 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, + const color = ValueBox(createColorTexture(1)) + color.value.set([ 0, 0, 255 ]) + + const spheres = createRenderObject( + 'mesh', { position: ValueBox(new Float32Array(vertices)), normal: ValueBox(new Float32Array(normals)), + color, transform: ValueBox(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) + renderObjects.push(spheres) - return true + return renderObjects }, - update: (props: RepresentationProps) => false, - draw: () => renderables.forEach(r => r.draw()) + update: (props: RepresentationProps) => false } } diff --git a/src/mol-gl/camera.ts b/src/mol-gl/camera.ts index 3a22d2e54c2337eadd5140c56ed1e094b0d91aac..f3a3ffa04848428f9f7c1c5d4600bbe6f1e681e7 100644 --- a/src/mol-gl/camera.ts +++ b/src/mol-gl/camera.ts @@ -82,7 +82,7 @@ export namespace Camera { const right = Vec3.create(1, 0, 0) const front = Vec3.create(0, 0, 1) - let dirty = false + let dirty = true let ddistance = 0 let prevX = 0 diff --git a/src/mol-gl/renderable.ts b/src/mol-gl/renderable.ts index 2d85d090872039bb521f0705034cc9be8e20d0b2..e6f246de63a6d33242fb9c8cbcee2693a5de3bd0 100644 --- a/src/mol-gl/renderable.ts +++ b/src/mol-gl/renderable.ts @@ -16,6 +16,9 @@ export type AttributesBuffers<T extends AttributesData> = { [K in keyof T]: REGL export interface Renderable<T extends AttributesData> { draw(): void + // isPicking: () => boolean + // isVisible: () => boolean + // isTransparent: () => boolean } 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 9531cd989d1f71df4f962e0fffd5bbabfc7ef025..7a3d038d8c7e8f16251b32a8c355b6394ac40194 100644 --- a/src/mol-gl/renderable/mesh.ts +++ b/src/mol-gl/renderable/mesh.ts @@ -8,7 +8,8 @@ import REGL = require('regl'); import { ValueBox } from 'mol-util/value-cell' import { Renderable } from '../renderable' -import { getBuffers, createTransformAttributes, fillSerial } from './util' +import { ColorTexture } from '../util' +import { getBuffers, createTransformAttributes, fillSerial, createColorUniforms } from './util' import Attribute from '../attribute'; import { MeshShaders } from '../shaders' @@ -21,6 +22,7 @@ namespace Mesh { position: { type: Float32Array, itemSize: 3 } normal: { type: Float32Array, itemSize: 3 } transform: { type: Float32Array, itemSize: 16 } + color: { type: ColorTexture, itemSize: 16 } } export type Data = { [K in keyof DataType]: DataType[K]['type'] } export type BoxedData = { [K in keyof Data]: ValueBox<Data[K]> } @@ -40,6 +42,7 @@ namespace Mesh { uniforms: { objectId: uniforms.objectId || 0, instanceCount, + ...createColorUniforms(regl, data.color), ...uniforms }, attributes: getBuffers({ diff --git a/src/mol-gl/renderable/util.ts b/src/mol-gl/renderable/util.ts index 156728132654c2862392b04852a19b0620656415..f536db6099e2dbf022a0c8756cdd32a7d3d03083 100644 --- a/src/mol-gl/renderable/util.ts +++ b/src/mol-gl/renderable/util.ts @@ -7,6 +7,7 @@ import REGL = require('regl'); import { ValueBox } from 'mol-util/value-cell' +import { ColorTexture } from '../util'; import { Attributes, AttributesData, AttributesBuffers } from '../renderable' import Attribute from '../attribute' @@ -23,6 +24,22 @@ export function createTransformAttributes (regl: REGL.Regl, transform: ValueBox< } } +export function createColorUniforms (regl: REGL.Regl, color: ValueBox<ColorTexture>) { + const colorTex = regl.texture({ + width: color.value.width, + height: color.value.height, + format: 'rgb', + type: 'uint8', + wrapS: 'clamp', + wrapT: 'clamp', + data: color.value + }) + return { + colorTex, + colorTexSize: [ color.value.width, color.value.height ] + } +} + export function getBuffers<T extends AttributesData>(attributes: Attributes<T>): AttributesBuffers<T> { const buffers: AttributesBuffers<any> = {} for (const k of Object.keys(attributes)) { diff --git a/src/mol-gl/renderer.ts b/src/mol-gl/renderer.ts new file mode 100644 index 0000000000000000000000000000000000000000..ba4bc48ecad66f48db3a6bf5e3d4cac7c1d30e3b --- /dev/null +++ b/src/mol-gl/renderer.ts @@ -0,0 +1,116 @@ +/** + * 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 './context' +import { Camera } from './camera' +import { PointRenderable, MeshRenderable, Renderable } from './renderable' + +import { Vec3, Mat4 } from 'mol-math/linear-algebra' + +let _renderObjectId = 0; +function getNextId() { + return _renderObjectId++ % 0x7FFFFFFF; +} + +export interface RenderObject { + id: number + type: 'mesh' | 'point' + data: any + elements: any + uniforms: any +} + +export function createRenderObject(type: 'mesh' | 'point', data: any, elements?: any, uniforms?: any) { + return { id: getNextId(), type, data, elements, uniforms } +} + +export interface Renderer { + add: (o: RenderObject) => void + remove: (o: RenderObject) => void + draw: () => void + frame: () => void +} + +export function createRenderable(regl: REGL.Regl, o: RenderObject) { + switch (o.type) { + case 'mesh': return MeshRenderable.create(regl, o.data, o.uniforms || {}, o.elements) + case 'point': return PointRenderable.create(regl, o.data) + } +} + +export function createRenderer(container: HTMLDivElement): Renderer { + const renderableList: Renderable<any>[] = [] + const objectIdRenderableMap: { [k: number]: Renderable<any> } = {} + + 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), + near: 0.01, + far: 1000 + }) + + 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), + '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 + } + }) + + const draw = () => { + camera.update((state: any) => { + if (!camera.isDirty()) return; + baseContext(() => { + // console.log(ctx) + regl.clear({color: [0, 0, 0, 1]}) + // TODO painters sort, filter visible, filter picking, visibility culling? + renderableList.forEach(r => { + r.draw() + }) + }) + }, undefined) + } + + return { + add: (o: RenderObject) => { + const renderable = createRenderable(regl, o) + renderableList.push(renderable) + objectIdRenderableMap[o.id] = renderable + }, + remove: (o: RenderObject) => { + if (o.id in objectIdRenderableMap) { + // TODO + // objectIdRenderableMap[o.id].destroy() + delete objectIdRenderableMap[o.id] + } + }, + draw, + frame: () => { + regl.frame((ctx) => draw()) + } + } +} \ No newline at end of file diff --git a/src/mol-gl/util.ts b/src/mol-gl/util.ts index c055b15c8b54f385052b30a72fabbfe11c91a6c1..5ce7ca89e4f157caa2c9b50e77899f0f9b5ea9f7 100644 --- a/src/mol-gl/util.ts +++ b/src/mol-gl/util.ts @@ -10,4 +10,18 @@ export function calculateTextureInfo (n: number, itemSize: number) { width = width + (itemSize - (width % itemSize)) % itemSize const height = width > 0 ? Math.ceil(n * itemSize / width) : 0 return { width, height, length: width * height * itemSize } +} + +export interface ColorTexture extends Uint8Array { + width: number, + height: number +} + +export function createColorTexture (n: number): ColorTexture { + const colorTexInfo = calculateTextureInfo(n, 3) + const colorTexture = new Uint8Array(colorTexInfo.length) + return Object.assign(colorTexture, { + width: colorTexInfo.width, + height: colorTexInfo.height + }) } \ No newline at end of file