/* * Copyright (c) 1996 CONNIE K. PENG All Rights Reserved. * * Permission to use, copy, modify, and distribute this software * and its documentation for NON-COMMERCIAL purposes. * */ import java.io.InputStream; import java.io.StreamTokenizer; import java.io.IOException; import java.net.URL; public class Object3D { Line lines[]; // lines of this object Vertex points[]; // points of this object Face faces[]; // faces of this object Vector center; // center point of this object int maxPoints; // max number of points int maxLines; // max number of lines int maxFaces; int numLines; // number of lines in this object int numPoints; // number of points in this object int numFaces; // initialize the object public Object3D() { maxPoints = 50; maxLines = 50; maxFaces = 50; numPoints = 0; numLines = 0; numFaces = 0; lines = new Line[maxLines]; points = new Vertex[maxPoints]; faces = new Face[maxFaces]; center = new Vector(); } // copy constuctor public Object3D(Object3D object) { numLines = object.numLines; numPoints = object.numPoints; numFaces = object.numFaces; maxLines = object.maxLines; maxPoints = object.maxPoints; maxFaces = object.maxFaces; lines = new Line[maxLines]; points = new Vertex[maxPoints]; faces = new Face[maxFaces]; for (int i = 0; i < numPoints; i++) points[i] = new Vertex(object.points[i]); for (int i = 0; i < numLines; i++) lines[i] = new Line(object.lines[i]); for (int i = 0; i < numFaces; i++) faces[i] = new Face(object.faces[i]); center = new Vector(object.center); } // read the object from input file public Object3D(InputStream is) throws IOException, FileFormatException { this(); StreamTokenizer st = new StreamTokenizer(is); st.eolIsSignificant(true); st.commentChar('#'); scan: while (true) { switch (st.nextToken ()) { default: break scan; case StreamTokenizer.TT_EOL: break; case StreamTokenizer.TT_EOF: break scan; case StreamTokenizer.TT_WORD: // read point's coordinator if ("v".equals(st.sval)) { double x = 0, y = 0, z = 0; if (st.nextToken() == StreamTokenizer.TT_NUMBER) { x = st.nval; if (st.nextToken() == StreamTokenizer.TT_NUMBER) { y = st.nval; if (st.nextToken() == StreamTokenizer.TT_NUMBER) z = st.nval; else throw new FileFormatException(st.toString()); } else throw new FileFormatException(st.toString()); } else throw new FileFormatException(st.toString()); // and this point into object addVertex((float)x, (float)y, (float)z); while (st.ttype != StreamTokenizer.TT_EOL && st.ttype != StreamTokenizer.TT_EOF) st.nextToken(); break; } // read the center point's coordinator else if ("c".equals(st.sval)) { double x = 0, y = 0, z = 0; if (st.nextToken() == StreamTokenizer.TT_NUMBER) { x = st.nval; if (st.nextToken() == StreamTokenizer.TT_NUMBER) { y = st.nval; if (st.nextToken() == StreamTokenizer.TT_NUMBER) z = st.nval; else throw new FileFormatException(st.toString()); } else throw new FileFormatException(st.toString()); } else throw new FileFormatException(st.toString()); center.set_element((float)x, (float)y, (float)z); while (st.ttype != StreamTokenizer.TT_EOL && st.ttype != StreamTokenizer.TT_EOF) st.nextToken(); break; } // read line's starting and ending point index else if ("l".equals(st.sval)) { int s, e; if (st.nextToken() == StreamTokenizer.TT_NUMBER) { s = (int) st.nval; if (st.nextToken() == StreamTokenizer.TT_NUMBER) e = (int) st.nval; else throw new FileFormatException(st.toString()); } else throw new FileFormatException(st.toString()); // add this line into object addLine(s, e); while (st.ttype != StreamTokenizer.TT_EOL && st.ttype != StreamTokenizer.TT_EOF) st.nextToken(); break; } // read faces of object else if ("f".equals(st.sval)) { int r, g, b; int num, lindex; // get the color of the face if (st.nextToken() == StreamTokenizer.TT_NUMBER) r = (int) st.nval; else throw new FileFormatException(st.toString()); if (st.nextToken() == StreamTokenizer.TT_NUMBER) g = (int) st.nval; else throw new FileFormatException(st.toString()); if (st.nextToken() == StreamTokenizer.TT_NUMBER) b = (int) st.nval; else throw new FileFormatException(st.toString()); // get the total number of lines of this face if (st.nextToken() == StreamTokenizer.TT_NUMBER) { num = (int) st.nval; if (num < 3) throw new FileFormatException("Number of lines in each face has to be greater than 2"); } else throw new FileFormatException(st.toString()); addFace(r, g, b, num); // add each line of the face for (int i = 0; i < num; i++) { if (st.nextToken() == StreamTokenizer.TT_NUMBER) { lindex = (int) st.nval; faces[numFaces-1].addLine(lindex); } else throw new FileFormatException(st.toString()); } while (st.ttype != StreamTokenizer.TT_EOL && st.ttype != StreamTokenizer.TT_EOF) st.nextToken(); break; } else throw new FileFormatException(st.toString()); } } is.close(); } // add on point public void addVertex(float x, float y, float z) { if (numPoints >= maxPoints) { // double the capacity of the points array maxPoints *= 2; Vertex[] v = new Vertex[maxPoints]; for (int i = 0; i < points.length; i++) v[i] = new Vertex(points[i]); points = v; } points[numPoints] = new Vertex(x, y, z); numPoints++; } // add one line public void addLine(int s, int e) { if (numLines >= maxLines) { // double the capacity of lines array maxLines *= 2; Line l[] = new Line[maxLines]; for (int i = 0; i < lines.length; i++) l[i] = new Line(lines[i]); lines = l; } lines[numLines] = new Line(s, e); numLines++; } // add one face public void addFace(int r, int g, int b, int num) { if (numLines >= maxLines) { // double the capacity of Face array maxFaces *= 2; Face f[] = new Face[maxFaces]; for (int i = 0; i < faces.length; i++) f[i] = new Face(faces[i]); faces = f; } faces[numFaces] = new Face(r, g, b, num); numFaces++; } // doing transformation for each point of object according to the matrix public void Transform3D(Matrix m) { for (int i = 0; i < numPoints; i++) points[i].multiply(m); center.multiply(m); } // check each face if it is behind the view public void CheckBehindFace() { int l1, l2; int v1, v2, v3, v4; Vector u = new Vector(); Vector v = new Vector(); Vector uxv; Vector viewDir = new Vector(); for (int i = 0; i < numFaces; i++) { l1 = faces[i].line[0]; l2 = faces[i].line[1]; // v1----------v2 v4 // l1 | // | l2 // | // v3 if (l1 < 0) { // if line number is negative, switch it's start and end points v1 = lines[Math.abs(l1)-1].end; v2 = lines[Math.abs(l1)-1].start; } else { v1 = lines[l1-1].start; v2 = lines[l1-1].end; } if (l2 < 0) { // if line number is negative, switch it's start and end points v3 = lines[Math.abs(l2)-1].start; v4 = lines[Math.abs(l2)-1].end; } else { v3 = lines[l2-1].end; v4 = lines[l2-1].start; } // get the normal vector of the face u.set_element(points[v1].element[0], points[v1].element[1], points[v1].element[2]); u.sub((Vector)points[v2]); v.set_element(points[v3].element[0], points[v3].element[1], points[v3].element[2]); v.sub((Vector)points[v4]); uxv = u.cross_product(v); viewDir.set_element(points[v2].element[0], points[v2].element[1], points[v2].element[2]); viewDir.make_vector(); // check if the normal vector of face points to the same direction of // the viewing direction if (uxv.dot_product(viewDir) <= 0) faces[i].behind = true; else faces[i].behind = false; } } // doing canonical view clipping for all the lines of the object public void Clipping3D(ViewFrame vf) { byte retval; for (int i = 0; i < numLines; i++) { int s = lines[i].start; int e = lines[i].end; Vector start = new Vector((Vector)points[s]); Vector end = new Vector((Vector)points[e]); retval = vf.Clipping(start, end); // if this line is totally outside of the viewing volumn if ((retval & 0x04) != 0) { lines[i].OutOfRange = true; points[s].OutOfRange = true; points[e].OutOfRange = true; } else { // if starting point is clipped out if ((retval & 0x02) != 0) { points[s].OutOfRange = true; // add the new point, and replace the line's start point to it addVertex(start.element[0], start.element[1], start.element[2]); lines[i].start = numPoints - 1; } // if ending point is clipped out if ((retval & 0x01) != 0) { points[e].OutOfRange = true; // add the new point, and replace the line's end point to it addVertex(end.element[0], end.element[1], end.element[2]); lines[i].end = numPoints - 1; } } } } // doing perspective transformation for each point according to the viewing // distance public void Perspective3D(int distance) { for (int i = 0; i < numPoints; i++) if (points[i].OutOfRange == false) points[i].perspective(distance); center.perspective(distance); } // transform the object public Object3D Move_Transform(Matrix m, ViewFrame vf) { Transform3D(m); Object3D object = new Object3D(this); object.Transform3D(vf.W2F_Matrix); object.CheckBehindFace(); object.Clipping3D(vf); object.Perspective3D(100); // viewing distance is always 100 unit of VFZ object.Transform3D(vf.F2S_Matrix); return object; } }