//----------------------------------------------------------------------------
// William Baxter III's Ray Tracer
//
//     Project for Comp 238, Raster Graphics
//     University of North Carolina at Chapel Hill
//     
// $Id:$
//----------------------------------------------------------------------------

#include "wb3Triangle.hpp"

//----------------------------------------------------------------------------
wb3Triangle::wb3Triangle()
{
  m_vert[0] = Vec3f::ZERO;
  m_vert[1] = Vec3f(0,1,0);
  m_vert[2] = Vec3f(0,0,1);
  // compute normal to plane
  Vec3f u(m_vert[1]-m_vert[0]);
  Vec3f v(m_vert[2]-m_vert[0]);
  m_N = u ^ v;
  m_N.Normalize();
  // last part of plane eq
  m_fD = -(m_vert[0] * m_N);
  m_uv = 0;
} 
//----------------------------------------------------------------------------
wb3Triangle::wb3Triangle(
  const Vec3f& p0, const Vec3f& p1, const Vec3f& p2)
{
  m_vert[0] = p0;
  m_vert[1] = p1;
  m_vert[2] = p2;
  // compute normal to plane
  Vec3f u(p1-p0);
  Vec3f v(p2-p0);
  m_N = u ^ v;
  m_N.Normalize();
  // last part of plane eq
  m_fD = -(p0 * m_N);
} 
//----------------------------------------------------------------------------
wb3Triangle::wb3Triangle(const Vec3f verts[3])
{
  m_vert[0] = verts[0];
  m_vert[1] = verts[1];
  m_vert[2] = verts[2];
  // compute normal to plane
  Vec3f u(verts[1]-verts[0]);
  Vec3f v(verts[2]-verts[0]);
  m_N = u^v; // cross prod
  m_N.Normalize();
  // last part of plane eq
  m_fD = -(verts[0] * m_N);
} 

//----------------------------------------------------------------------------
wb3Triangle::~wb3Triangle()
{
  if (m_uv)
    delete [] m_uv;
  if (m_norm)
    delete [] m_norm;
}


//----------------------------------------------------------------------------
// CHECKS IF 2D POINT P IS IN A 2D TRI ABC.
//----------------------------------------------------------------------------
static int ptInTri2D(float Px, float Py, 
                     float Ax, float Ay, float Bx, float By, float Cx, float Cy)
{
  float dABx=Bx-Ax, dABy=By-Ay, dBCx=Cx-Bx, dBCy=Cy-By; // "REPEATS"
  if (dABx*dBCy < dABy*dBCx) // CW
  {
    if (dABx*(Py-Ay) >= dABy*(Px-Ax)) return(0);        // ABxAP
    if (dBCx*(Py-By) >= dBCy*(Px-Bx)) return(0);        // BCxBP
    if ((Ax-Cx)*(Py-Cy) >= (Ay-Cy)*(Px-Cx)) return(0);  // CAxCP
  }
  else // CCW
  {
    if (dABx*(Py-Ay) < dABy*(Px-Ax)) return(0);         // ABxAP
    if (dBCx*(Py-By) < dBCy*(Px-Bx)) return(0);         // BCxBP
    if ((Ax-Cx)*(Py-Cy) < (Ay-Cy)*(Px-Cx)) return(0);   // CAxCP
  }
  return(1); // "INSIDE" EACH EDGE'S IN-HALF-SPACE (PT P IS INSIDE TRIANGLE)
};

//----------------------------------------------------------------------------
// CHECKS IF 3D POINT P (ON ABC'S PLANE) IS IN 3D TRI ABC.
// P=PtOnTriPlane, N=PlaneNormal (does not have to be normalized)
//----------------------------------------------------------------------------
int wb3Triangle::ptInTri3D(const Vec3f& P) const
{
  #define ABS(_x) (((_x)<0)?(-(_x)):_x)  // HANDY UNIVERSAL ABSOLUTE VALUE FUNC

  // DETERMINE LARGEST COMPONENT OF NORMAL 
  //   (magnitude, since we want the largest projection)
  Vec3f N(ABS(m_N.x), ABS(m_N.y), ABS(m_N.z));

  // PROJECT ONTO PLANE WHERE PERPENDICULAR TO LARGEST NORMAL COMPONENT AXIS
  const Vec3f& A = m_vert[0];
  const Vec3f& B = m_vert[1];
  const Vec3f& C = m_vert[2];
  if (N.x>N.y && N.x>N.z)      // X IS LARGEST SO PROJECT ONTO YZ-PLANE
    return( ptInTri2D(P.y,P.z, A.y,A.z, B.y,B.z, C.y,C.z) );
  else if (N.y>N.x && N.y>N.z) // Y IS LARGEST SO PROJECT ONTO XZ-PLANE
    return( ptInTri2D(P.x,P.z, A.x,A.z, B.x,B.z, C.x,C.z) );
  else                         // Z IS LARGEST SO PROJECT ONTO XY-PLANE
    return( ptInTri2D(P.x,P.y, A.x,A.y, B.x,B.y, C.x,C.y) );
}

//----------------------------------------------------------------------------
const wb3Artifact* wb3Triangle::Intersect(const Ray3f& r, float* dist,
                                          const wb3Artifact *ignore) const
{
  const wb3Artifact *ret = wb3Plane::Intersect(r, dist, ignore);
  if (ret)
  {
    // Get the point of intersection with plane
    Vec3f isect(r[*dist]);

    // Find out if that's inside the triangle
    if (ptInTri3D(isect))
      return this;
  }
  return 0;
}
//----------------------------------------------------------------------------

void wb3Triangle::SetVerts(
  const Vec3f& p0, const Vec3f& p1, const Vec3f& p2) 
{
  m_vert[0] = p0;
  m_vert[1] = p1;
  m_vert[2] = p2;
  // compute normal to plane
  Vec3f u(p1-p0);
  Vec3f v(p2-p0);
  m_N = u^v; // cross prod
  m_N.Normalize();
  // last part of plane eq
  m_fD = -(p0 * m_N);
}

//----------------------------------------------------------------------------

void wb3Triangle::SetTexCoords(
  const Vec2f& p0, const Vec2f& p1, const Vec2f& p2)
{
  // Dynamically allocate because we could have TONS of triangles in a 
  // scene and it would be a waste to allocate space in all of them 
  // for something we don't use.
  if (!m_uv) m_uv = new Vec2f[3];
  m_uv[0] = p0;
  m_uv[1] = p1;
  m_uv[2] = p2;
}

//----------------------------------------------------------------------------

void wb3Triangle::SetNormals(
  const Vec3f& p0, const Vec3f& p1, const Vec3f& p2)
{
  // Dynamically allocate because we could have TONS of triangles in a 
  // scene and it would be a waste to allocate space in all of them 
  // for something we don't use.
  if (!m_norm) m_norm = new Vec3f[3];
  m_norm[0] = p0;
  m_norm[1] = p1;
  m_norm[2] = p2;
  // Just to be sure...
  m_norm[0].Normalize();
  m_norm[1].Normalize();
  m_norm[2].Normalize();
}


//----------------------------------------------------------------------------
Vec3f& wb3Triangle::GetColorComponent(
    Vec3f& color, wb3Component::ComponentType type, const Vec3f& Pw) const
{
  // In general this will iterate over all textures in the textureset of
  // the given type and add each of their contributions to the Materials.
  color = GetMaterial()->GetColorComponent(type);
  // Color becomes the base color
  
  const wb3TextureList *tl = GetTextureList(type);
  if (!tl) return color;

  // [Texture set stuff...]
  // iterator
  using namespace std;

  Vec2f UV;
  if (m_uv)
  {
    // Barycentric UV generation
    Vec3f v01 = m_vert[1] - m_vert[0];
    Vec3f v12 = m_vert[2] - m_vert[1];
    Vec3f v20 = m_vert[0] - m_vert[2];
    float total = 1.0f/(v01 ^ v12).Length(); // 2xarea of tri

    Vec3f cross((m_vert[1]-Pw) ^ v12);
    float c0 = cross.Length()*total;
    cross.Set((m_vert[2]-Pw) ^ v20);
    float c1 = cross.Length()*total;
    float c2 = 1.0f - c0 - c1;
    UV = c0 * m_uv[0];
    UV += c1 * m_uv[1];
    UV += c2 * m_uv[2];
  }
  else 
  {
    // Use standard mapping
    // TODO
  }
  Vec3f contrib; 
  list<wb3Texture*>::const_iterator it;
  for(it = tl->begin(); it != tl->end(); ++it)
  {
    (*it)->GetValue(contrib, UV);
    color &= contrib;
  }
  return color;
}
//----------------------------------------------------------------------------
float wb3Triangle::GetScalarComponent(
    wb3Component::ComponentType type, const Vec3f& Pw) const
{
  // In general this will iterate over all textures in the textureset of
  // the given type and add each of their contributions to the Materials.
  // Except for bump
  float attrib = GetMaterial()->GetScalarComponent(type);

  // [Texture set stuff...]

  return attrib;
}
//----------------------------------------------------------------------------

