// FILE:     Triangle.java
// PURPOSE:  Represents a triangle object in our ray tracing environment.
// METHOD:   Holds three 3D vertices, and has the ability to
//           determine if a casting ray intersects with it and what the
//           resulting shade should be.
//
// MODS:     11.25.98 -- Leonard McMillan -- original 
//           11.25.98 -- jlueck@mit.edu   -- Rev 2


import java.util.Vector;
import java.awt.Color;

public class Triangle implements Renderable {

    Surface surface;
    Vector3D N;
    Vector3D v1, v2, v3;
    PlaneEqn e1, e2, e3;
    float D;
    Vector3D P;

    public Triangle(Surface s, Vector3D v1, Vector3D v2, Vector3D v3) {

        surface = s;
	this.v1 = v1;
	this.v2 = v2;
	this.v3 = v3;

	// calculate normal for this triangle once.
	Vector3D ev1 = v2.sub(v1);
	Vector3D ev2 = v3.sub(v1);
	N = ev1.cross(ev2);
	N.normalize();

	// calculate the fourth parameter to plane equation once.
	D = -N.dot(v1);

	// set up edge equations.
	e1 = new PlaneEqn(v2, v1, N);
	e2 = new PlaneEqn(v3, v2, N);
	e3 = new PlaneEqn(v1, v3, N);
	
    }

    public boolean intersect(Ray ray) {

	float uN = ray.direction.dot(N);

	// if the plane is not front-facing, doesn't intersect.
	if (uN >= 0) return false;
	
	// calculate P = Po + tu (pg. 534 Hearn & Baker)
	float t;
	t = -(D + N.dot(ray.origin)) / uN;

	// see if plane is in forward direction of ray
	if (t < 0) return false;

	// test to see if we're closer than a previous object
	if (t > ray.t) return false;

	// do inside-outside test to see if the point of intersection
	// is within the triangle.
	P = new Vector3D(ray.origin.x + t*ray.direction.x,
			 ray.origin.y + t*ray.direction.y,
			 ray.origin.z + t*ray.direction.z);
	
	if (e1.eval(P) >= 0 && e2.eval(P) >= 0 && e3.eval(P) >= 0) {
	    
	    ray.t = t;
	    ray.object = this;
	    return true;
	}

	return false;
	
    }

    public Color Shade(Ray ray, Vector lights, Vector objects, Color bgnd) {
        // An object shader doesn't really do too much other than
        // supply a few critical bits of geometric information
        // for a surface shader. It must must compute:
        //
        //   1. the point of intersection (p)
        //   2. a unit-length surface normal (n)
        //   3. a unit-length vector towards the ray's origin (v)
        //   4. false for inside flag
	//

        Vector3D v = new Vector3D(-ray.direction.x, 
				  -ray.direction.y, 
				  -ray.direction.z);
	Vector3D n = new Vector3D(N);
	n.normalize();

        return surface.Shade(P, n, v, lights, objects, bgnd, false);
    }

    public String toString() {
        return ("triangle "+v1+" "+v2+" "+v3);
    }
}
