diff --git a/src/lib/components/Scene.svelte b/src/lib/components/Scene.svelte index e286a82..6ad2be5 100644 --- a/src/lib/components/Scene.svelte +++ b/src/lib/components/Scene.svelte @@ -5,23 +5,23 @@ import { useLoader } from '@threlte/core'; import { BufferGeometry, - Float32BufferAttribute, MathUtils, Vector3, Mesh, - Points, - Triangle, DoubleSide, - Sphere + Color, + Box3, + Matrix4 } from 'three'; - import { degToRad } from 'three/src/math/MathUtils.js'; - import { MeshBVH } from 'three-mesh-bvh'; + import { ExtendedTriangle, MeshBVH } from 'three-mesh-bvh'; import type { Readable } from 'svelte/store'; + import type { HitPointInfo } from 'three-mesh-bvh'; + import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'; export let buildSurface = [300, 300, 300]; export let layerHeight = 0.2; export let nozzleSize = 0.4; - export let tolerance = 0.1; + export let tolerance = 0.005; export let maxNonPlanarAngle = MathUtils.degToRad(20); export let bedNormal = new Vector3(0, 0, 1); @@ -30,33 +30,16 @@ const stl: Readable = useLoader(STLLoader).load('/benchy.stl'); let mesh: Mesh; - let surface: BufferGeometry | undefined; + let layers: BufferGeometry[] = []; $: if ($stl) { - //slice(mesh); - // we don't really care about the faces, since the vertices bound the area anyways - // which is the only thing that matters when creating non-planar slices. - // sort vertices by z, then x, then y in separate index arrays - // add face index that maps an index/vertex to multiple faces (indices) - // on each layer get the closest vertex in z, then find the next closest and - // determine the angle between them. If it's less than the maxNonPlanarAngle - // add it to the current slice and set it to consumed, if not - - // build bvh for the mesh, query the clostest point. - // only store the indices for each slice. - // query the mesh bvh for the closest point while discarding points not - // in the slice. Keep track of candidates while querying the bvh. - - // need to build bvh live while generating the slices, so angle checks can be done with - // respect to the closest point in the slice - const bvh = new MeshBVH($stl); - const positions = bvh.geometry.getAttribute('position'); - const index = bvh.geometry.index!; + const positions = $stl.getAttribute('position'); + const index = $stl.index!; const qualifyingTriangles = Array.from({ length: index.count / 3 }, () => false); let qualifyingTrianglesCount = 0; - const triangle = new Triangle(); + const triangle = new ExtendedTriangle(); const normal = new Vector3(); for (let i = 0; i < index.count / 3; i++) { triangle.setFromAttributeAndIndices( @@ -73,8 +56,6 @@ } } - const spheres = Array.from({ length: 3 }, () => new Sphere()); - const vectors = Array.from({ length: 3 }, () => new Vector3()); const surfaces: number[][] = []; while (qualifyingTrianglesCount > 0) { const faceIndex = qualifyingTriangles.findIndex((it) => it); @@ -83,19 +64,21 @@ const surface = [faceIndex]; let cursor = 0; while (cursor < surface.length) { - for (let i = 0; i < 3; i++) { - vectors[i].fromBufferAttribute(positions, index.array[surface[cursor] * 3 + i]); - spheres[i].set(vectors[i], tolerance); - } + triangle.setFromAttributeAndIndices( + positions, + index.array[surface[cursor] * 3], + index.array[surface[cursor] * 3 + 1], + index.array[surface[cursor] * 3 + 2] + ); bvh.shapecast({ intersectsBounds(box, _isLeaf, _score, _depth, _nodeIndex) { - return spheres.some((sphere) => box.intersectsSphere(sphere)); + return triangle.intersectsBox(box); }, - intersectsTriangle(triangle, triangleIndex, _contained, _depth) { + intersectsTriangle(target, triangleIndex, _contained, _depth) { if ( qualifyingTriangles[triangleIndex] && - spheres.some((sphere) => triangle.intersectsSphere(sphere)) + target.distanceToTriangle(triangle) < tolerance ) { qualifyingTriangles[triangleIndex] = false; qualifyingTrianglesCount--; @@ -106,82 +89,23 @@ cursor++; } - surfaces.push( - surface.flatMap((face) => [ - index.array[face * 3], - index.array[face * 3 + 1], - index.array[face * 3 + 2] - ]) - ); + surfaces.push(surface); } - console.log(surfaces); - - surface = new BufferGeometry(); - surface.setAttribute('position', positions); - surface.setAttribute('normal', $stl.getAttribute('normal')); - surface.setIndex(surfaces[4]); - - /*const hull: [position: Vector3, index: number][][] = []; - let limit = 0; - while (points.length > 0) { - const consumed = points.map(() => false); - const currentHull: [position: Vector3, index: number][] = [[points[0][0], 0]]; - consumed[0] = true; - for (let i = 1; i < points.length; i++) { - inner: do { - const b = points[i][0].clone().sub(currentHull[currentHull.length - 1][0]); - const angle = Math.asin( - Math.abs(b.clone().dot(bedNormal)) / - (Math.abs(b.length()) * Math.abs(bedNormal.length())) - ); - if (angle <= maxNonPlanarAngle) { - currentHull.push([points[i][0], points[i][2]]); - consumed[i] = true; - break inner; - } else if (points[i][0].z < currentHull[currentHull.length - 1][0].z) { - consumed[currentHull.pop()![1]] = false; - if (currentHull.length === 0) { - currentHull.push([points[i][0], points[i][2]]); - consumed[i] = true; - break inner; - } - } else { - break inner; - } - } while (true); + layers = surfaces.map((surface) => { + const geometry = new BufferGeometry(); + geometry.setAttribute('position', positions); + geometry.setAttribute('normal', $stl.getAttribute('normal')); + const indices: number[] = Array.from({ length: surface.length * 3 }); + for (let i = 0; i < surface.length; i++) { + const pos = surface[i] * 3; + indices[i * 3] = $stl.index!.array[pos]; + indices[i * 3 + 1] = $stl.index!.array[pos + 1]; + indices[i * 3 + 2] = $stl.index!.array[pos + 2]; } - - points = points.filter((_, j) => !consumed[j]); - hull.push(currentHull); - if (limit++ > 100) break; - } - console.log(hull);*/ - } - - async function slice(mesh: Mesh) { - const { World, ColliderDesc, Ray } = await import('@dimforge/rapier3d'); - console.log(mesh.geometry.getAttribute('indices')); - - const positions = mesh.geometry.getAttribute('position'); - const collider = ColliderDesc.trimesh( - new Float32Array(positions.array), - new Uint32Array( - mesh.geometry.index?.array ?? Array.from({ length: positions.count }, (_, i) => i) - ) - ); - collider.setTranslation(mesh.position.x, mesh.position.y, mesh.position.z); - collider.setRotation(mesh.quaternion); - collider.shape; - const rayNormal = new Vector3(0, 0, 1); - const shapePos = new Vector3(0, 0, 0); - const shapeRot = { x: 0, y: 0, z: 0, w: 0 }; - console.log(((buildSurface[0] / nozzleSize) * buildSurface[1]) / nozzleSize); - for (let x = 0; x < buildSurface[0]; x += nozzleSize) { - for (let y = 0; y < buildSurface[1]; y += nozzleSize) { - // collider.shape.castRay(new Ray({ x, y, z: 0 }, rayNormal), shapePos, shapeRot, 20, false); - } - } + geometry.setIndex(indices); + return geometry; + }); } @@ -202,11 +126,11 @@ gridSize={[buildSurface[0], buildSurface[1]]} /> -{#if surface} +{#each layers as surface} - + -{/if} +{/each} {#if $stl && false} diff --git a/src/polyfill-types.d.ts b/src/polyfill-types.d.ts new file mode 100644 index 0000000..e5748e4 --- /dev/null +++ b/src/polyfill-types.d.ts @@ -0,0 +1,11 @@ +import { MeshBVH, computeBoundsTree, disposeBoundsTree } from 'three-mesh-bvh'; + +declare module 'three' { + export interface BufferGeometry { + boundsTree?: MeshBVH; + computeBoundsTree: typeof computeBoundsTree; + disposeBoundsTree: typeof disposeBoundsTree; + } +} + +export {}; diff --git a/src/polyfills.ts b/src/polyfills.ts new file mode 100644 index 0000000..ddd15cc --- /dev/null +++ b/src/polyfills.ts @@ -0,0 +1,5 @@ +import { BufferGeometry } from 'three'; +import { computeBoundsTree, disposeBoundsTree } from 'three-mesh-bvh'; + +BufferGeometry.prototype.computeBoundsTree = computeBoundsTree; +BufferGeometry.prototype.disposeBoundsTree = disposeBoundsTree; diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts new file mode 100644 index 0000000..00b6cf5 --- /dev/null +++ b/src/routes/+layout.ts @@ -0,0 +1 @@ +import '../polyfills.js';