import java.applet.*;
import java.awt.*;
import java.io.*;
import java.net.*;
import java.util.*;
import Triangle;

// todo:
// transform lights?
// fix z-buffering to interpolate properly? think so...
// fix polygon clipping:  fix off by one bug
// cull polys outside the view fustrum
// move vis to separate array
// cache!

public class PolyTest extends Applet {
  ZRaster raster;
  Vector p, v, s;
  TriPoly pList[];
  Vertex3D mList[];
  Vertex3D wList[];
  Vertex3D vList[];
  Vertex3D sList[];
  Vertex3D s2List[];
  Surface surfList[];
  Color background;
  Image screen2;

  Matrix3D view, world, screen, project;

  float eyex, eyey, eyez;
  float lookatx, lookaty, lookatz;
  float upx, upy, upz;
  float lightx, lighty, lightz;
  float fov;

  long time;
  int vis, tri;
  int vis2, tri2;

  Vector ps, vs, ls;
  Light ambient;

  public void init( ) {
    // Create a raster for rendering
    background = Color.black;
    raster = new ZRaster(this.getSize().width, this.getSize().height);
    raster.fill(background);
    screen2 = raster.toImage();

    eyex = 0;        eyey = 0;        eyez = -10;
    lookatx = 0;     lookaty = 0;     lookatz = 0;
    upx = 0;         upy = 1;         upz = 0;
    fov = 30;

    // Read in the triangle file
    p = new Vector();
    v = new Vector();
    s = new Vector();
    ls = new Vector();
    System.out.println("loading");
    InputStream is = null;
    try {
      is = new URL(getDocumentBase(), getParameter("datafile")).openStream();
      ReadInput(is);
      is.close();
    } catch (IOException e) {
      System.err.println("Error reading "+getParameter("datafile"));
    }
    pList = new TriPoly[p.size()];
    p.copyInto(pList);
    mList = new Vertex3D[v.size()];
    wList = new Vertex3D[v.size()];
    vList = new Vertex3D[v.size()];
    sList = new Vertex3D[v.size()];
    s2List = new Vertex3D[v.size()];
    v.copyInto(mList);
    v.copyInto(wList);
    v.copyInto(vList);
    v.copyInto(sList);
    v.copyInto(s2List);
    surfList = new Surface[s.size()];
    s.copyInto(surfList);

    project = new Matrix3D(raster);
    float t = (float) Math.sin(Math.PI*(fov/2)/180);
    float s = (t*size().height)/size().width;

    screen = new Matrix3D();
    screen.perspective(-t, t, -s, s, -1, -200);
    screen.lookAt(eyex, eyey, eyez, lookatx, lookaty, lookatz, upx, upy, upz);
        
    world = new Matrix3D();
    view = new Matrix3D();
    time = System.currentTimeMillis();
    ambient = new Light(Light.AMBIENT, 0, 0, 0, 0, 0, 0);
    System.out.println("num lights:" + ls.size());
    for(int i=0; i<ls.size(); i++) {
      if(((Light)ls.elementAt(i)).lightType == Light.AMBIENT) {
	ambient.ir += ((Light)ls.elementAt(i)).ir;
	ambient.ig += ((Light)ls.elementAt(i)).ig;
	ambient.ib += ((Light)ls.elementAt(i)).ib;
	ls.removeElementAt(i);
	i--;
      }
    }
    System.out.println("num lights:" + ls.size());
    System.out.println("transforming");
    TransformObject();
    System.out.println("drawing");
    DrawObject();
    time = System.currentTimeMillis() - time;
    screen2 = raster.toImage();
  }

  void TransformObject() {
    world.transform(mList, wList, 0, mList.length);
    Vertex3D eye, norm, edge1, edge2, light;
    Matrix3D cross = new Matrix3D();
    float l;
    // do culling
    //  1. get Vector from viewpoint to poly
    for (int i = 0; i < pList.length; i++) {
      eye = new Vertex3D(eyex - wList[pList[i].v2[1]].x,
			 eyey - wList[pList[i].v2[1]].y,
			 eyez - wList[pList[i].v2[1]].z, 0);
      //  2. create (or transform?) normal of poly
      edge1 = new Vertex3D(wList[pList[i].v2[1]].x-wList[pList[i].v2[0]].x,
			   wList[pList[i].v2[1]].y-wList[pList[i].v2[0]].y,
			   wList[pList[i].v2[1]].z-wList[pList[i].v2[0]].z, 0);
      edge2 = new Vertex3D(wList[pList[i].v2[2]].x-wList[pList[i].v2[0]].x,
			   wList[pList[i].v2[2]].y-wList[pList[i].v2[0]].y,
			   wList[pList[i].v2[2]].z-wList[pList[i].v2[0]].z, 0);
      //eye.normalize();
      edge1.normalize();
      edge2.normalize();
      cross.loadIdentity();
      cross.skew(edge1.x, edge1.y, edge1.z);
      norm = cross.compose(edge2);
      norm.normalize();
    //  3. dot with normal
      if(eye.dot(norm) > 0) {
    //  4. play with vis and refcount
	pList[i].vis = false;
	wList[pList[i].v2[0]].refcount--;
	wList[pList[i].v2[1]].refcount--;
	wList[pList[i].v2[2]].refcount--;
      } else {
	pList[i].vis = true;
	pList[i].normal = norm;
    // do lighting
    //  0. get ambient (above)
/*	l=0;
    //  1. loop over lights
	for(int j=0; j<ls.size(); j++) {
	  if(((Light)ls.elementAt(j)).lightType == Light.DIRECTIONAL) {
    //  2. compute light
	    light = new Vertex3D(((Light)ls.elementAt(j)).x,// - wList[pList[i].v2[1]].x,
				 ((Light)ls.elementAt(j)).y,// - wList[pList[i].v2[1]].y,
				 ((Light)ls.elementAt(j)).z,// - wList[pList[i].v2[1]].z,
				 0);
	    l += -light.dot(norm);// / 10f;
	  }
	}    
    //  3. change color of vertex
	// do this in TriPoly
	pList[i].l = l;
*/
      }
    }
    System.out.println("view trans");
    view.transform(wList, vList, 0, wList.length);
    screen.transform(vList, s2List, 0, vList.length);
    // do clipping
    //  1. eh?
    ps = new Vector();
    vs = new Vector();
    tri2 = 0;
    Vertex3D tmpv;
    float tmpx, tmpy;
    TriPoly tmpp;
    for(int i=0; i < pList.length; i++) {
      if(pList[i].vis) {
	clipPoly(pList[i]);
      }
    }
    for(int i=0; i < ps.size(); i++) {
      if(((TriPoly)ps.elementAt(i)).vis) {
	clipPoly2((TriPoly)ps.elementAt(i));
      }
    }
    // double check vis of new polys
    /*for(int i=0; i < ps.size(); i++) {
      if(((TriPoly)ps.elementAt(i)).vis) {
	edge1 = new Vertex3D(
((Vertex3D)vs.elementAt(((TriPoly)ps.elementAt(i)).v2[1])).x-((Vertex3D)vs.elementAt(((TriPoly)ps.elementAt(i)).v2[0])).x,
((Vertex3D)vs.elementAt(((TriPoly)ps.elementAt(i)).v2[1])).y-((Vertex3D)vs.elementAt(((TriPoly)ps.elementAt(i)).v2[0])).y,
((Vertex3D)vs.elementAt(((TriPoly)ps.elementAt(i)).v2[1])).z-((Vertex3D)vs.elementAt(((TriPoly)ps.elementAt(i)).v2[0])).z, 0);
	edge2 = new Vertex3D(
((Vertex3D)vs.elementAt(((TriPoly)ps.elementAt(i)).v2[2])).x-((Vertex3D)vs.elementAt(((TriPoly)ps.elementAt(i)).v2[0])).x,
((Vertex3D)vs.elementAt(((TriPoly)ps.elementAt(i)).v2[2])).y-((Vertex3D)vs.elementAt(((TriPoly)ps.elementAt(i)).v2[0])).y,
((Vertex3D)vs.elementAt(((TriPoly)ps.elementAt(i)).v2[2])).z-((Vertex3D)vs.elementAt(((TriPoly)ps.elementAt(i)).v2[0])).z, 0);
	//eye.normalize();
	edge1.normalize();
	edge2.normalize();
	cross.loadIdentity();
	cross.skew(edge1.x, edge1.y, edge1.z);
	norm = cross.compose(edge2);
	System.out.println(norm);
      }
    } */

    /*    edge1 = new Vertex3D(s2List[pList[1005].v2[1]].x-s2List[pList[1005].v2[0]].x,
			 s2List[pList[1005].v2[1]].y-s2List[pList[1005].v2[0]].y,
			 s2List[pList[1005].v2[1]].z-s2List[pList[1005].v2[0]].z, 0);
    edge2 = new Vertex3D(s2List[pList[1005].v2[2]].x-s2List[pList[1005].v2[0]].x,
			 s2List[pList[1005].v2[2]].y-s2List[pList[1005].v2[0]].y,
			 s2List[pList[1005].v2[2]].z-s2List[pList[1005].v2[0]].z, 0);
    //eye.normalize();
    edge1.normalize();
    edge2.normalize();
    cross.loadIdentity();
    cross.skew(edge1.x, edge1.y, edge1.z);
    norm = cross.compose(edge2);
    System.out.println(pList[1005].vis);
    System.out.println(norm); */
	/*
      int start=0, next=1, last=2, num=0;
      while(next != 0) {
	if(s2List[pList[i].v2[start]].z < -1f && s2List[pList[i].v2[next]].z < -1f) {
	  // both outside
	  pList[i].vis = false;
	  s2List[pList[i].v2[start]].refcount = -666;
	  s2List[pList[i].v2[next]].refcount = -666;
	  num += 2;
	} else if(s2List[pList[i].v2[start]].z >= -1f && s2List[pList[i].v2[next]].z >= -1f) {
	  // both inside
	  ;
	} else if(s2List[pList[i].v2[start]].z < -1f && s2List[pList[i].v2[next]].z >= -1f) {
	  // start outside
	  //compute tmpx and tmpy
	  tmpv = new Vertex3D(tmpx, tmpy, -1f, 0);
	  tmpVerts.addElement(tmpv);
	  tmpv = new Vertex3D(s2List[pList[i].v2[next]].x, s2List[pList[i].v2[next]].y, s2List[pList[i].v2[next]].z, 0);
	  tmpVerts.addElement(tmpv);
	  tmpv = new Vertex3D(s2List[pList[i].v2[last]].x, s2List[pList[i].v2[last]].y, s2List[pList[i].v2[last]].z, 0);
	  tmpVerts.addElement(tmpv);
	  tmpp = new TriPoly(tmpVerts.size()-2, tmpVerts.size()-1, tmpVerts.size());
	  pList[i].vis = false;
	  s2List[pList[i].v2[start]].refcount = -666;
	  num++;
	} else if(s2List[pList[i].v2[start]].z >= -1f && s2List[pList[i].v2[next]].z < -1f) {
	  // next outside
	  //compute tmpx and tmpy
	  tmpv = new Vertex3D(s2List[pList[i].v2[next]].x, s2List[pList[i].v2[next]].y, s2List[pList[i].v2[next]].z, 0);
	  tmpVerts.addElement(tmpv);
	  tmpv = new Vertex3D(tmpx, tmpy, -1f, 0);
	  tmpVerts.addElement(tmpv);
	  tmpv = new Vertex3D(s2List[pList[i].v2[last]].x, s2List[pList[i].v2[last]].y, s2List[pList[i].v2[last]].z, 0);
	  tmpVerts.addElement(tmpv);
	  tmpp = new TriPoly(tmpVerts.size()-2, tmpVerts.size()-1, tmpVerts.size());
	  pList[i].vis = false;
	  s2List[pList[i].v2[next]].refcount = -666;
	  num++;
	}
	start = next;
	next = last;
	last = (next + 1) % 3;
      }
    }
	*/
      /*      if(s2List[i].z < -1f || s2List[i].z > 1f) {
	s2List[i].refcount = -666;
	System.out.println(s2List[i]);
	} */
    //  2. play with vis and refcount
    project.transform(s2List, sList, 0, s2List.length);
    project.transform(vs);
  }

  void clipPoly(TriPoly p) { // who needs loops??? cut and paste is my friend
    p.vis = false;
    Vertex3D p1, p2, p3;
    int viser = 0;
    if(s2List[p.v2[0]].x < -1f && s2List[p.v2[1]].x < -1f && s2List[p.v2[2]].x < -1f) {
      // all gone
    } else if(s2List[p.v2[0]].x < -1f && s2List[p.v2[1]].x < -1f && s2List[p.v2[2]].x >= -1f) {
      c1(p, 0, -1f);
    } else if(s2List[p.v2[0]].x < -1f && s2List[p.v2[1]].x >= -1f && s2List[p.v2[2]].x < -1f) {
      c2(p, 0, -1f);
    } else if(s2List[p.v2[0]].x < -1f && s2List[p.v2[1]].x >= -1f && s2List[p.v2[2]].x >= -1f) {
      c3(p, 0, -1f);
    } else if(s2List[p.v2[0]].x >= -1f && s2List[p.v2[1]].x < -1f && s2List[p.v2[2]].x < -1f) {
      c4(p, 0, -1f);
    } else if(s2List[p.v2[0]].x >= -1f && s2List[p.v2[1]].x < -1f && s2List[p.v2[2]].x >= -1f) {
      c5(p, 0, -1f);
    } else if(s2List[p.v2[0]].x >= -1f && s2List[p.v2[1]].x >= -1f && s2List[p.v2[2]].x < -1f) {
      c6(p, 0, -1f);
    } else if(s2List[p.v2[0]].x >= -1f && s2List[p.v2[1]].x >= -1f && s2List[p.v2[2]].x >= -1f) {
      // no clip
      viser++;
    }
    if(s2List[p.v2[0]].y < -1f && s2List[p.v2[1]].y < -1f && s2List[p.v2[2]].y < -1f) {
      // all gone
    } else if(s2List[p.v2[0]].y < -1f && s2List[p.v2[1]].y < -1f && s2List[p.v2[2]].y >= -1f) {
      c1(p, 1, -1f);
    } else if(s2List[p.v2[0]].y < -1f && s2List[p.v2[1]].y >= -1f && s2List[p.v2[2]].y < -1f) {
      c2(p, 1, -1f);
    } else if(s2List[p.v2[0]].y < -1f && s2List[p.v2[1]].y >= -1f && s2List[p.v2[2]].y >= -1f) {
      c3(p, 1, -1f);
    } else if(s2List[p.v2[0]].y >= -1f && s2List[p.v2[1]].y < -1f && s2List[p.v2[2]].y < -1f) {
      c4(p, 1, -1f);
    } else if(s2List[p.v2[0]].y >= -1f && s2List[p.v2[1]].y < -1f && s2List[p.v2[2]].y >= -1f) {
      c5(p, 1, -1f);
    } else if(s2List[p.v2[0]].y >= -1f && s2List[p.v2[1]].y >= -1f && s2List[p.v2[2]].y < -1f) {
      c6(p, 1, -1f);
    } else if(s2List[p.v2[0]].y >= -1f && s2List[p.v2[1]].y >= -1f && s2List[p.v2[2]].y >= -1f) {
      // no clip
      viser++;
    }
    if(s2List[p.v2[0]].z < -1f && s2List[p.v2[1]].z < -1f && s2List[p.v2[2]].z < -1f) {
      // all gone
    } else if(s2List[p.v2[0]].z < -1f && s2List[p.v2[1]].z < -1f && s2List[p.v2[2]].z >= -1f) {
      c1(p, 2, -1f);
    } else if(s2List[p.v2[0]].z < -1f && s2List[p.v2[1]].z >= -1f && s2List[p.v2[2]].z < -1f) {
      c2(p, 2, -1f);
    } else if(s2List[p.v2[0]].z < -1f && s2List[p.v2[1]].z >= -1f && s2List[p.v2[2]].z >= -1f) {
      c3(p, 2, -1f);
    } else if(s2List[p.v2[0]].z >= -1f && s2List[p.v2[1]].z < -1f && s2List[p.v2[2]].z < -1f) {
      c4(p, 2, -1f);
    } else if(s2List[p.v2[0]].z >= -1f && s2List[p.v2[1]].z < -1f && s2List[p.v2[2]].z >= -1f) {
      c5(p, 2, -1f);
    } else if(s2List[p.v2[0]].z >= -1f && s2List[p.v2[1]].z >= -1f && s2List[p.v2[2]].z < -1f) {
      c6(p, 2, -1f);
    } else if(s2List[p.v2[0]].z >= -1f && s2List[p.v2[1]].z >= -1f && s2List[p.v2[2]].z >= -1f) {
      // no clip
      viser++;
    }
    // FIXME tweaked equal signs below
    if(s2List[p.v2[0]].x >= 1f && s2List[p.v2[1]].x >= 1f && s2List[p.v2[2]].x >= 1f) {
      // all gone
    } else if(s2List[p.v2[0]].x >= 1f && s2List[p.v2[1]].x >= 1f && s2List[p.v2[2]].x < 1f) {
      c1(p, 0, 1f);
    } else if(s2List[p.v2[0]].x >= 1f && s2List[p.v2[1]].x < 1f && s2List[p.v2[2]].x >= 1f) {
      c2(p, 0, 1f);
    } else if(s2List[p.v2[0]].x >= 1f && s2List[p.v2[1]].x < 1f && s2List[p.v2[2]].x < 1f) {
      c3(p, 0, 1f);
    } else if(s2List[p.v2[0]].x < 1f && s2List[p.v2[1]].x >= 1f && s2List[p.v2[2]].x >= 1f) {
      c4(p, 0, 1f);
    } else if(s2List[p.v2[0]].x < 1f && s2List[p.v2[1]].x >= 1f && s2List[p.v2[2]].x < 1f) {
      c5(p, 0, 1f);
    } else if(s2List[p.v2[0]].x < 1f && s2List[p.v2[1]].x < 1f && s2List[p.v2[2]].x >= 1f) {
      c6(p, 0, 1f);
    } else if(s2List[p.v2[0]].x < 1f && s2List[p.v2[1]].x < 1f && s2List[p.v2[2]].x < 1f) {
      // no clip
      viser++;
    }
    if(s2List[p.v2[0]].y > 1f && s2List[p.v2[1]].y > 1f && s2List[p.v2[2]].y > 1f) {
      // all gone
    } else if(s2List[p.v2[0]].y > 1f && s2List[p.v2[1]].y > 1f && s2List[p.v2[2]].y <= 1f) {
      c1(p, 1, 1f);
    } else if(s2List[p.v2[0]].y > 1f && s2List[p.v2[1]].y <= 1f && s2List[p.v2[2]].y > 1f) {
      c2(p, 1, 1f);
    } else if(s2List[p.v2[0]].y > 1f && s2List[p.v2[1]].y <= 1f && s2List[p.v2[2]].y <= 1f) {
      c3(p, 1, 1f);
    } else if(s2List[p.v2[0]].y <= 1f && s2List[p.v2[1]].y > 1f && s2List[p.v2[2]].y > 1f) {
      c4(p, 1, 1f);
    } else if(s2List[p.v2[0]].y <= 1f && s2List[p.v2[1]].y > 1f && s2List[p.v2[2]].y <= 1f) {
      c5(p, 1, 1f);
    } else if(s2List[p.v2[0]].y <= 1f && s2List[p.v2[1]].y <= 1f && s2List[p.v2[2]].y > 1f) {
      c6(p, 1, 1f);
    } else if(s2List[p.v2[0]].y <= 1f && s2List[p.v2[1]].y <= 1f && s2List[p.v2[2]].y <= 1f) {
      // no clip
      viser++;
    }
    if(s2List[p.v2[0]].z > 1f && s2List[p.v2[1]].z > 1f && s2List[p.v2[2]].z > 1f) {
      // all gone
    } else if(s2List[p.v2[0]].z > 1f && s2List[p.v2[1]].z > 1f && s2List[p.v2[2]].z <= 1f) {
      c1(p, 2, 1f);
    } else if(s2List[p.v2[0]].z > 1f && s2List[p.v2[1]].z <= 1f && s2List[p.v2[2]].z > 1f) {
      c2(p, 2, 1f);
    } else if(s2List[p.v2[0]].z > 1f && s2List[p.v2[1]].z <= 1f && s2List[p.v2[2]].z <= 1f) {
      c3(p, 2, 1f);
    } else if(s2List[p.v2[0]].z <= 1f && s2List[p.v2[1]].z > 1f && s2List[p.v2[2]].z > 1f) {
      c4(p, 2, 1f);
    } else if(s2List[p.v2[0]].z <= 1f && s2List[p.v2[1]].z > 1f && s2List[p.v2[2]].z <= 1f) {
      c5(p, 2, 1f);
    } else if(s2List[p.v2[0]].z <= 1f && s2List[p.v2[1]].z <= 1f && s2List[p.v2[2]].z > 1f) {
      c6(p, 2, 1f);
    } else if(s2List[p.v2[0]].z <= 1f && s2List[p.v2[1]].z <= 1f && s2List[p.v2[2]].z <= 1f) {
      // no clip
      viser++;
    }
    if(viser == 6)
      p.vis = true;
  }

  void clipPoly2(TriPoly p) { // who needs loops??? cut and paste is my friend
    p.vis = false;
    Vertex3D p1, p2, p3;
    int viser = 0;
    if(((Vertex3D)vs.elementAt(p.v2[0])).x < -1f && ((Vertex3D)vs.elementAt(p.v2[1])).x < -1f && ((Vertex3D)vs.elementAt(p.v2[2])).x < -1f) {
      // all gone
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).x < -1f && ((Vertex3D)vs.elementAt(p.v2[1])).x < -1f && ((Vertex3D)vs.elementAt(p.v2[2])).x >= -1f) {
      c1b(p, 0, -1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).x < -1f && ((Vertex3D)vs.elementAt(p.v2[1])).x >= -1f && ((Vertex3D)vs.elementAt(p.v2[2])).x < -1f) {
      c2b(p, 0, -1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).x < -1f && ((Vertex3D)vs.elementAt(p.v2[1])).x >= -1f && ((Vertex3D)vs.elementAt(p.v2[2])).x >= -1f) {
      c3b(p, 0, -1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).x >= -1f && ((Vertex3D)vs.elementAt(p.v2[1])).x < -1f && ((Vertex3D)vs.elementAt(p.v2[2])).x < -1f) {
      c4b(p, 0, -1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).x >= -1f && ((Vertex3D)vs.elementAt(p.v2[1])).x < -1f && ((Vertex3D)vs.elementAt(p.v2[2])).x >= -1f) {
      c5b(p, 0, -1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).x >= -1f && ((Vertex3D)vs.elementAt(p.v2[1])).x >= -1f && ((Vertex3D)vs.elementAt(p.v2[2])).x < -1f) {
      c6b(p, 0, -1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).x >= -1f && ((Vertex3D)vs.elementAt(p.v2[1])).x >= -1f && ((Vertex3D)vs.elementAt(p.v2[2])).x >= -1f) {
      // no clip
      viser++;
    }
    if(((Vertex3D)vs.elementAt(p.v2[0])).y < -1f && ((Vertex3D)vs.elementAt(p.v2[1])).y < -1f && ((Vertex3D)vs.elementAt(p.v2[2])).y < -1f) {
      // all gone
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).y < -1f && ((Vertex3D)vs.elementAt(p.v2[1])).y < -1f && ((Vertex3D)vs.elementAt(p.v2[2])).y >= -1f) {
      c1b(p, 1, -1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).y < -1f && ((Vertex3D)vs.elementAt(p.v2[1])).y >= -1f && ((Vertex3D)vs.elementAt(p.v2[2])).y < -1f) {
      c2b(p, 1, -1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).y < -1f && ((Vertex3D)vs.elementAt(p.v2[1])).y >= -1f && ((Vertex3D)vs.elementAt(p.v2[2])).y >= -1f) {
      c3b(p, 1, -1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).y >= -1f && ((Vertex3D)vs.elementAt(p.v2[1])).y < -1f && ((Vertex3D)vs.elementAt(p.v2[2])).y < -1f) {
      c4b(p, 1, -1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).y >= -1f && ((Vertex3D)vs.elementAt(p.v2[1])).y < -1f && ((Vertex3D)vs.elementAt(p.v2[2])).y >= -1f) {
      c5b(p, 1, -1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).y >= -1f && ((Vertex3D)vs.elementAt(p.v2[1])).y >= -1f && ((Vertex3D)vs.elementAt(p.v2[2])).y < -1f) {
      c6b(p, 1, -1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).y >= -1f && ((Vertex3D)vs.elementAt(p.v2[1])).y >= -1f && ((Vertex3D)vs.elementAt(p.v2[2])).y >= -1f) {
      // no clip
      viser++;
    }
    if(((Vertex3D)vs.elementAt(p.v2[0])).z < -1f && ((Vertex3D)vs.elementAt(p.v2[1])).z < -1f && ((Vertex3D)vs.elementAt(p.v2[2])).z < -1f) {
      // all gone
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).z < -1f && ((Vertex3D)vs.elementAt(p.v2[1])).z < -1f && ((Vertex3D)vs.elementAt(p.v2[2])).z >= -1f) {
      c1b(p, 2, -1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).z < -1f && ((Vertex3D)vs.elementAt(p.v2[1])).z >= -1f && ((Vertex3D)vs.elementAt(p.v2[2])).z < -1f) {
      c2b(p, 2, -1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).z < -1f && ((Vertex3D)vs.elementAt(p.v2[1])).z >= -1f && ((Vertex3D)vs.elementAt(p.v2[2])).z >= -1f) {
      c3b(p, 2, -1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).z >= -1f && ((Vertex3D)vs.elementAt(p.v2[1])).z < -1f && ((Vertex3D)vs.elementAt(p.v2[2])).z < -1f) {
      c4b(p, 2, -1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).z >= -1f && ((Vertex3D)vs.elementAt(p.v2[1])).z < -1f && ((Vertex3D)vs.elementAt(p.v2[2])).z >= -1f) {
      c5b(p, 2, -1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).z >= -1f && ((Vertex3D)vs.elementAt(p.v2[1])).z >= -1f && ((Vertex3D)vs.elementAt(p.v2[2])).z < -1f) {
      c6b(p, 2, -1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).z >= -1f && ((Vertex3D)vs.elementAt(p.v2[1])).z >= -1f && ((Vertex3D)vs.elementAt(p.v2[2])).z >= -1f) {
      // no clip
      viser++;
    }
    if(((Vertex3D)vs.elementAt(p.v2[0])).x > 1f && ((Vertex3D)vs.elementAt(p.v2[1])).x > 1f && ((Vertex3D)vs.elementAt(p.v2[2])).x > 1f) {
      // all gone
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).x > 1f && ((Vertex3D)vs.elementAt(p.v2[1])).x > 1f && ((Vertex3D)vs.elementAt(p.v2[2])).x <= 1f) {
      c1b(p, 0, 1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).x > 1f && ((Vertex3D)vs.elementAt(p.v2[1])).x <= 1f && ((Vertex3D)vs.elementAt(p.v2[2])).x > 1f) {
      c2b(p, 0, 1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).x > 1f && ((Vertex3D)vs.elementAt(p.v2[1])).x <= 1f && ((Vertex3D)vs.elementAt(p.v2[2])).x <= 1f) {
      c3b(p, 0, 1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).x <= 1f && ((Vertex3D)vs.elementAt(p.v2[1])).x > 1f && ((Vertex3D)vs.elementAt(p.v2[2])).x > 1f) {
      c4b(p, 0, 1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).x <= 1f && ((Vertex3D)vs.elementAt(p.v2[1])).x > 1f && ((Vertex3D)vs.elementAt(p.v2[2])).x <= 1f) {
      c5b(p, 0, 1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).x <= 1f && ((Vertex3D)vs.elementAt(p.v2[1])).x <= 1f && ((Vertex3D)vs.elementAt(p.v2[2])).x > 1f) {
      c6b(p, 0, 1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).x <= 1f && ((Vertex3D)vs.elementAt(p.v2[1])).x <= 1f && ((Vertex3D)vs.elementAt(p.v2[2])).x <= 1f) {
      // no clip
      viser++;
    }
    if(((Vertex3D)vs.elementAt(p.v2[0])).y > 1f && ((Vertex3D)vs.elementAt(p.v2[1])).y > 1f && ((Vertex3D)vs.elementAt(p.v2[2])).y > 1f) {
      // all gone
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).y > 1f && ((Vertex3D)vs.elementAt(p.v2[1])).y > 1f && ((Vertex3D)vs.elementAt(p.v2[2])).y <= 1f) {
      c1b(p, 1, 1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).y > 1f && ((Vertex3D)vs.elementAt(p.v2[1])).y <= 1f && ((Vertex3D)vs.elementAt(p.v2[2])).y > 1f) {
      c2b(p, 1, 1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).y > 1f && ((Vertex3D)vs.elementAt(p.v2[1])).y <= 1f && ((Vertex3D)vs.elementAt(p.v2[2])).y <= 1f) {
      c3b(p, 1, 1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).y <= 1f && ((Vertex3D)vs.elementAt(p.v2[1])).y > 1f && ((Vertex3D)vs.elementAt(p.v2[2])).y > 1f) {
      c4b(p, 1, 1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).y <= 1f && ((Vertex3D)vs.elementAt(p.v2[1])).y > 1f && ((Vertex3D)vs.elementAt(p.v2[2])).y <= 1f) {
      c5b(p, 1, 1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).y <= 1f && ((Vertex3D)vs.elementAt(p.v2[1])).y <= 1f && ((Vertex3D)vs.elementAt(p.v2[2])).y > 1f) {
      c6b(p, 1, 1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).y <= 1f && ((Vertex3D)vs.elementAt(p.v2[1])).y <= 1f && ((Vertex3D)vs.elementAt(p.v2[2])).y <= 1f) {
      // no clip
      viser++;
    }
    if(((Vertex3D)vs.elementAt(p.v2[0])).z > 1f && ((Vertex3D)vs.elementAt(p.v2[1])).z > 1f && ((Vertex3D)vs.elementAt(p.v2[2])).z > 1f) {
      // all gone
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).z > 1f && ((Vertex3D)vs.elementAt(p.v2[1])).z > 1f && ((Vertex3D)vs.elementAt(p.v2[2])).z <= 1f) {
      c1b(p, 2, 1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).z > 1f && ((Vertex3D)vs.elementAt(p.v2[1])).z <= 1f && ((Vertex3D)vs.elementAt(p.v2[2])).z > 1f) {
      c2b(p, 2, 1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).z > 1f && ((Vertex3D)vs.elementAt(p.v2[1])).z <= 1f && ((Vertex3D)vs.elementAt(p.v2[2])).z <= 1f) {
      c3b(p, 2, 1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).z <= 1f && ((Vertex3D)vs.elementAt(p.v2[1])).z > 1f && ((Vertex3D)vs.elementAt(p.v2[2])).z > 1f) {
      c4b(p, 2, 1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).z <= 1f && ((Vertex3D)vs.elementAt(p.v2[1])).z > 1f && ((Vertex3D)vs.elementAt(p.v2[2])).z <= 1f) {
      c5b(p, 2, 1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).z <= 1f && ((Vertex3D)vs.elementAt(p.v2[1])).z <= 1f && ((Vertex3D)vs.elementAt(p.v2[2])).z > 1f) {
      c6b(p, 2, 1f);
    } else if(((Vertex3D)vs.elementAt(p.v2[0])).z <= 1f && ((Vertex3D)vs.elementAt(p.v2[1])).z <= 1f && ((Vertex3D)vs.elementAt(p.v2[2])).z <= 1f) {
      // no clip
      viser++;
    }
    if(viser == 6)
      p.vis = true;
    if(!p.vis)
      System.out.println("Double clipped");
  }

  void addPoly(Vertex3D p1, Vertex3D p2, Vertex3D p3, TriPoly p) {
    p1.refcount++;
    p2.refcount++;
    p3.refcount++;
    vs.addElement(p1);
    vs.addElement(p2);
    vs.addElement(p3);
    TriPoly foo = new TriPoly(vs.size()-3, vs.size()-2, vs.size()-1, p.surf);
    foo.l = p.l;
    foo.normal = p.normal;
    ps.addElement(foo);
  }

  Vertex3D inter(int axis, float plane, Vertex3D p1, Vertex3D p2) {
    float x, y, z;
    float dx, dy, dz;
    dx = p1.x - p2.x;
    dy = p1.y - p2.y;
    dz = p1.z - p2.z;
    if(axis == 0) {
      x = plane;
      y = p1.y + (dy/dx)*(plane - p1.x);
      z = p1.z + (dz/dx)*(plane - p1.x);
    } else if(axis == 1) {
      x = p1.x + (dx/dy)*(plane - p1.y);
      y = plane;
      z = p1.z + (dz/dy)*(plane - p1.y);
    } else {
      x = p1.x + (dx/dz)*(plane - p1.z);
      y = p1.y + (dy/dz)*(plane - p1.z);
      z = plane;
    }
    /*if(((p1.y < p2.y) && (y < p1.y || y > p2.y)) || 
       ((p1.y > p2.y) && (y > p1.y || y < p2.y)) || 
       ((p1.z < p2.z) && (z < p1.z || z > p2.z)) || 
       ((p1.z > p2.z) && (z > p1.z || z < p2.z))) {
      System.out.println("start: " + p1);
      System.out.println(" mid: " + x + ", " + y + ", " + z);
      System.out.println(" stop: " + p2);
      } */
    return new Vertex3D(x, y, z, 0);
  }

  void c1(TriPoly p, int axis, float plane) {
    Vertex3D p1, p2, p3;
    // 2 outside
    // 0, 1 moved
    p1 = inter(axis, plane, s2List[p.v2[0]], s2List[p.v2[2]]);
    p2 = inter(axis, plane, s2List[p.v2[1]], s2List[p.v2[2]]);
    p3 = new Vertex3D(s2List[p.v2[2]].x, s2List[p.v2[2]].y, s2List[p.v2[2]].z, 0);
    addPoly(p1, p2, p3, p);
    tri2++;
  }

  void c2(TriPoly p, int axis, float plane) {
    Vertex3D p1, p2, p3;
    // 2 outside
    // 0, 2 moved
    p1 = inter(axis, plane, s2List[p.v2[0]], s2List[p.v2[1]]);
    p2 = new Vertex3D(s2List[p.v2[1]].x, s2List[p.v2[1]].y, s2List[p.v2[1]].z, 0);
    p3 = inter(axis, plane, s2List[p.v2[2]], s2List[p.v2[1]]);
    addPoly(p1, p2, p3, p);
    tri2++;
  }

  void c3(TriPoly p, int axis, float plane) {
    Vertex3D p1, p2, p3;
    // 1 outside
    // 0 moved
    p1 = inter(axis, plane, s2List[p.v2[0]], s2List[p.v2[1]]);
    p2 = new Vertex3D(s2List[p.v2[1]].x, s2List[p.v2[1]].y, s2List[p.v2[1]].z, 0);
    p3 = new Vertex3D(s2List[p.v2[2]].x, s2List[p.v2[2]].y, s2List[p.v2[2]].z, 0);
    addPoly(p1, p2, p3, p);
    tri2++;
    p1 = inter(axis, plane, s2List[p.v2[0]], s2List[p.v2[1]]);
    p2 = new Vertex3D(s2List[p.v2[2]].x, s2List[p.v2[2]].y, s2List[p.v2[2]].z, 0);
    p3 = inter(axis, plane, s2List[p.v2[0]], s2List[p.v2[2]]);
    addPoly(p1, p2, p3, p);
    tri2++;
  }

  void c4(TriPoly p, int axis, float plane) {
    Vertex3D p1, p2, p3;
    // 2 outside
    // 1, 2 moved
    p1 = new Vertex3D(s2List[p.v2[0]].x, s2List[p.v2[0]].y, s2List[p.v2[0]].z, 0);
    p2 = inter(axis, plane, s2List[p.v2[1]], s2List[p.v2[0]]);
    p3 = inter(axis, plane, s2List[p.v2[2]], s2List[p.v2[0]]);
    addPoly(p1, p2, p3, p);
    tri2++;
  }

  void c5(TriPoly p, int axis, float plane) {
    Vertex3D p1, p2, p3;
    // 1 outside
    // 1 moved
    p1 = inter(axis, plane, s2List[p.v2[1]], s2List[p.v2[2]]);
    p2 = new Vertex3D(s2List[p.v2[2]].x, s2List[p.v2[2]].y, s2List[p.v2[2]].z, 0);
    p3 = new Vertex3D(s2List[p.v2[0]].x, s2List[p.v2[0]].y, s2List[p.v2[0]].z, 0);
    addPoly(p1, p2, p3, p);
    tri2++;
    p1 = inter(axis, plane, s2List[p.v2[1]], s2List[p.v2[2]]);
    p2 = new Vertex3D(s2List[p.v2[0]].x, s2List[p.v2[0]].y, s2List[p.v2[0]].z, 0);
    p3 = inter(axis, plane, s2List[p.v2[1]], s2List[p.v2[0]]);
    addPoly(p1, p2, p3, p);
    tri2++;
  }

  void c6(TriPoly p, int axis, float plane) {
    Vertex3D p1, p2, p3;
    // 1 outside
    // 2 moved
    p1 = inter(axis, plane, s2List[p.v2[2]], s2List[p.v2[0]]);
    p2 = new Vertex3D(s2List[p.v2[0]].x, s2List[p.v2[0]].y, s2List[p.v2[0]].z, 0);
    p3 = new Vertex3D(s2List[p.v2[1]].x, s2List[p.v2[1]].y, s2List[p.v2[1]].z, 0);
    addPoly(p1, p2, p3, p);
    tri2++;
    p1 = inter(axis, plane, s2List[p.v2[2]], s2List[p.v2[0]]);
    p2 = new Vertex3D(s2List[p.v2[1]].x, s2List[p.v2[1]].y, s2List[p.v2[1]].z, 0);
    p3 = inter(axis, plane, s2List[p.v2[2]], s2List[p.v2[1]]);
    addPoly(p1, p2, p3, p);
    tri2++;
  }

  void c1b(TriPoly p, int axis, float plane) {
    Vertex3D p1, p2, p3;
    // 2 outside
    // 0, 1 moved
    p1 = inter(axis, plane, ((Vertex3D)vs.elementAt(p.v2[0])), ((Vertex3D)vs.elementAt(p.v2[2])));
    p2 = inter(axis, plane, ((Vertex3D)vs.elementAt(p.v2[1])), ((Vertex3D)vs.elementAt(p.v2[2])));
    p3 = new Vertex3D(((Vertex3D)vs.elementAt(p.v2[2])).x, ((Vertex3D)vs.elementAt(p.v2[2])).y, ((Vertex3D)vs.elementAt(p.v2[2])).z, 0);
    addPoly(p1, p2, p3, p);
    tri2++;
  }

  void c2b(TriPoly p, int axis, float plane) {
    Vertex3D p1, p2, p3;
    // 2 outside
    // 0, 2 moved
    p1 = inter(axis, plane, ((Vertex3D)vs.elementAt(p.v2[0])), ((Vertex3D)vs.elementAt(p.v2[1])));
    p2 = new Vertex3D(((Vertex3D)vs.elementAt(p.v2[1])).x, ((Vertex3D)vs.elementAt(p.v2[1])).y, ((Vertex3D)vs.elementAt(p.v2[1])).z, 0);
    p3 = inter(axis, plane, ((Vertex3D)vs.elementAt(p.v2[2])), ((Vertex3D)vs.elementAt(p.v2[1])));
    addPoly(p1, p2, p3, p);
    tri2++;
  }

  void c3b(TriPoly p, int axis, float plane) {
    Vertex3D p1, p2, p3;
    // 1 outside
    // 0 moved
    p1 = inter(axis, plane, ((Vertex3D)vs.elementAt(p.v2[0])), ((Vertex3D)vs.elementAt(p.v2[1])));
    p2 = new Vertex3D(((Vertex3D)vs.elementAt(p.v2[1])).x, ((Vertex3D)vs.elementAt(p.v2[1])).y, ((Vertex3D)vs.elementAt(p.v2[1])).z, 0);
    p3 = new Vertex3D(((Vertex3D)vs.elementAt(p.v2[2])).x, ((Vertex3D)vs.elementAt(p.v2[2])).y, ((Vertex3D)vs.elementAt(p.v2[2])).z, 0);
    addPoly(p1, p2, p3, p);
    tri2++;
    p1 = inter(axis, plane, ((Vertex3D)vs.elementAt(p.v2[0])), ((Vertex3D)vs.elementAt(p.v2[1])));
    p2 = new Vertex3D(((Vertex3D)vs.elementAt(p.v2[2])).x, ((Vertex3D)vs.elementAt(p.v2[2])).y, ((Vertex3D)vs.elementAt(p.v2[2])).z, 0);
    p3 = inter(axis, plane, ((Vertex3D)vs.elementAt(p.v2[0])), ((Vertex3D)vs.elementAt(p.v2[2])));
    addPoly(p1, p2, p3, p);
    tri2++;
  }

  void c4b(TriPoly p, int axis, float plane) {
    Vertex3D p1, p2, p3;
    // 2 outside
    // 1, 2 moved
    p1 = new Vertex3D(((Vertex3D)vs.elementAt(p.v2[0])).x, ((Vertex3D)vs.elementAt(p.v2[0])).y, ((Vertex3D)vs.elementAt(p.v2[0])).z, 0);
    p2 = inter(axis, plane, ((Vertex3D)vs.elementAt(p.v2[1])), ((Vertex3D)vs.elementAt(p.v2[0])));
    p3 = inter(axis, plane, ((Vertex3D)vs.elementAt(p.v2[2])), ((Vertex3D)vs.elementAt(p.v2[0])));
    addPoly(p1, p2, p3, p);
    tri2++;
  }

  void c5b(TriPoly p, int axis, float plane) {
    Vertex3D p1, p2, p3;
    // 1 outside
    // 1 moved
    p1 = inter(axis, plane, ((Vertex3D)vs.elementAt(p.v2[1])), ((Vertex3D)vs.elementAt(p.v2[2])));
    p2 = new Vertex3D(((Vertex3D)vs.elementAt(p.v2[2])).x, ((Vertex3D)vs.elementAt(p.v2[2])).y, ((Vertex3D)vs.elementAt(p.v2[2])).z, 0);
    p3 = new Vertex3D(((Vertex3D)vs.elementAt(p.v2[0])).x, ((Vertex3D)vs.elementAt(p.v2[0])).y, ((Vertex3D)vs.elementAt(p.v2[0])).z, 0);
    addPoly(p1, p2, p3, p);
    tri2++;
    p1 = inter(axis, plane, ((Vertex3D)vs.elementAt(p.v2[1])), ((Vertex3D)vs.elementAt(p.v2[2])));
    p2 = new Vertex3D(((Vertex3D)vs.elementAt(p.v2[0])).x, ((Vertex3D)vs.elementAt(p.v2[0])).y, ((Vertex3D)vs.elementAt(p.v2[0])).z, 0);
    p3 = inter(axis, plane, ((Vertex3D)vs.elementAt(p.v2[1])), ((Vertex3D)vs.elementAt(p.v2[0])));
    addPoly(p1, p2, p3, p);
    tri2++;
  }

  void c6b(TriPoly p, int axis, float plane) {
    Vertex3D p1, p2, p3;
    // 1 outside
    // 2 moved
    p1 = inter(axis, plane, ((Vertex3D)vs.elementAt(p.v2[2])), ((Vertex3D)vs.elementAt(p.v2[0])));
    p2 = new Vertex3D(((Vertex3D)vs.elementAt(p.v2[0])).x, ((Vertex3D)vs.elementAt(p.v2[0])).y, ((Vertex3D)vs.elementAt(p.v2[0])).z, 0);
    p3 = new Vertex3D(((Vertex3D)vs.elementAt(p.v2[1])).x, ((Vertex3D)vs.elementAt(p.v2[1])).y, ((Vertex3D)vs.elementAt(p.v2[1])).z, 0);
    addPoly(p1, p2, p3, p);
    tri2++;
    p1 = inter(axis, plane, ((Vertex3D)vs.elementAt(p.v2[2])), ((Vertex3D)vs.elementAt(p.v2[0])));
    p2 = new Vertex3D(((Vertex3D)vs.elementAt(p.v2[1])).x, ((Vertex3D)vs.elementAt(p.v2[1])).y, ((Vertex3D)vs.elementAt(p.v2[1])).z, 0);
    p3 = inter(axis, plane, ((Vertex3D)vs.elementAt(p.v2[2])), ((Vertex3D)vs.elementAt(p.v2[1])));
    addPoly(p1, p2, p3, p);
    tri2++;
  }

  void DrawObject()
    {
      int ix, iy;
      int w, h;
      w = raster.width;
      h = raster.height;
      raster.fill(background);
      vis = 0;
      vis2 = 0;
      for (int i = 0; i < pList.length; i++)
	if(pList[i].vis /* && s2List[pList[i].v2[0]].refcount > 0 && s2List[pList[i].v2[1]].refcount > 0 && s2List[pList[i].v2[2]].refcount > 0*/) {
	  pList[i].Draw(raster, sList, surfList, ambient, ls);
	  vis++;
	}
      for(int i=0; i < ps.size(); i++) {
	if(((TriPoly)ps.elementAt(i)).vis) {
	  ((TriPoly)ps.elementAt(i)).Draw(raster, vs, surfList, ambient, ls);
	  vis2++;
	}
      }
    }

  // parse a floating point number
  /*  private float getNumber(StreamTokenizer st) throws IOException {
    st.nextToken();
    return (Float.valueOf(st.sval).floatValue());
  }
  */
  private double getNumber(StreamTokenizer st) throws IOException
    {
      if (st.nextToken() != StreamTokenizer.TT_NUMBER) {
	System.err.println("ERROR: line "+st.lineno()+": expected number");
	throw new IOException(st.toString());
      }
      return st.nval;
    }

  // parse a packed integer in hex, octal, or decimal
  private int getColor(StreamTokenizer st) throws IOException {
    st.nextToken();
    int pix;
    if (st.sval.startsWith("0x") || st.sval.startsWith("0X")) {
      pix = (int) Long.parseLong(st.sval.substring(2), 16);
    } else
      if (st.sval.startsWith("0") || st.sval.length() > 1) {
	pix = (int) Long.parseLong(st.sval.substring(1), 8);
      } else
	pix = Integer.parseInt(st.sval);
    return pix;
  }

  // Scan the triangle file
  public void ReadInput(InputStream is) throws IOException {
    int foo;
    tri = 0;

    StreamTokenizer st = new StreamTokenizer(is);
    st.commentChar('#');
    while ((foo = st.nextToken()) != StreamTokenizer.TT_EOF) {
      if(foo == StreamTokenizer.TT_WORD) {
	if(st.sval.equals("v")) {
	  Vertex3D v1, v2, v3;
	  float x, y, z;

	  x = (float)getNumber(st);
	  y = (float)getNumber(st);
	  z = (float)getNumber(st);
	  v1 = new Vertex3D(x, y, z, 0);
	  v.addElement(v1);

	} else if(st.sval.equals("f")) {
	  int v0 = (int) getNumber(st);
	  int v1 = (int) getNumber(st);
	  while (st.nextToken() == StreamTokenizer.TT_NUMBER) {
	    st.pushBack();
	    int v2 = (int) getNumber(st);
	    if (v2 == v0) continue;
	    if (s.size() == 0) {
	      s.addElement(new Surface(0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 5.0f));
	    }
	    p.addElement(new TriPoly(v0, v2, v1, s.size()-1));
	    ((Vertex3D)v.elementAt(v0)).refcount++;
	    ((Vertex3D)v.elementAt(v1)).refcount++;
	    ((Vertex3D)v.elementAt(v2)).refcount++;
	    tri++;
	    v1 = v2;
	  }
	  st.pushBack();
	} else if(st.sval.equals("eye")) {
	  eyex = (float) getNumber(st);
	  eyey = (float) getNumber(st);
	  eyez = (float) getNumber(st);
	} else if (st.sval.equals("look")) {
	  lookatx = (float) getNumber(st);
	  lookaty = (float) getNumber(st);
	  lookatz = (float) getNumber(st);
	} else if (st.sval.equals("up")) {
	  upx = (float) getNumber(st);
	  upy = (float) getNumber(st);
	  upz = (float) getNumber(st);
	} else if (st.sval.equals("fov")) {
	  fov = (float) getNumber(st);
	} else if(st.sval.equals("la")) {
	  float r = (float) getNumber(st);
	  float g = (float) getNumber(st);
	  float b = (float) getNumber(st);
	  ls.addElement(new Light(Light.AMBIENT, 0, 0, 0, r, g, b));
	} else if(st.sval.equals("ld")) {
	  float r = (float) getNumber(st);
	  float g = (float) getNumber(st);
	  float b = (float) getNumber(st);
	  float x = (float) getNumber(st);
	  float y = (float) getNumber(st);
	  float z = (float) getNumber(st);
	  ls.addElement(new Light(Light.DIRECTIONAL, x, y, z, r, g, b));
	} else if(st.sval.equals("lp")) {
	} else if(st.sval.equals("surf")) {
	  float r = (float) getNumber(st);
	  float g = (float) getNumber(st);
	  float b = (float) getNumber(st);
	  float ka = (float) getNumber(st);
	  float kd = (float) getNumber(st);
	  float ks = (float) getNumber(st);
	  float ns = (float) getNumber(st);
	  s.addElement(new Surface(r, g, b, ka, kd, ks, ns));
	} else {
	  System.err.println("ERROR: line "+st.lineno()+": unexpected token :"+st.sval);
	}
      }
    }
  }

  public void paint(Graphics g) {
    g.drawImage(screen2, 0, 0, this);
    g.setColor(Color.white);
    g.drawString("Time  = "+time+" ms", 10, 10);
    g.drawString("Polys = "+vis+"/"+tri, 10, 20);
    g.drawString("New Polys = "+vis2+"/"+tri2, 10, 30);
  }

  public void update(Graphics g) {
    paint(g);
  }

  float v0x, v0y, v0z;

  public boolean mouseDown(Event e, int x, int y)
    {
      if (e.metaDown()) {
	showStatus("Resetting model matrix");
	world.loadIdentity();
      }
      v0x = (float) (x - (size().width / 2));
      v0y = (float) ((size().height / 2) - y);
      v0z = (float) size().width;
      float l0 = (float) (1 / Math.sqrt(v0x*v0x + v0y*v0y + v0z*v0z));
      v0x *= l0;
      v0y *= l0;
      v0z *= l0;
      return true;
    }

  public boolean mouseDrag(Event e, int x, int y)
    {
      if (e.metaDown()) {
	showStatus("Resetting model matrix");
      } else {
	float v1x = (float) (x - (size().width / 2));
	float v1y = (float) ((size().height / 2) - y);
	float v1z = (float) size().width;
	float l = (float) (1 / Math.sqrt(v1x*v1x + v1y*v1y + v1z*v1z));
	v1x *= l;
	v1y *= l;
	v1z *= l;

	float ax = v0y*v1z - v0z*v1y;
	float ay = v0z*v1x - v0x*v1z;
	float az = v0x*v1y - v0y*v1x;
	l = (float) Math.sqrt(ax*ax + ay*ay + az*az);
	float theta = (float) Math.asin(l);
	if (v0x*v1x + v0y*v1y + v0z*v1z < 0)
	  theta += (float) Math.PI / 2;
	time = System.currentTimeMillis();
	world.rotate(ax, ay, az, theta);
	      
	v0x = v1x;
	v0y = v1y;
	v0z = v1z;
	TransformObject();
        DrawObject();
	time = System.currentTimeMillis() - time;
      }
      screen2 = raster.toImage();
      repaint();
      return true;
    }

}
