mirror of
https://github.com/molstar/molstar.git
synced 2026-06-04 21:34:23 +08:00
Compare commits
1 Commits
master
...
mesh-clipp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2b652d5ab |
215
src/mol-geo/geometry/mesh/object-clipping.ts
Normal file
215
src/mol-geo/geometry/mesh/object-clipping.ts
Normal 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);
|
||||
}
|
||||
@@ -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
80
src/mol-math/intersect.ts
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user