raycast method

  1. @override
void raycast(
  1. Raycaster raycaster,
  2. List<Intersection> intersects
)
override

Implementation

@override
raycast(Raycaster raycaster, intersects) {
  var start = Vector4.init();
  var end = Vector4.init();

  var ssOrigin = Vector4.init();
  var ssOrigin3 = Vector3.init();
  var mvMatrix = Matrix4();
  var line = Line3(null, null);
  var closestPoint = Vector3.init();

  if (raycaster.camera == null) {
    print('LineSegments2: "Raycaster.camera" needs to be set in order to raycast against LineSegments2.');
  }

  var threshold = (raycaster.params["Line2"] != null) ? raycaster.params["Line2"].threshold ?? 0 : 0;

  var ray = raycaster.ray;
  var camera = raycaster.camera;
  var projectionMatrix = camera?.projectionMatrix ?? Matrix4();

  var geometry = this.geometry!;
  var material = this.material;
  var resolution = material.resolution;
  var lineWidth = material.linewidth + threshold;

  var instanceStart = geometry.attributes["instanceStart"];
  var instanceEnd = geometry.attributes["instanceEnd"];

  // pick a point 1 unit out along the ray to avoid the ray origin
  // sitting at the camera origin which will cause "w" to be 0 when
  // applying the projection matrix.
  ray.at(1, ssOrigin);
  // TODO ray.at need Vec3 but ssOrigin is vec4

  // ndc space [ - 1.0, 1.0 ]
  ssOrigin.w = 1;
  ssOrigin.applyMatrix4(camera?.matrixWorldInverse ?? Matrix4());
  ssOrigin.applyMatrix4(projectionMatrix);
  ssOrigin.multiplyScalar(1 / ssOrigin.w);

  // screen space
  ssOrigin.x *= resolution.x / 2;
  ssOrigin.y *= resolution.y / 2;
  ssOrigin.z = 0;

  ssOrigin3.copy(ssOrigin);

  var matrixWorld = this.matrixWorld;
  mvMatrix.multiplyMatrices(camera?.matrixWorldInverse ?? Matrix4(), matrixWorld);

  for (var i = 0, l = instanceStart.count; i < l; i++) {
    start.fromBufferAttribute(instanceStart, i);
    end.fromBufferAttribute(instanceEnd, i);

    start.w = 1;
    end.w = 1;

    // camera space
    start.applyMatrix4(mvMatrix);
    end.applyMatrix4(mvMatrix);

    // clip space
    start.applyMatrix4(projectionMatrix);
    end.applyMatrix4(projectionMatrix);

    // ndc space [ - 1.0, 1.0 ]
    start.multiplyScalar(1 / start.w);
    end.multiplyScalar(1 / end.w);

    // skip the segment if it's outside the camera near and far planes
    var isBehindCameraNear = start.z < -1 && end.z < -1;
    var isPastCameraFar = start.z > 1 && end.z > 1;
    if (isBehindCameraNear || isPastCameraFar) {
      continue;
    }

    // screen space
    start.x *= resolution.x / 2;
    start.y *= resolution.y / 2;

    end.x *= resolution.x / 2;
    end.y *= resolution.y / 2;

    // create 2d segment
    line.start.copy(start);
    line.start.z = 0;

    line.end.copy(end);
    line.end.z = 0;

    // get closest point on ray to segment
    var param = line.closestPointToPointParameter(ssOrigin3, true);
    line.at(param, closestPoint);

    // check if the intersection point is within clip space
    var zPos = MathUtils.lerp(start.z, end.z, param);
    var isInClipSpace = zPos >= -1 && zPos <= 1;

    var isInside = ssOrigin3.distanceTo(closestPoint) < lineWidth * 0.5;

    if (isInClipSpace && isInside) {
      line.start.fromBufferAttribute(instanceStart, i);
      line.end.fromBufferAttribute(instanceEnd, i);

      line.start.applyMatrix4(matrixWorld);
      line.end.applyMatrix4(matrixWorld);

      var pointOnLine = Vector3.init();
      var point = Vector3.init();

      ray.distanceSqToSegment(line.start, line.end, point, pointOnLine);

      intersects.add(Intersection({
        "point": point,
        "pointOnLine": pointOnLine,
        "distance": ray.origin.distanceTo(point),
        "object": this,
        "face": null,
        "faceIndex": i,
        "uv": null,
        "uv2": null,
      }));
    }
  }
}