@pixi/3d
    Preparing search index...

    Interface IHitVolume3D

    A hit shape of your own for Container3D#hitVolume, like IHitArea in 2D

    Any object with these members works, with no base class: an adapter around a physics collider, an analytic capsule, a signed distance field. Box, Sphere and Geometry3D work as they are, with faster built-in tests, so don't wrap them.

    Everything is in the node's local space, so the volume never deals with transforms.

    // A flat disc in the node's local XZ plane.
    class DiscVolume implements IHitVolume3D {
    constructor(public radius: number) {}

    get boundingRadiusSq(): number {
    return this.radius * this.radius;
    }

    raycast(localRay: Ray, out: HitResult3D): number {
    const t = -localRay.origin.y / localRay.direction.y;
    if (!Number.isFinite(t) || t < 0) return -1; // behind the origin, or parallel
    localRay.at(t, out.point);
    if (out.point.x ** 2 + out.point.z ** 2 > this.radius ** 2) return -1;
    out.distance = t;
    out.normal.set(0, 0, 0);
    out.uv.set(0, 0);
    out.triangleIndex = -1;
    return t;
    }
    }

    mesh.interactive = true;
    mesh.hitVolume = new DiscVolume(2);
    interface IHitVolume3D {
        boundingRadiusSq: number;
        containsPoint?(localPoint: Vector3): boolean;
        raycast(localRay: Ray, out: HitResult3D): number;
    }
    Index
    boundingRadiusSq: number

    Squared radius of a sphere around the node's local origin that encloses the volume

    A ray that misses this sphere skips raycast. It's read on every pick, so it can change with the shape. Infinity suits an unbounded shape, at the cost of a raycast call on every pick.

    public readonly boundingRadiusSq = Infinity; // an endless ground plane has no bounds
    
    • Whether a point in the node's local space is inside the volume

      Only needed for the volume to clip the node's descendants, as every hitVolume you set does. Without it, the volume still picks but never clips.

      Parameters

      • localPoint: Vector3

        The point to test, in the node's local space.

      Returns boolean

      Whether the point lies inside the volume.

      public containsPoint(localPoint: Vector3): boolean {
      return localPoint.x ** 2 + localPoint.z ** 2 <= radius ** 2 && Math.abs(localPoint.y) <= halfHeight;
      }
    • Finds where localRay first hits the volume

      localRay.direction is unit length, so t is the distance to the hit in the node's local units. Hits on nodes of different scale still compare correctly: the picker measures world distance from out.point.

      On a hit, fill out as the engine shapes do: out.point with localRay.at(t, out.point), out.distance with t, and, unless the volume has triangles, out.normal and out.uv with zeros and out.triangleIndex with -1. On a miss, return -1; the picker ignores out.

      Parameters

      • localRay: Ray

        The pick ray in the node's local space, unit-length direction.

      • out: HitResult3D

        Scratch buffer to fill on a hit.

      Returns number

      The parametric t of the closest hit (>= 0), or -1 for a miss.

      public raycast(localRay: Ray, out: HitResult3D): number {
      const t = localRay.intersectSphereT(bounds);
      if (t === -1) return -1;

      localRay.at(t, out.point);
      out.distance = t;
      out.normal.set(0, 0, 0);
      out.uv.set(0, 0);
      out.triangleIndex = -1;
      return t;
      }