javascriptalgorithmthree.js3dface

THREE.js Detecting adjacent faces from a raycaster intersection point


I have a Mesh created with a BufferGeometry. I also have the coordinates of where my mouse intersects the Mesh, using the Raycaster.

I am trying to detect faces within(and touching) a radius from the intersection point. Once I detect the "tangent" faces, I then want to color the faces. Because I am working with a BufferGeometry, I am manipulating the buffer attributes on my geometry.

Here is my code:

let vertexA;
let vertexB;
let vertexC;
let intersection;
const radius = 3;
const color = new THREE.Color('red');
const positionsAttr = mesh.geometry.attributes.position;
const colorAttr = mesh.geometry.attributes.color;

// on every mouseMove event, do below:

vertexA = new THREE.Vector3();
vertexB = new THREE.Vector3();
vertexC = new THREE.Vector3();
intersection = raycaster.intersectObject(mesh).point;

// function to detect tangent edge
function isEdgeTouched(v1, v2, point, radius) {
  const line = new THREE.Line3();
  const closestPoint = new THREE.Vector3();
  line.set(v1, v2);
  line.closestPointToPoint(point, true, closestPoint);
  return point.distanceTo(closestPoint) < radius;
}

// function to color a face
function colorFace(faceIndex) {
  colorAttr.setXYZ(faceIndex * 3 + 0, color.r, color.g, color.b);
  colorAttr.setXYZ(faceIndex * 3 + 0, color.r, color.g, color.b);
  colorAttr.setXYZ(faceIndex * 3 + 0, color.r, color.g, color.b);
  colorAttr.needsUpdate = true;
}


// iterate over each face, color it if tangent
for (let i=0; i < (positionsAttr.count) /3); i++) {
  vertexA.fromBufferAttribute(positionsAttr, i * 3 + 0);
  vertexB.fromBufferAttribute(positionsAttr, i * 3 + 1);
  vertexC.fromBufferAttribute(positionsAttr, i * 3 + 2);
  if (isEdgeTouched(vertexA, vertexB, point, radius)
    || isEdgeTouched(vertexA, vertexB, point, radius)
    || isEdgeTouched(vertexA, vertexB, point, radius)) {
    colorFace(i);
}

While this code works, it seems to be very poor in performance especially when I am working with a geometry with many many faces. When I checked the performance monitor on Chrome DevTools, I notices that both the isEdgeTouched and colorFace functions take up too much time on each iteration for a face.

Is there a way to improve this algorithm, or is there a better algorithm to use to detect adjacent faces?

Edit

I got some help from the THREE.js slack channel, and modified the algorithm to use Three's Sphere. I am now no longer doing "edge" detection, but instead checking whether a face is within the Sphere

Updated code below:

const sphere = new THREE.Sphere(intersection, radius);

// now checking if each vertex of a face is within sphere
// if all are, then color the face at index i
for (let i=0; i < (positionsAttr.count) /3); i++) {
  vertexA.fromBufferAttribute(positionsAttr, i * 3 + 0);
  vertexB.fromBufferAttribute(positionsAttr, i * 3 + 1);
  vertexC.fromBufferAttribute(positionsAttr, i * 3 + 2);
  if (sphere.containsPoint(vertexA)
    && sphere.containsPoint(vertexA)
    && sphere.containsPoint(vertexA)) {
    colorFace(i);
}

When I tested this in my app, I noticed that the performance has definitely improved from the previous version. However, I am still wondering if I could improve this further.


Solution

  • This seem to be a classic Nearest Neighbors problem.

    You can narrow the search by finding the nearest triangles to a given point very fast by building a Bounding Volume Hierarchy (BVH) for the mesh, such as the AABB-tree.

    BVH: https://en.m.wikipedia.org/wiki/Bounding_volume_hierarchy

    AABB-Tree: https://www.azurefromthetrenches.com/introductory-guide-to-aabb-tree-collision-detection/

    Then you can query against the BVH a range query using a sphere or a box of a given radius. That amounts to traverse the BVH using a sphere/box "query" which is used to discard quickly and very early the Bounding Volume Nodes that does not clip the sphere/box "query". At the end the real distance or intersection test is made only with triangles whose BV intersect the sphere/box "query", typically a very small fraction of the triangles.

    The complexity of the query against the BVH is O(log n) in contrast with your approach which is O(n).