From 91385af0989f5ef8e05c576ff6fcd010ec2ce9e7 Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Fri, 6 Apr 2018 19:36:35 -0700 Subject: [PATCH] renamed Sureface to Mesh, wip MeshBuilder --- src/apps/render-test/mcubes.ts | 8 +- src/mol-geo/shape/mesh-builder.ts | 65 ++++++++++++ src/mol-geo/shape/{surface.ts => mesh.ts} | 100 +++++++++---------- src/mol-geo/util.ts | 33 ++++-- src/mol-geo/util/marching-cubes/algorithm.ts | 6 +- src/mol-math/geometry/sphere.ts | 21 +++- 6 files changed, 164 insertions(+), 69 deletions(-) create mode 100644 src/mol-geo/shape/mesh-builder.ts rename src/mol-geo/shape/{surface.ts => mesh.ts} (73%) diff --git a/src/apps/render-test/mcubes.ts b/src/apps/render-test/mcubes.ts index 5c0efa532..350cdc9e5 100644 --- a/src/apps/render-test/mcubes.ts +++ b/src/apps/render-test/mcubes.ts @@ -6,7 +6,7 @@ import { Run } from 'mol-task' import { compute } from 'mol-geo/util/marching-cubes/algorithm' -import { Surface } from 'mol-geo/shape/surface' +import { Mesh } from 'mol-geo/shape/mesh' import { Tensor, Mat4, Vec3 } from 'mol-math/linear-algebra' function fillField(tensor: Tensor, f: (x: number, y: number, z: number) => number, min: number[], max: number[]): Tensor { @@ -27,7 +27,7 @@ function fillField(tensor: Tensor, f: (x: number, y: number, z: number) => numbe return tensor } -export default async function computeSurface(f: (x: number, y: number, z: number) => number, data?: { field: Tensor, surface: Surface }) { +export default async function computeSurface(f: (x: number, y: number, z: number) => number, data?: { field: Tensor, surface: Mesh }) { let field: Tensor; if (data) field = data.field; else { @@ -51,7 +51,7 @@ export default async function computeSurface(f: (x: number, y: number, z: number const scale = Mat4.fromScaling(Mat4.zero(), Vec3.create(size[0] / (grid[0] - 1), size[1] / (grid[1] - 1), size[2] / (grid[2] - 1))); const transform = Mat4.mul(Mat4.zero(), translation, scale); - Surface.transformImmediate(surface, transform); - Surface.computeNormalsImmediate(surface); + Mesh.transformImmediate(surface, transform); + Mesh.computeNormalsImmediate(surface); return { surface, field }; } \ No newline at end of file diff --git a/src/mol-geo/shape/mesh-builder.ts b/src/mol-geo/shape/mesh-builder.ts new file mode 100644 index 000000000..5733378b8 --- /dev/null +++ b/src/mol-geo/shape/mesh-builder.ts @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { ValueCell } from 'mol-util/value-cell' +import { Vec3, Mat4 } from 'mol-math/linear-algebra'; +import { ChunkedArray } from 'mol-data/util'; + +import Box, { BoxProps } from '../primitive/box'; +import { Mesh } from './mesh'; + +type ElementId = { id: number } + +export interface MeshBuilder { + add(t: Mat4, _vertices: Float32Array, _normals: Float32Array, _indices?: Uint32Array): number + addBox(t: Mat4, props?: BoxProps & ElementId): number + getMesh(): Mesh +} + +const tmpV = Vec3.zero() + +export namespace MeshBuilder { + export function create(initialCount = 2048, chunkSize = 1024): MeshBuilder { + const vertices = ChunkedArray.create(Float32Array, 3, chunkSize, initialCount); + const normals = ChunkedArray.create(Float32Array, 3, chunkSize, initialCount); + const indices = ChunkedArray.create(Uint32Array, 3, chunkSize * 3, initialCount * 3); + + // const offsets = ChunkedArray.create<number>(n => new Uint32Array(n), 1, 1000); + // const elementIds = ChunkedArray.create(Uint32Array, 1, chunkSize, initialCount); + + ChunkedArray.compact(indices, true) + + const add = (t: Mat4, _vertices: Float32Array, _normals: Float32Array, _indices?: Uint32Array) => { + const offset = vertices.elementCount * vertices.elementSize + for (let i = 0, il = _vertices.length; i < il; i += 3) { + Vec3.fromArray(tmpV, _vertices, i) + Vec3.transformMat4(tmpV, tmpV, t) + // Vec3.transformDirection(tmpV, tmpV, n) // TODO + ChunkedArray.add3(vertices, tmpV[0], tmpV[1], tmpV[2]); + } + // ChunkedArray.add(vertices, _vertices[i]) + return offset + } + + return { + add, + addBox: (t: Mat4, props?: BoxProps & ElementId) => { + const box = Box(props) + return add(t, box.vertices, box.normals, box.indices) + }, + getMesh: () => { + return { + vertexCount: vertices.elementCount, + triangleCount: indices.elementCount, + vertexBuffer: ValueCell.create(ChunkedArray.compact(vertices, true) as Float32Array), + indexBuffer: ValueCell.create(ChunkedArray.compact(indices, true) as Uint32Array), + normalBuffer: ValueCell.create(ChunkedArray.compact(normals, true) as Float32Array), + normalsComputed: true, + } + } + } + } +} \ No newline at end of file diff --git a/src/mol-geo/shape/surface.ts b/src/mol-geo/shape/mesh.ts similarity index 73% rename from src/mol-geo/shape/surface.ts rename to src/mol-geo/shape/mesh.ts index f16c239f0..9f8330766 100644 --- a/src/mol-geo/shape/surface.ts +++ b/src/mol-geo/shape/mesh.ts @@ -7,8 +7,10 @@ import { Task } from 'mol-task' import { ValueCell } from 'mol-util' import { Vec3, Mat4 } from 'mol-math/linear-algebra' +import Sphere from 'mol-math/geometry/sphere' +import { transformPositionArray } from '../util'; -export interface Surface { +export interface Mesh { vertexCount: number, triangleCount: number, vertexBuffer: ValueCell<Float32Array>, @@ -17,11 +19,11 @@ export interface Surface { normalsComputed: boolean, vertexAnnotation?: ValueCell<ArrayLike<number>> - //boundingSphere?: { center: Geometry.LinearAlgebra.Vector3, radius: number }; + boundingSphere?: Sphere } -export namespace Surface { - export function computeNormalsImmediate(surface: Surface) { +export namespace Mesh { + export function computeNormalsImmediate(surface: Mesh) { if (surface.normalsComputed) return; const normals = surface.normalBuffer.ref.value && surface.normalBuffer.ref.value!.length >= surface.vertexCount * 3 @@ -58,8 +60,8 @@ export namespace Surface { surface.normalsComputed = true; } - export function computeNormals(surface: Surface): Task<Surface> { - return Task.create<Surface>('Surface (Compute Normals)', async ctx => { + export function computeNormals(surface: Mesh): Task<Mesh> { + return Task.create<Mesh>('Surface (Compute Normals)', async ctx => { if (surface.normalsComputed) return surface; await ctx.update('Computing normals...'); @@ -68,20 +70,15 @@ export namespace Surface { }); } - export function transformImmediate(surface: Surface, t: Mat4) { - const p = Vec3.zero(); - const vertices = surface.vertexBuffer.ref.value; - for (let i = 0, _c = surface.vertexCount * 3; i < _c; i += 3) { - p[0] = vertices[i]; - p[1] = vertices[i + 1]; - p[2] = vertices[i + 2]; - Vec3.transformMat4(p, p, t); - vertices[i] = p[0]; - vertices[i + 1] = p[1]; - vertices[i + 2] = p[2]; - } - surface.normalsComputed = false; - //surface.boundingSphere = void 0; + export function transformImmediate(mesh: Mesh, t: Mat4) { + transformRangeImmediate(mesh, t, 0, mesh.vertexCount) + } + + export function transformRangeImmediate(mesh: Mesh, t: Mat4, offset: number, count: number) { + transformPositionArray(t, mesh.vertexBuffer.ref.value, offset, count) + // transformDirectionArray(n, mesh.normalBuffer.ref.value, offset, count) // TODO + mesh.normalsComputed = false; + // mesh.boundingSphere = void 0; } } @@ -168,38 +165,37 @@ export namespace Surface { // return computation(async ctx => await laplacianSmoothComputation(ctx, surface, iterCount, (1.1 * vertexWeight) / 1.1)); // } -// export function computeBoundingSphere(surface: Surface): Computation<Surface> { -// return computation<Surface>(async ctx => { -// if (surface.boundingSphere) { -// return surface; -// } -// await ctx.updateProgress('Computing bounding sphere...'); - -// const vertices = surface.vertices; -// let x = 0, y = 0, z = 0; -// for (let i = 0, _c = surface.vertices.length; i < _c; i += 3) { -// x += vertices[i]; -// y += vertices[i + 1]; -// z += vertices[i + 2]; -// } -// x /= surface.vertexCount; -// y /= surface.vertexCount; -// z /= surface.vertexCount; -// let r = 0; -// for (let i = 0, _c = vertices.length; i < _c; i += 3) { -// const dx = x - vertices[i]; -// const dy = y - vertices[i + 1]; -// const dz = z - vertices[i + 2]; -// r = Math.max(r, dx * dx + dy * dy + dz * dz); -// } -// surface.boundingSphere = { -// center: LinearAlgebra.Vector3.fromValues(x, y, z), -// radius: Math.sqrt(r) -// } -// return surface; -// }); -// } - + export function computeBoundingSphere(mesh: Mesh): Task<Mesh> { + return Task.create<Mesh>('Mesh (Compute Bounding Sphere)', async ctx => { + if (mesh.boundingSphere) { + return mesh; + } + await ctx.update('Computing bounding sphere...'); + + const vertices = mesh.vertexBuffer.ref.value; + let x = 0, y = 0, z = 0; + for (let i = 0, _c = vertices.length; i < _c; i += 3) { + x += vertices[i]; + y += vertices[i + 1]; + z += vertices[i + 2]; + } + x /= mesh.vertexCount; + y /= mesh.vertexCount; + z /= mesh.vertexCount; + let r = 0; + for (let i = 0, _c = vertices.length; i < _c; i += 3) { + const dx = x - vertices[i]; + const dy = y - vertices[i + 1]; + const dz = z - vertices[i + 2]; + r = Math.max(r, dx * dx + dy * dy + dz * dz); + } + mesh.boundingSphere = { + center: Vec3.create(x, y, z), + radius: Math.sqrt(r) + } + return mesh; + }); + } // export function transform(surface: Surface, t: number[]): Computation<Surface> { // return computation<Surface>(async ctx => { diff --git a/src/mol-geo/util.ts b/src/mol-geo/util.ts index f87508ebb..615b7c4f5 100644 --- a/src/mol-geo/util.ts +++ b/src/mol-geo/util.ts @@ -4,9 +4,9 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Vec3 } from 'mol-math/linear-algebra' +import { Vec3, Mat4, Mat3 } from 'mol-math/linear-algebra' -export function normalizeVec3array<T extends Helpers.NumberArray> (a: T) { +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 ] @@ -19,12 +19,26 @@ export function normalizeVec3array<T extends Helpers.NumberArray> (a: T) { } } +const tmpV = Vec3.zero() + +export function transformPositionArray (t: Mat4, array: Helpers.NumberArray, offset: number, count: number) { + for (let i = 0, il = count * 3; i < il; i += 3) { + Vec3.fromArray(tmpV, array, offset + i) + Vec3.transformMat4(tmpV, tmpV, t) + Vec3.toArray(tmpV, array, offset + i) + } +} + +export function transformDirectionArray (t: Mat3, array: Helpers.NumberArray, offset: number, count: number) { + // TODO +} + 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 +/** 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 @@ -36,8 +50,10 @@ export function appplyRadius(vertices: Helpers.NumberArray, radius: number) { } } -// indexed vertex normals weighted by triangle areas http://www.iquilezles.org/www/articles/normals/normals.htm -// normal array must contain only zeros +/** + * 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() @@ -71,12 +87,11 @@ export function computeIndexedVertexNormals<T extends Helpers.NumberArray> (vert normals[ ci + 2 ] += cb[ 2 ] } - normalizeVec3array(normals) + normalizeVec3Array(normals) return normals } -// vertex normals for unindexed triangle soup -// normal array must contain only zeros +/** 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) @@ -108,6 +123,6 @@ export function computeVertexNormals<T extends Helpers.NumberArray> (vertices: H normals[ i + 8 ] = cb[ 2 ] } - normalizeVec3array(normals) + normalizeVec3Array(normals) return normals } \ No newline at end of file diff --git a/src/mol-geo/util/marching-cubes/algorithm.ts b/src/mol-geo/util/marching-cubes/algorithm.ts index 9b2da47f4..bf8572a51 100644 --- a/src/mol-geo/util/marching-cubes/algorithm.ts +++ b/src/mol-geo/util/marching-cubes/algorithm.ts @@ -7,7 +7,7 @@ import { Task, RuntimeContext } from 'mol-task' import { ChunkedArray } from 'mol-data/util' import { Tensor } from 'mol-math/linear-algebra' -import { Surface } from '../../shape/surface' +import { Mesh } from '../../shape/mesh' import { Index, EdgeIdInfo, CubeEdges, EdgeTable, TriTable } from './tables' import { ValueCell } from 'mol-util' @@ -23,7 +23,7 @@ export interface MarchingCubesParameters { annotationField?: Tensor, - oldSurface?: Surface + oldSurface?: Mesh } export function compute(parameters: MarchingCubesParameters) { @@ -71,7 +71,7 @@ class MarchingCubesComputation { this.state.vertexBuffer = <any>void 0; this.state.verticesOnEdges = <any>void 0; - let ret: Surface = { + let ret: Mesh = { vertexCount: this.state.vertexCount, triangleCount: this.state.triangleCount, vertexBuffer: this.parameters.oldSurface ? ValueCell.update(this.parameters.oldSurface.vertexBuffer, vb) : ValueCell.create(vb), diff --git a/src/mol-math/geometry/sphere.ts b/src/mol-math/geometry/sphere.ts index 6135cbf4b..0b81ae4c5 100644 --- a/src/mol-math/geometry/sphere.ts +++ b/src/mol-math/geometry/sphere.ts @@ -1 +1,20 @@ -// TODO: rebranded vec4 \ 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 { Vec3 } from '../linear-algebra' + +export interface Sphere { + center: Vec3 + radius: number +} + +export namespace Sphere { + export function create(center: Vec3, radius: number): Sphere { + return { center, radius } + } +} + +export default Sphere \ No newline at end of file -- GitLab