Compare commits

...

1 Commits

Author SHA1 Message Date
Alexander Rose
c2b652d5ab wip, prototype for mesh clipping 2022-03-02 19:51:34 -08:00
5 changed files with 307 additions and 2 deletions

View File

@@ -0,0 +1,215 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ChunkedArray } from '../../../mol-data/util';
import { MeshValues } from '../../../mol-gl/renderable/mesh';
import { Sphere3D } from '../../../mol-math/geometry';
import { sphereIntersect } from '../../../mol-math/intersect';
import { Vec3 } from '../../../mol-math/linear-algebra';
import { ValueCell } from '../../../mol-util';
import { Clip } from '../../../mol-util/clip';
interface ObjectClippingInput {
drawCount: number
vertexCount: number
instanceCount: number
groupCount: number
transformBuffer: Float32Array
instanceBuffer: Float32Array
positionBuffer: Float32Array
indexBuffer: Uint32Array
normalBuffer: Float32Array
groupBuffer: Float32Array
// colorData: TextureImage<Uint8Array>
// colorType: 'group' | 'groupInstance'
boundingSphere: Sphere3D
invariantBoundingSphere: Sphere3D
objectCount: number
objectType: number[]
objectPosition: number[]
objectScale: number[]
objectInvert: boolean[]
}
export function calcMeshObjectClipping(input: ObjectClippingInput) {
console.log('calcMeshObjectClipping', input);
const { drawCount, vertexCount, indexBuffer, positionBuffer, normalBuffer, objectCount, groupBuffer, objectPosition, objectScale, objectType, objectInvert } = input;
const triangleCount = drawCount / 3;
if (objectCount === 0) {
return {
indexBuffer,
positionBuffer,
normalBuffer,
groupBuffer,
drawCount,
vertexCount
};
}
const tests: ((p: Vec3) => boolean)[] = [];
for (let i = 0; i < objectCount; ++i) {
if (objectType[i] === Clip.Type.sphere) {
const c = Vec3.fromArray(Vec3(), objectPosition, i * 3);
const r = objectScale[i * 3] / 2;
if (objectInvert[i]) {
tests.push((p: Vec3) => Vec3.distance(p, c) <= r);
} else {
tests.push((p: Vec3) => Vec3.distance(p, c) >= r);
}
}
}
const invert = objectInvert[0];
const center = Vec3.fromArray(Vec3(), objectPosition, 0);
const radius = objectScale[0] / 2;
// new
const index = ChunkedArray.create(Uint32Array, 3, 1024, triangleCount);
// re-use
const vertex = ChunkedArray.create(Float32Array, 3, 1024, positionBuffer);
vertex.currentIndex = vertexCount * 3;
vertex.elementCount = vertexCount;
const normal = ChunkedArray.create(Float32Array, 3, 1024, normalBuffer);
normal.currentIndex = vertexCount * 3;
normal.elementCount = vertexCount;
const group = ChunkedArray.create(Float32Array, 1, 1024, groupBuffer);
group.currentIndex = vertexCount;
group.elementCount = vertexCount;
const vA = Vec3();
const vB = Vec3();
const vC = Vec3();
const xA = Vec3();
const xB = Vec3();
const nA = Vec3();
const nB = Vec3();
function add(a: number, b: number, p: Vec3) {
Vec3.fromArray(nA, normalBuffer, a * 3);
Vec3.fromArray(nB, normalBuffer, b * 3);
Vec3.scale(nA, Vec3.add(nA, nA, nB), 0.5);
ChunkedArray.add3(vertex, p[0], p[1], p[2]);
ChunkedArray.add3(normal, nA[0], nA[1], nA[2]);
ChunkedArray.add(group, groupBuffer[a]);
}
function split(tip: Vec3, sideA: Vec3, sideB: Vec3, tipIdx: number, sideIdxA: number, sideIdxB: number, reverse: boolean) {
if (reverse) {
sphereIntersect(xA, sideA, tip, center, radius, invert);
sphereIntersect(xB, sideB, tip, center, radius, invert);
} else {
sphereIntersect(xA, tip, sideA, center, radius, invert);
sphereIntersect(xB, tip, sideB, center, radius, invert);
}
add(tipIdx, sideIdxA, xA);
add(tipIdx, sideIdxB, xB);
if (reverse) {
ChunkedArray.add3(index, tipIdx, newVertexCount + 1, newVertexCount);
newTriangleCount += 1;
} else {
ChunkedArray.add3(index, newVertexCount, newVertexCount + 1, sideIdxA);
ChunkedArray.add3(index, newVertexCount + 1, sideIdxB, sideIdxA);
newTriangleCount += 2;
}
newVertexCount += 2;
}
let newVertexCount = vertexCount;
let newTriangleCount = 0;
for (let i = 0; i < triangleCount; ++i) {
const iA = indexBuffer[i * 3];
const iB = indexBuffer[i * 3 + 1];
const iC = indexBuffer[i * 3 + 2];
Vec3.fromArray(vA, positionBuffer, iA * 3);
Vec3.fromArray(vB, positionBuffer, iB * 3);
Vec3.fromArray(vC, positionBuffer, iC * 3);
let tA = true;
let tB = true;
let tC = true;
for (const t of tests) {
if (!t(vA)) { tA = false; }
if (!t(vB)) { tB = false; }
if (!t(vC)) { tC = false; }
}
if (!tA && !tB && !tC) continue;
if (tA && tB && tC) {
ChunkedArray.add3(index, iA, iB, iC);
newTriangleCount += 1;
} else if (tA && tB && !tC) {
split(vC, vA, vB, iC, iA, iB, false);
} else if (!tA && !tB && tC) {
split(vC, vA, vB, iC, iA, iB, true);
} else if (tA && tC && !tB) {
split(vB, vA, vC, iB, iA, iC, false);
} else if (!tA && !tC && tB) {
split(vB, vA, vC, iB, iA, iC, true);
} else if (tB && tC && !tA) {
split(vA, vC, vB, iA, iC, iB, false);
} else if (!tB && !tC && tA) {
split(vA, vC, vB, iA, iC, iB, true);
} else {
console.log('what');
}
}
console.log({ vertexCount, newVertexCount, triangleCount, newTriangleCount });
return {
indexBuffer: ChunkedArray.compact(index),
positionBuffer: ChunkedArray.compact(vertex),
normalBuffer: ChunkedArray.compact(normal),
groupBuffer: ChunkedArray.compact(group),
drawCount: newTriangleCount * 3,
vertexCount: newVertexCount
};
}
//
export function applyMeshObjectClipping(values: MeshValues) {
console.log('applyMeshObjectClipping');
const clippingData = calcMeshObjectClipping({
drawCount: values.drawCount.ref.value,
vertexCount: values.uVertexCount.ref.value,
instanceCount: values.instanceCount.ref.value,
groupCount: values.uGroupCount.ref.value,
transformBuffer: values.transform.ref.value,
instanceBuffer: values.aInstance.ref.value,
positionBuffer: values.aPosition.ref.value,
indexBuffer: values.elements.ref.value,
normalBuffer: values.aNormal.ref.value,
groupBuffer: values.aGroup.ref.value,
// colorData: TextureImage<Uint8Array>
// colorType: 'group' | 'groupInstance'
boundingSphere: values.boundingSphere.ref.value,
invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
objectCount: values.dClipObjectCount.ref.value,
objectType: values.uClipObjectType.ref.value,
objectPosition: values.uClipObjectPosition.ref.value,
objectScale: values.uClipObjectScale.ref.value,
objectInvert: values.uClipObjectInvert.ref.value,
});
console.log(values.dClipObjectCount.ref.value, values.drawCount.ref.value, clippingData);
ValueCell.updateIfChanged(values.dClipObjectCount, 0);
ValueCell.update(values.elements, clippingData.indexBuffer);
ValueCell.update(values.aPosition, clippingData.positionBuffer);
ValueCell.update(values.aNormal, clippingData.normalBuffer);
ValueCell.update(values.aGroup, clippingData.groupBuffer);
ValueCell.updateIfChanged(values.drawCount, clippingData.drawCount);
ValueCell.updateIfChanged(values.uVertexCount, clippingData.vertexCount);
}

View File

@@ -6,7 +6,8 @@ export const clip_pixel = `
int clippingFlag = 0;
#endif
if (clipTest(vec4(vModelPosition, 0.0), clippingFlag))
discard;
// TODO: disabled for testing
// if (clipTest(vec4(vModelPosition, 0.0), clippingFlag))
// discard;
#endif
`;

80
src/mol-math/intersect.ts Normal file
View File

@@ -0,0 +1,80 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Vec2, Vec3 } from './linear-algebra';
const tmpT = Vec2();
const tmpOrigin = Vec3();
const tmpDir = Vec3();
export function sphereIntersect(result: Vec3, p0: Vec3, p1: Vec3, center: Vec3, radius: number, invert: boolean) {
Vec3.copy(tmpOrigin, p0);
Vec3.sub(tmpDir, p1, p0);
const maxT = Vec3.magnitude(tmpDir);
Vec3.normalize(tmpDir, tmpDir);
const ts = raySphere(tmpT, tmpOrigin, tmpDir, center, radius);
if (ts === undefined || ts[1] < 0.0 || ts[0] > maxT) {
return undefined;
}
const t = invert ? Math.max(ts[0], 0.0) : Math.min(ts[1], maxT);
Vec3.scaleAndAdd(result, tmpOrigin, tmpDir, t);
return result;
};
function solveQuadratic(result: Vec2, a: number, b: number, c: number) {
const det = b * b - 4.0 * a * c;
if (det < 0.0) {
return undefined;
} else if (det > 0.0) {
const denom = 1.0 / (2.0 * a);
const disc = Math.sqrt(det);
const root0 = (-b + disc) * denom;
const root1 = (-b - disc) * denom;
if (root0 < root1) {
result[0] = root0;
result[1] = root1;
} else {
result[0] = root1;
result[1] = root0;
}
return result;
}
const root = -b / (2.0 * a);
if (root === 0.0) {
return undefined;
}
result[0] = result[1] = root;
return result;
}
const tmpRaySphereRoots = Vec2();
const tmpDiff = Vec3();
function raySphere(result: Vec2, origin: Vec3, direction: Vec3, center: Vec3, radius: number) {
const radiusSquared = radius * radius;
Vec3.sub(tmpDiff, origin, center);
const a = Vec3.dot(direction, direction);
const b = 2.0 * Vec3.dot(direction, tmpDiff);
const c = Vec3.squaredMagnitude(tmpDiff) - radiusSquared;
const roots = solveQuadratic(tmpRaySphereRoots, a, b, c);
if (roots === undefined) {
return undefined;
}
Vec2.copy(result, roots);
return result;
}

View File

@@ -338,6 +338,10 @@ export function UnitsVisual<G extends Geometry, P extends StructureParams & Geom
},
setClipping(clipping: Clipping) {
Visual.setClipping(renderObject, clipping, lociApply, true);
// TODO: only for testing
if (renderObject) {
processValues?.(renderObject.values, geometry, currentProps, currentTheme);
}
},
destroy() {
dispose?.(geometry);

View File

@@ -22,6 +22,7 @@ import { Texture } from '../../../mol-gl/webgl/texture';
import { WebGLContext } from '../../../mol-gl/webgl/context';
import { applyMeshColorSmoothing } from '../../../mol-geo/geometry/mesh/color-smoothing';
import { ColorSmoothingParams, getColorSmoothingProps } from '../../../mol-geo/geometry/base';
import { applyMeshObjectClipping } from '../../../mol-geo/geometry/mesh/object-clipping';
export const MolecularSurfaceMeshParams = {
...UnitsMeshParams,
@@ -93,6 +94,10 @@ export function MolecularSurfaceMeshVisual(materialId: number): UnitsVisual<Mole
applyMeshColorSmoothing(values, csp.resolution, csp.stride, webgl, colorTexture);
(geometry.meta as MolecularSurfaceMeta).colorTexture = values.tColorGrid.ref.value;
}
// TODO: only for testing
console.time('applyMeshObjectClipping');
applyMeshObjectClipping(values);
console.timeEnd('applyMeshObjectClipping');
},
dispose: (geometry: Mesh) => {
(geometry.meta as MolecularSurfaceMeta).colorTexture?.destroy();