diff --git a/src/apps/render-test/mcubes.ts b/src/apps/render-test/mcubes.ts new file mode 100644 index 0000000000000000000000000000000000000000..0c1b4ea9568928e13d9bce1a7b5d6e4cbb318243 --- /dev/null +++ b/src/apps/render-test/mcubes.ts @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Run } from 'mol-task' +import { compute } from 'mol-geo/util/marching-cubes/algorithm' +import { Surface } from 'mol-geo/shape/surface' +import { Tensor } from 'mol-math/linear-algebra' + +function fillField(tensor: Tensor, f: (x: number, y: number, z: number) => number, min: number[], max: number[]): Tensor { + const { space: { set, dimensions: [ii, jj, kk] }, data } = tensor; + + const dx = (max[0] - min[0]) / ii; + const dy = (max[1] - min[1]) / jj; + const dz = (max[2] - min[2]) / kk; + + for (let i = 0, x = min[0]; i < ii; i++, x += dx) { + for (let j = 0, y = min[1]; j < jj; j++, y += dy) { + for (let k = 0, z = min[2]; k < kk; k++, z += dz) { + set(data, i, j, k, f(x, y, z)); + } + } + } + + return tensor +} + +export default async function computeSurface(f: (x: number, y: number, z: number) => number, data?: { field: Tensor, surface: Surface }) { + let field: Tensor; + if (data) field = data.field; + else { + const space = Tensor.Space([30, 30, 30], [0, 1, 2]); + field = Tensor.create(space, space.create(Float32Array)); + } + + fillField(field, f, [-1.1, -1.1, -1.1], [1.1, 1.1, 1.1]); + const surface = await Run(compute({ + scalarField: field, + isoLevel: 0, + buffers: data ? { + vertex: data.surface.vertexBuffer, + index: data.surface.indexBuffer, + normal: data.surface.normalBuffer + } : void 0 + })); + return { surface, field }; +} \ No newline at end of file diff --git a/src/apps/render-test/state.ts b/src/apps/render-test/state.ts index 982a0284b71e9f41f0606af4fe00d8e287d6ba7a..616ea2ef18347c3bec6d0bdd8d0c3f12a47f33a7 100644 --- a/src/apps/render-test/state.ts +++ b/src/apps/render-test/state.ts @@ -16,10 +16,12 @@ import { calculateTextureInfo } from 'mol-gl/util'; import Icosahedron from 'mol-geo/primitive/icosahedron' import Box from 'mol-geo/primitive/box' +import mcubes from './mcubes' + export default class State { regl: REGL.Regl - initRegl (container: HTMLDivElement) { + async initRegl (container: HTMLDivElement) { const regl = glContext.create({ container, extensions: [ @@ -108,11 +110,21 @@ export default class State { ...createTransformAttributes(regl, transformArray1) }) - const mesh2 = MeshRenderable.create(regl, + let rr = 1.0; + 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: Attribute.create(regl, new Float32Array(box.vertices), { size: 3 }), - normal: Attribute.create(regl, new Float32Array(box.normals), { size: 3 }), - ...createTransformAttributes(regl, transformArray2) + position: Attribute.create(regl, cubes.surface.vertexBuffer, { size: 3 }), + normal: Attribute.create(regl, cubes.surface.vertexBuffer, { size: 3 }), + ...createTransformAttributes(regl, transformArray1) }, { colorTex, @@ -123,8 +135,36 @@ export default class State { 'light.falloff': 0, 'light.radius': 500 }, - box.indices - ) + cubes.surface.indexBuffer + ); + + 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 baseContext = regl({ context: { @@ -143,12 +183,12 @@ export default class State { baseContext(() => { // console.log(ctx) regl.clear({color: [0, 0, 0, 1]}) - position.update(array => { array[0] = Math.random() }) + //position.update(array => { array[0] = Math.random() }) // points.update(a => { a.position[0] = Math.random() }) // mesh.draw() // points.draw() mesh2.draw() - points2.draw() + // points2.draw() // model1({}, ({ transform }) => { // points.draw() // }) diff --git a/src/mol-data/util/chunked-array.ts b/src/mol-data/util/chunked-array.ts index 5e54f5ed82ee07a90b82b3f9c8d80fa8a8b07a30..2cb03e02a9343a8616735481d14a562f57bd2559 100644 --- a/src/mol-data/util/chunked-array.ts +++ b/src/mol-data/util/chunked-array.ts @@ -75,6 +75,10 @@ namespace ChunkedArray { } export function compact<T>(array: ChunkedArray<T>, doNotResizeSingleton = false): ArrayLike<T> { + return _compact(array, doNotResizeSingleton); + } + + export function _compact<T>(array: ChunkedArray<T>, doNotResizeSingleton: boolean): ArrayLike<T> { const { ctor, chunks, currentIndex } = array; if (!chunks.length) return ctor(0); diff --git a/src/mol-geo/shape/surface.ts b/src/mol-geo/shape/surface.ts index 9f31177f9b585710e61ff7f66afa3a417626c948..093458d41cfc14ab2e82fab9a64a401b6f19e5d9 100644 --- a/src/mol-geo/shape/surface.ts +++ b/src/mol-geo/shape/surface.ts @@ -4,6 +4,8 @@ * @author David Sehnal <david.sehnal@gmail.com> */ +import { Task } from 'mol-task' + export interface Surface { vertexCount: number, triangleCount: number, @@ -16,47 +18,49 @@ export interface Surface { //boundingSphere?: { center: Geometry.LinearAlgebra.Vector3, radius: number }; } -// export namespace Surface { -// export function computeNormalsImmediate(surface: Surface) { -// if (surface.normals) return; - -// const normals = new Float32Array(surface.vertices.length), -// v = surface.vertices, triangles = surface.triangleIndices; -// for (let i = 0; i < triangles.length; i += 3) { -// const a = 3 * triangles[i], -// b = 3 * triangles[i + 1], -// c = 3 * triangles[i + 2]; - -// const nx = v[a + 2] * (v[b + 1] - v[c + 1]) + v[b + 2] * v[c + 1] - v[b + 1] * v[c + 2] + v[a + 1] * (-v[b + 2] + v[c + 2]), -// ny = -(v[b + 2] * v[c]) + v[a + 2] * (-v[b] + v[c]) + v[a] * (v[b + 2] - v[c + 2]) + v[b] * v[c + 2], -// nz = v[a + 1] * (v[b] - v[c]) + v[b + 1] * v[c] - v[b] * v[c + 1] + v[a] * (-v[b + 1] + v[b + 1]); - -// normals[a] += nx; normals[a + 1] += ny; normals[a + 2] += nz; -// normals[b] += nx; normals[b + 1] += ny; normals[b + 2] += nz; -// normals[c] += nx; normals[c + 1] += ny; normals[c + 2] += nz; -// } - -// for (let i = 0; i < normals.length; i += 3) { -// const nx = normals[i]; -// const ny = normals[i + 1]; -// const nz = normals[i + 2]; -// const f = 1.0 / Math.sqrt(nx * nx + ny * ny + nz * nz); -// normals[i] *= f; normals[i + 1] *= f; normals[i + 2] *= f; -// } -// surface.normals = normals; -// } - -// export function computeNormals(surface: Surface): Computation<Surface> { -// return computation<Surface>(async ctx => { -// if (surface.normals) { -// return surface; -// }; - -// await ctx.updateProgress('Computing normals...'); -// computeNormalsImmediate(surface); -// return surface; -// }); -// } +export namespace Surface { + export function computeNormalsImmediate(surface: Surface) { + if (surface.normalsComputed) return; + + const normals = surface.normalBuffer && surface.normalBuffer.length >= surface.vertexCount * 3 + ? surface.normalBuffer : new Float32Array(surface.vertexBuffer.length); + + const v = surface.vertexBuffer, triangles = surface.indexBuffer; + for (let i = 0, ii = 3 * surface.triangleCount; i < ii; i += 3) { + const a = 3 * triangles[i], + b = 3 * triangles[i + 1], + c = 3 * triangles[i + 2]; + + const nx = v[a + 2] * (v[b + 1] - v[c + 1]) + v[b + 2] * v[c + 1] - v[b + 1] * v[c + 2] + v[a + 1] * (-v[b + 2] + v[c + 2]), + ny = -(v[b + 2] * v[c]) + v[a + 2] * (-v[b] + v[c]) + v[a] * (v[b + 2] - v[c + 2]) + v[b] * v[c + 2], + nz = v[a + 1] * (v[b] - v[c]) + v[b + 1] * v[c] - v[b] * v[c + 1] + v[a] * (-v[b + 1] + v[b + 1]); + + normals[a] += nx; normals[a + 1] += ny; normals[a + 2] += nz; + normals[b] += nx; normals[b + 1] += ny; normals[b + 2] += nz; + normals[c] += nx; normals[c + 1] += ny; normals[c + 2] += nz; + } + + for (let i = 0, ii = 3 * surface.vertexCount; i < ii; i += 3) { + const nx = normals[i]; + const ny = normals[i + 1]; + const nz = normals[i + 2]; + const f = 1.0 / Math.sqrt(nx * nx + ny * ny + nz * nz); + normals[i] *= f; normals[i + 1] *= f; normals[i + 2] *= f; + } + surface.normalBuffer = normals; + surface.normalsComputed = true; + } + + export function computeNormals(surface: Surface): Task<Surface> { + return Task.create<Surface>('Surface (Compute Normals)', async ctx => { + if (surface.normalsComputed) return surface; + + await ctx.update('Computing normals...'); + computeNormalsImmediate(surface); + return surface; + }); + } +} // function addVertex(src: Float32Array, i: number, dst: Float32Array, j: number) { // dst[3 * j] += src[3 * i]; diff --git a/src/mol-geo/util/marching-cubes/algorithm.ts b/src/mol-geo/util/marching-cubes/algorithm.ts index 2a534fba89d2f38dbf18d70e8404ace9329aed09..0a22094fafd220eaf33f73d76da373d7007b301c 100644 --- a/src/mol-geo/util/marching-cubes/algorithm.ts +++ b/src/mol-geo/util/marching-cubes/algorithm.ts @@ -68,15 +68,15 @@ class MarchingCubesComputation { } private finish() { - const vertexBuffer = ChunkedArray.compact(this.state.vertexBuffer) as Float32Array; - const indexBuffer = ChunkedArray.compact(this.state.triangleBuffer) as Uint32Array; + const vertexBuffer = ChunkedArray.compact(this.state.vertexBuffer, true) as Float32Array; + const indexBuffer = ChunkedArray.compact(this.state.triangleBuffer, true) as Uint32Array; this.state.vertexBuffer = <any>void 0; this.state.verticesOnEdges = <any>void 0; let ret: Surface = { - triangleCount: 0, - vertexCount: 0, + vertexCount: this.state.vertexCount, + triangleCount: this.state.triangleCount, vertexBuffer, indexBuffer, // vertexAnnotation: this.state.annotate ? ChunkedArray.compact(this.state.annotationBuffer) : void 0, @@ -128,6 +128,8 @@ class MarchingCubesState { vertexBuffer: ChunkedArray<number>; annotationBuffer: ChunkedArray<number>; triangleBuffer: ChunkedArray<number>; + vertexCount = 0; + triangleCount = 0; private get3dOffsetFromEdgeInfo(index: Index) { return (this.nX * (((this.k + index.k) % 2) * this.nY + this.j + index.j) + this.i + index.i); @@ -173,6 +175,8 @@ class MarchingCubesState { ChunkedArray.add(this.annotationBuffer, a); } + this.vertexCount++; + return id; } @@ -191,8 +195,8 @@ class MarchingCubesState { vertexBufferSize = Math.min(262144, Math.max(dX * dY * dZ / 16, 1024) | 0), triangleBufferSize = Math.min(1 << 16, vertexBufferSize * 4); - this.vertexBuffer = ChunkedArray.create<number>(s => new Float32Array(s), 3, vertexBufferSize); - this.triangleBuffer = ChunkedArray.create<number>(s => new Uint32Array(s), 3, triangleBufferSize); + this.vertexBuffer = ChunkedArray.create<number>(s => new Float32Array(s), 3, vertexBufferSize, params.buffers && params.buffers.vertex); + this.triangleBuffer = ChunkedArray.create<number>(s => new Uint32Array(s), 3, triangleBufferSize, params.buffers && params.buffers.index); this.annotate = !!params.annotationField; if (this.annotate) this.annotationBuffer = ChunkedArray.create(s => new Int32Array(s), 1, vertexBufferSize); @@ -203,7 +207,7 @@ class MarchingCubesState { get(i: number, j: number, k: number) { return this.scalarFieldGet(this.scalarField, i, j, k); - } + } processCell(i: number, j: number, k: number) { let tableIndex = 0; @@ -236,6 +240,7 @@ class MarchingCubesState { let triInfo = TriTable[tableIndex]; for (let t = 0; t < triInfo.length; t += 3) { + this.triangleCount++; ChunkedArray.add3(this.triangleBuffer, this.vertList[triInfo[t]], this.vertList[triInfo[t + 1]], this.vertList[triInfo[t + 2]]); } } diff --git a/src/mol-math/linear-algebra/tensor.ts b/src/mol-math/linear-algebra/tensor.ts index 5952080321b59a03c44c637dbc16cf3209dfbfbb..5a2231baf00ec1013259a3a30b21884a065003b6 100644 --- a/src/mol-math/linear-algebra/tensor.ts +++ b/src/mol-math/linear-algebra/tensor.ts @@ -41,6 +41,8 @@ export namespace Tensor { return { dimensions, axisOrderFastToSlow, axisOrderSlowToFast, accessDimensions, defaultCtor: ctor || Float64Array } } + export function create(space: Space, data: Data): Tensor { return { space, data }; } + export function Space(dimensions: number[], axisOrderSlowToFast: number[], ctor?: ArrayCtor): Space { const layout = Layout(dimensions, axisOrderSlowToFast, ctor); const { get, set } = accessors(layout);