trace method

RayTrace trace(
  1. Ray ray, {
  2. String? filterTag,
})

Trace ray through the world, bouncing off reflective surfaces.

filterTag — when non-null only entities with a matching RaycastColliderComponent.tag are considered at each segment.

Implementation

RayTrace trace(Ray ray, {String? filterTag}) {
  final segments = <RayTraceSegment>[];
  var currentRay = ray;

  for (int bounce = 0; bounce <= maxBounces; bounce++) {
    final hit = raycastSystem.castRay(currentRay, filterTag: filterTag);

    if (hit == null) {
      // Ray reached max distance without hitting anything.
      segments.add(
        RayTraceSegment(
          from: currentRay.origin,
          to: currentRay.at(currentRay.maxDistance),
        ),
      );
      break;
    }

    segments.add(
      RayTraceSegment(from: currentRay.origin, to: hit.point, hit: hit),
    );

    final collider = hit.entity.getComponent<RaycastColliderComponent>()!;

    // Terminate if the surface is non-reflective or we've used all bounces.
    if (!collider.isReflective ||
        collider.reflectivity < minReflectivity ||
        bounce == maxBounces) {
      break;
    }

    // Reflect direction: r = d - 2(d·n)n
    final d = currentRay.direction;
    final n = hit.normal;
    final dDotN = d.dx * n.dx + d.dy * n.dy;
    final reflected = d - Offset(n.dx * 2.0 * dDotN, n.dy * 2.0 * dDotN);

    // Advance the origin slightly past the hit point to avoid self-intersection.
    currentRay = Ray(
      origin: hit.point + reflected * 1.0,
      direction: reflected,
      maxDistance: currentRay.maxDistance * collider.reflectivity,
    );
  }

  return RayTrace(segments: segments);
}