// File: tomfunkhouser[P]gaps/pkgs/R3Shapes/R3Mesh.cpp
// Function: gaps::R3Mesh::Intersection(gaps::R3Ray const&, gaps::R3MeshFace*, gaps::R3MeshIntersection*) const
R3MeshType R3Mesh::
Intersection(const R3Ray& ray, R3MeshFace *f, R3MeshIntersection *intersection) const
{
  // Initialize pick variables
  R3MeshType type = R3_MESH_NULL_TYPE;
  if (intersection) {
    intersection->type = R3_MESH_NULL_TYPE;
    intersection->vertex = NULL;
    intersection->edge = NULL;
    intersection->face = NULL;
    intersection->t = RN_INFINITY;
  }

  // Check if degenerate face
  R3Vector normal = FaceNormal(f);
  if (RNIsZero(normal.Dot(normal))) return R3_MESH_NULL_TYPE;

  // Check if face intersects ray
  RNScalar t;
  R3Point p;
  R3Plane plane = FacePlane(f);
  if (R3Intersects(ray, plane, &p, &t) || R3Intersects(ray, -plane, &p, &t)) {
    // Check if face bbox contains p
    if (R3Contains(FaceBBox(f), p)) {
#if 1
      // Get convenient variables
      R3Point& p0 = f->vertex[0]->position;
      R3Point& p1 = f->vertex[1]->position;
      R3Point& p2 = f->vertex[2]->position;

      // Check side of first edge
      R3Vector e0 = p1 - p0;
      e0.Normalize();
      R3Vector n0 = normal % e0;
      R3Plane s0(p0, n0);
      RNScalar b0 = R3SignedDistance(s0, p);
      if (RNIsNegative(b0)) return R3_MESH_NULL_TYPE;

      // Check side of second edge
      R3Vector e1 = p2 - p1;
      e1.Normalize();
      R3Vector n1 = normal % e1;
      R3Plane s1(p1, n1);
      RNScalar b1 = R3SignedDistance(s1, p);
      if (RNIsNegative(b1)) return R3_MESH_NULL_TYPE;

      // Check side of third edge
      R3Vector e2 = p0 - p2;
      e2.Normalize();
      R3Vector n2 = normal % e2;
      R3Plane s2(p2, n2);
      RNScalar b2 = R3SignedDistance(s2, p);
      if (RNIsNegative(b2)) return R3_MESH_NULL_TYPE;

      // Fill in interesection info
      // Could easily return intersection type, etc. but too lazy now
      type = R3_MESH_FACE_TYPE;
      if (intersection) {
        intersection->type = type;
        intersection->point = p;
        intersection->vertex = NULL;
        intersection->edge = NULL;
        intersection->face = f;
        intersection->t = t;
      }
#else
      // This does not always work -- don't know why
      // Compute interpolation parameters for intersection point (from Graphics Gems I, page 393)
      RNScalar a, b;
      RNDimension dim = FaceNormal(f).MaxDimension();
      RNDimension dim1 = (dim + 1) % 3;
      RNDimension dim2 = (dim + 2) % 3;
      R3Point& p0 = f->vertex[0]->position;
      R3Point& p1 = f->vertex[1]->position;
      R3Point& p2 = f->vertex[2]->position;
      RNScalar u0 = p[dim1] - p0[dim1];
      RNScalar v0 = p[dim2] - p0[dim2];
      RNScalar u1 = p1[dim1] - p0[dim1];
      RNScalar u2 = p2[dim1] - p0[dim1];
      RNScalar v1 = p1[dim2] - p0[dim2];
      RNScalar v2 = p2[dim2] - p0[dim2];
      if (RNIsZero(u1)) {
        if (RNIsZero(u2)) {
          a = RN_INFINITY;
          b = RN_INFINITY;
        }
        else {
          b = u0/u2;
          if (RNIsLess(b, 0.0) || RNIsGreater(b, 1.0) || RNIsZero(v1)) a = b = RN_INFINITY;
          else a = (v0 - b*v2) / v1;
        }
      }
      else {
        RNScalar denom = v2*u1 - u2*v1;
        if (RNIsZero(denom)) a = b = RN_INFINITY;
        else {
          b = (v0*u1 - u0*v1) / denom;
          if (RNIsLess(b, 0.0) || RNIsGreater(b, 1.0) || RNIsZero(u1)) a = b = RN_INFINITY;
          else a = (u0 - b*u2) / u1;
        }
      }

      // Check if intersection point is inside triangle
      if (RNIsPositiveOrZero(a, 0.001) && RNIsPositiveOrZero(b, 0.001) && RNIsLess(a+b, 1.0, 0.001)) {
        if (RNIsZero(a, 0.01)) {
          if (RNIsZero(b, 0.01)) {
            type = R3_MESH_VERTEX_TYPE;
            if (intersection) {
              intersection->type = type;
              intersection->point = p;
              intersection->vertex = f->vertex[0];
              intersection->edge = f->edge[0];
              intersection->face = f;
              intersection->t = t;
            }
          }
          else if (RNIsEqual(b, 1.0, 0.01)) {
            type = R3_MESH_VERTEX_TYPE;
            if (intersection) {
              intersection->type = type;
              intersection->point = p;
              intersection->vertex = f->vertex[2];
              intersection->edge = f->edge[2];
              intersection->face = f;
              intersection->t = t;
            }
          }
          else {
            type = R3_MESH_EDGE_TYPE;
            if (intersection) {
              intersection->type = type;
              intersection->point = p;
              intersection->vertex = NULL;
              intersection->edge = f->edge[2];
              intersection->face = f;
              intersection->t = t;
            }
          }
        }
        else if (RNIsEqual(a, 1.0, 0.01)) {
          type = R3_MESH_VERTEX_TYPE;
          if (intersection) {
            intersection->type = type;
            intersection->point = p;
            intersection->vertex = f->vertex[1];
            intersection->edge = f->edge[1];
            intersection->face = f;
            intersection->t = t;
          }
        }                      
        else {
          if (RNIsZero(b, 0.01)) {
            type = R3_MESH_EDGE_TYPE;
            if (intersection) {
              intersection->type = type;
              intersection->point = p;
              intersection->vertex = NULL;
              intersection->edge = f->edge[0];
              intersection->face = f;
              intersection->t = t;
            }
          }
          else if (RNIsEqual(a+b, 1.0, 0.01)) {
            type = R3_MESH_EDGE_TYPE;
            if (intersection) {
              intersection->type = type;
              intersection->point = p;
              intersection->vertex = NULL;
              intersection->edge = f->edge[1];
              intersection->face = f;
              intersection->t = t;
            }
          }
          else {
            type = R3_MESH_FACE_TYPE;
            if (intersection) {
              intersection->type = type;
              intersection->point = p;
              intersection->vertex = NULL;
              intersection->edge = NULL;
              intersection->face = f;
              intersection->t = t;
            }
          }
        }
      }
#endif
    }
  }

  // Return intersection type
  return type;
}

// Assembly:
subq $0x1e8, %rsp            # imm = 0x1E8
movq %rdi, 0x1d8(%rsp)
movq %rsi, 0x1d0(%rsp)
movq %rdx, 0x1c8(%rsp)
movq %rcx, 0x1c0(%rsp)
movq 0x1d8(%rsp), %rax
movq %rax, 0x18(%rsp)
movl $0x0, 0x1bc(%rsp)
cmpq $0x0, 0x1c0(%rsp)
je 0x24620
movq 0x1c0(%rsp), %rax
movl $0x0, (%rax)
movq 0x1c0(%rsp), %rax
movq $0x0, 0x8(%rax)
movq 0x1c0(%rsp), %rax
movq $0x0, 0x10(%rax)
movq 0x1c0(%rsp), %rax
movq $0x0, 0x18(%rax)
leaq 0x153ee1(%rip), %rax    # 0x1784f0
movsd (%rax), %xmm0
movq 0x1c0(%rsp), %rax
movsd %xmm0, 0x38(%rax)
movq 0x18(%rsp), %rdi
movq 0x1c8(%rsp), %rsi
callq 0x13840
movq %rax, %rsi
leaq 0x1a0(%rsp), %rdi
callq 0x6fcf0
leaq 0x1a0(%rsp), %rsi
movq %rsi, %rdi
callq 0x702c0
leaq 0x153e87(%rip), %rax    # 0x1784e0
movsd (%rax), %xmm1
callq 0x36b80
cmpl $0x0, %eax
je 0x24677
movl $0x0, 0x1e4(%rsp)
jmp 0x24a31
leaq 0x180(%rsp), %rdi
callq 0x6eb40
movq 0x18(%rsp), %rdi
movq 0x1c8(%rsp), %rsi
callq 0x13df0
movq %rax, %rsi
leaq 0x160(%rsp), %rdi
callq 0x6c140
movq 0x1d0(%rsp), %rdi
leaq 0x160(%rsp), %rsi
leaq 0x180(%rsp), %rdx
leaq 0x198(%rsp), %rcx
callq 0x7ae90
movl %eax, %ecx
movb $0x1, %al
cmpl $0x0, %ecx
movb %al, 0x17(%rsp)
jne 0x24726
movq 0x1d0(%rsp), %rax
movq %rax, 0x8(%rsp)
leaq 0x140(%rsp), %rdi
leaq 0x160(%rsp), %rsi
callq 0x38f60
movq 0x8(%rsp), %rdi
leaq 0x140(%rsp), %rsi
leaq 0x180(%rsp), %rdx
leaq 0x198(%rsp), %rcx
callq 0x7ae90
cmpl $0x0, %eax
setne %al
movb %al, 0x17(%rsp)
movb 0x17(%rsp), %al
testb $0x1, %al
jne 0x24733
jmp 0x24a23
movq 0x18(%rsp), %rdi
movq 0x1c8(%rsp), %rsi
callq 0x17e90
movq %rax, %rdi
leaq 0x180(%rsp), %rsi
callq 0x83a20
cmpl $0x0, %eax
je 0x24a21
movq 0x1c8(%rsp), %rax
movq 0x8(%rax), %rax
addq $0x18, %rax
movq %rax, 0x138(%rsp)
movq 0x1c8(%rsp), %rax
movq 0x10(%rax), %rax
addq $0x18, %rax
movq %rax, 0x130(%rsp)
movq 0x1c8(%rsp), %rax
movq 0x18(%rax), %rax
addq $0x18, %rax
movq %rax, 0x128(%rsp)
movq 0x130(%rsp), %rsi
movq 0x138(%rsp), %rdx
leaq 0x110(%rsp), %rdi
callq 0x6eeb0
leaq 0x110(%rsp), %rdi
callq 0x70410
leaq 0xf8(%rsp), %rdi
leaq 0x1a0(%rsp), %rsi
leaq 0x110(%rsp), %rdx
callq 0x70af0
movq 0x138(%rsp), %rsi
leaq 0xd8(%rsp), %rdi
leaq 0xf8(%rsp), %rdx
callq 0x6c260
leaq 0xd8(%rsp), %rdi
leaq 0x180(%rsp), %rsi
callq 0x86f90
movsd %xmm0, 0xd0(%rsp)
movsd 0xd0(%rsp), %xmm0
leaq 0x153ca8(%rip), %rax    # 0x1784e0
movsd (%rax), %xmm1
callq 0x38fd0
cmpl $0x0, %eax
je 0x24856
movl $0x0, 0x1e4(%rsp)
jmp 0x24a31
movq 0x128(%rsp), %rsi
movq 0x130(%rsp), %rdx
leaq 0xb8(%rsp), %rdi
callq 0x6eeb0
leaq 0xb8(%rsp), %rdi
callq 0x70410
leaq 0xa0(%rsp), %rdi
leaq 0x1a0(%rsp), %rsi
leaq 0xb8(%rsp), %rdx
callq 0x70af0
movq 0x130(%rsp), %rsi
leaq 0x80(%rsp), %rdi
leaq 0xa0(%rsp), %rdx
callq 0x6c260
leaq 0x80(%rsp), %rdi
leaq 0x180(%rsp), %rsi
callq 0x86f90
movsd %xmm0, 0x78(%rsp)
movsd 0x78(%rsp), %xmm0
leaq 0x153bfe(%rip), %rax    # 0x1784e0
movsd (%rax), %xmm1
callq 0x38fd0
cmpl $0x0, %eax
je 0x24900
movl $0x0, 0x1e4(%rsp)
jmp 0x24a31
movq 0x138(%rsp), %rsi
movq 0x128(%rsp), %rdx
leaq 0x60(%rsp), %rdi
callq 0x6eeb0
leaq 0x60(%rsp), %rdi
callq 0x70410
leaq 0x48(%rsp), %rdi
leaq 0x1a0(%rsp), %rsi
leaq 0x60(%rsp), %rdx
callq 0x70af0
movq 0x128(%rsp), %rsi
leaq 0x28(%rsp), %rdi
leaq 0x48(%rsp), %rdx
callq 0x6c260
leaq 0x28(%rsp), %rdi
leaq 0x180(%rsp), %rsi
callq 0x86f90
movsd %xmm0, 0x20(%rsp)
movsd 0x20(%rsp), %xmm0
leaq 0x153b69(%rip), %rax    # 0x1784e0
movsd (%rax), %xmm1
callq 0x38fd0
cmpl $0x0, %eax
je 0x24995
movl $0x0, 0x1e4(%rsp)
jmp 0x24a31
movl $0x3, 0x1bc(%rsp)
cmpq $0x0, 0x1c0(%rsp)
je 0x24a1f
movl 0x1bc(%rsp), %ecx
movq 0x1c0(%rsp), %rax
movl %ecx, (%rax)
movq 0x1c0(%rsp), %rdi
addq $0x20, %rdi
leaq 0x180(%rsp), %rsi
callq 0x6f1e0
movq 0x1c0(%rsp), %rax
movq $0x0, 0x8(%rax)
movq 0x1c0(%rsp), %rax
movq $0x0, 0x10(%rax)
movq 0x1c8(%rsp), %rcx
movq 0x1c0(%rsp), %rax
movq %rcx, 0x18(%rax)
movsd 0x198(%rsp), %xmm0
movq 0x1c0(%rsp), %rax
movsd %xmm0, 0x38(%rax)
jmp 0x24a21
jmp 0x24a23
movl 0x1bc(%rsp), %eax
movl %eax, 0x1e4(%rsp)
movl 0x1e4(%rsp), %eax
addq $0x1e8, %rsp            # imm = 0x1E8
retq