bvh.js 6.58 KB
import * as THREE from 'three';
import { computeBoundsTree, disposeBoundsTree, CONTAINED, INTERSECTED, NOT_INTERSECTED } from 'three-mesh-bvh';
THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree;
THREE.BufferGeometry.prototype.disposeBoundsTree = disposeBoundsTree;

export const getIntersectsTriangleIndices = (targetMesh, center, size, camera) => {
    const accumulatedTraversedNodeIndices = new Set();
    const accumulatedEndNodeIndices = new Set();

    const { geometry } = targetMesh;

    if (!geometry.boundsTree) geometry.computeBoundsTree();

    const bvh = geometry.boundsTree;

    const inverseMatrix = new THREE.Matrix4();
    inverseMatrix.copy(targetMesh.matrixWorld).invert();

    const sphere = new THREE.Sphere();
    sphere.center.copy(center).applyMatrix4(inverseMatrix);
    sphere.radius = size;

    const indices = new Set();

    const triangles = new Set();
    const accumulatedTriangles = new Set();
    const accumulatedIndices = new Set();

    const tempVec = new THREE.Vector3();
    const indexAttr = targetMesh.geometry.index;

    const tempRay = new THREE.Ray();

    const dir = new THREE.Vector3();

    const faceNormal = new THREE.Vector3();
    const normalAttr = targetMesh.geometry.attributes.normal;

    bvh.shapecast({
        // Edited from the three-mesh-bvh's scult.js example
        intersectsBounds: (box, isLeaf, score, depth, nodeIndex) => {
            accumulatedTraversedNodeIndices.add(nodeIndex);
            const intersects = sphere.intersectsBox(box);
            const { min, max } = box;
            if (intersects) {
                for (let x = 0; x <= 1; x++) {
                    for (let y = 0; y <= 1; y++) {
                        for (let z = 0; z <= 1; z++) {
                            tempVec.set(x === 0 ? min.x : max.x, y === 0 ? min.y : max.y, z === 0 ? min.z : max.z);
                            if (!sphere.containsPoint(tempVec)) {
                                return INTERSECTED;
                            }
                        }
                    }
                }
                return CONTAINED;
            }
            return intersects ? INTERSECTED : NOT_INTERSECTED;
        },
        intersectsRange: (offset, count, contained, depth, nodeIndex) => {
            accumulatedTraversedNodeIndices.add(nodeIndex);
        },
        intersectsTriangle: (tri, index, contained) => {
            /*
      const centroid = tri.a
        .clone()
        .add(tri.b)
        .add(tri.c)
        .multiplyScalar(1 / 3);

      // check for obstruction, dir is Vector3
      dir.subVectors(centroid, camera.position).normalize();
      tempRay.origin.copy(camera.position);
      tempRay.direction.copy(dir);

      const res = targetMesh.geometry.boundsTree.raycastFirst(
        tempRay,
        THREE.FrontSide
      );

      // index from intersectTriangles args
      if (res && res.faceIndex !== index) {
        return false;
      }*/

            const centroid = tri.a
                .clone()
                .add(tri.b)
                .add(tri.c)
                .multiplyScalar(1 / 3);

            tri.getNormal(faceNormal);
            tempRay.origin.copy(centroid).addScaledVector(faceNormal, 1e-6);
            tempRay.direction.subVectors(camera.position, centroid);

            const res = targetMesh.geometry.boundsTree.raycastFirst(tempRay, THREE.DoubleSide);
            if (res) {
                return false;
            }

            const triIndex = index;
            triangles.add(triIndex);
            accumulatedTriangles.add(triIndex);

            const i3 = 3 * index;
            const a = i3 + 0;
            const b = i3 + 1;
            const c = i3 + 2;
            const va = indexAttr.getX(a);
            const vb = indexAttr.getX(b);
            const vc = indexAttr.getX(c);
            if (contained) {
                indices.add(va);
                indices.add(vb);
                indices.add(vc);

                accumulatedIndices.add(va);
                accumulatedIndices.add(vb);
                accumulatedIndices.add(vc);
            } else {
                if (sphere.containsPoint(tri.a)) {
                    indices.add(va);
                    accumulatedIndices.add(va);
                }

                if (sphere.containsPoint(tri.b)) {
                    indices.add(vb);
                    accumulatedIndices.add(vb);
                }

                if (sphere.containsPoint(tri.c)) {
                    indices.add(vc);
                    accumulatedIndices.add(vc);
                }
            }

            return false;
        }
    });
    return {
        indices,
        accumulatedTraversedNodeIndices,
        accumulatedEndNodeIndices,
        accumulatedTriangles,
        accumulatedIndices
    };
};

const intersecsBounds = (sphere, box) => {
    const tempVec = new THREE.Vector3();
    const intersects = sphere.intersectsBox(box);
    const { min, max } = box;
    if (intersects) {
        for (let x = 0; x <= 1; x++) {
            for (let y = 0; y <= 1; y++) {
                for (let z = 0; z <= 1; z++) {
                    tempVec.set(x === 0 ? min.x : max.x, y === 0 ? min.y : max.y, z === 0 ? min.z : max.z);
                    if (!sphere.containsPoint(tempVec)) {
                        return INTERSECTED;
                    }
                }
            }
        }
        return CONTAINED;
    }
    return intersects ? INTERSECTED : NOT_INTERSECTED;
};

export const getIntersectionPoints = (geometry, indices, rchanged) => {
    const indexAttr = geometry.index;
    const position = geometry.getAttribute('position');
    if (!indexAttr) return [];
    const points = [];
    for (let i = 0, l = indices.length; i < l; i += rchanged) {
        const a = indexAttr.getX(indices[i]);
        const b = indexAttr.getY(indices[i]);
        const c = indexAttr.getZ(indices[i]);
        points.push(new THREE.Vector3(position.getX(a), position.getY(a), position.getZ(a)));
        points.push(new THREE.Vector3(position.getX(b), position.getY(b), position.getZ(b)));
        points.push(new THREE.Vector3(position.getX(c), position.getY(c), position.getZ(c)));
    }
    return points;
};

export const getIntersectionPointsIndices = (geometry, triangleIndices) => {
    const indexAttr = geometry.index;
    if (!indexAttr) return [];
    const indics = [];
    for (let i = 0, l = triangleIndices.length; i < l; i += 1) {
        const va = indexAttr.getX(triangleIndices[i]);
        const vb = indexAttr.getY(triangleIndices[i]);
        const vc = indexAttr.getZ(triangleIndices[i]);
        indics.push(va);
        indics.push(vb);
        indics.push(vc);
    }
    return indics;
};