import java.applet.Applet; import java.awt.*; import java.io.*; import java.net.URL; import go.*; class GoHelicopter extends GoInterface { // Number of vertices and triangles in the helicopter data file. final int HELICOPTER_VERTEX_NUM = 675; final int HELICOPTER_TRIANGLE_NUM = 1270; // Helicopter data. GoTriangles data = new GoTriangles(HELICOPTER_TRIANGLE_NUM * 3, Go.COLOR | Go.AUTO_NORMAL); // View variables. double eyeX; double eyeY; double eyeZ; double centerX; double centerY; double centerZ; double upX; double upY; double upZ; double radius; boolean perspective; // Rotate vector. double rX; double rY; double rZ; // Class temporary variables. GoMatrix modelview = new GoMatrix(); GoMatrix saveModelview = new GoMatrix(); int xStart, yStart; GoHelicopter(InputStream is) throws IOException, FileFormatException { // // Read in the model data. // readData(is); // // Calculate the bounding box. // go.push(go.RENDER_MODE); go.renderMode(go.BOUND_BOX); go.render(data); go.pop(go.RENDER_MODE); GoBoundBox boundBox = go.boundBox(); double xMin = boundBox.xMin(); double yMin = boundBox.yMin(); double zMin = boundBox.zMin(); double xMax = boundBox.xMax(); double yMax = boundBox.yMax(); double zMax = boundBox.zMax(); // // Enable culling. // go.enable(Go.CULL); // // Setup a light. // go.light(go.LIGHT_0, true); go.light(go.LIGHT_0, go.DIRECTIONAL, -1.3, 2.4, 1.0); // // Define the initial view. // double eyeMult = 100.0; eyeX = 1.3 * eyeMult; eyeY = -2.4 * eyeMult; eyeZ = 2.0 * eyeMult; centerX = (xMin + xMax) / 2.0; centerY = (yMin + yMax) / 2.0; centerZ = (zMin + zMax) / 2.0; upX = 0.0; upY = 0.0; upZ = 1.0; radius = 35.0; perspective = true; go.lookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); } // // Interface render method. // public void render() { go.clear(Go.IMAGE); go.render(data); swap(); } // // Interface resized method. // public void resized(int width, int height) { double dx = eyeX - centerX; double dy = eyeY - centerY; double dz = eyeZ - centerZ; double d = Math.sqrt(dx * dx + dy * dy + dz * dz); double fovy = 2.0 * 180.0/Math.PI * Math.atan(radius / d); double near = d - radius; double far = d + radius; double xUnits, yUnits; if(width >= height) { xUnits = 2 * radius * width / height; yUnits = 2 * radius; } else { xUnits = 2 * radius; yUnits = 2 * radius * height / width; } // setup projection matrix go.matrixMode(Go.PROJECTION); go.identity(); if(perspective) { if(width >= height) { go.perspective(fovy, xUnits / yUnits, near, far); } else { double r = radius * yUnits / xUnits; double fovyModified = Math.atan(r / d) * 180.0 / Math.PI * 2.0; go.perspective(fovyModified, xUnits / yUnits, near, far); } } else { go.ortho(-xUnits / 2.0, xUnits / 2.0, -yUnits / 2.0, yUnits / 2.0, near, far); } go.matrixMode(Go.MODELVIEW); } // // Interface mousePressed method. // public void mousePressed(int x, int y) { // // Init mouse info. // xStart = x; yStart = y; // // Save the current modelview matrix so that // it can be restored as the mouse is moved. // go.getModelview(saveModelview); // // Calc the rotation vector // final double x0 = 0.0; final double y0 = 0.0; final double z0 = 0.0; final double x1 = 0.0; final double y1 = 1.0; final double z1 = 0.0; go.push(go.MODELVIEW); go.inverse(); go.getModelview(modelview); go.pop(go.MODELVIEW); double[] m = modelview.m; double w = m[3] * x0 + m[7] * y0 + m[11] * z0 + m[15]; double x0N = (m[0] * x0 + m[4] * y0 + m[8] * z0 + m[12]) / w; double y0N = (m[1] * x0 + m[5] * y0 + m[9] * z0 + m[13]) / w; double z0N = (m[2] * x0 + m[6] * y0 + m[10] * z0 + m[14]) / w; w = m[3] * x1 + m[7] * y1 + m[11] * z1 + m[15]; double x1N = (m[0] * x1 + m[4] * y1 + m[8] * z1 + m[12]) / w; double y1N = (m[1] * x1 + m[5] * y1 + m[9] * z1 + m[13]) / w; double z1N = (m[2] * x1 + m[6] * y1 + m[10] * z1 + m[14]) / w; rX = x1N - x0N; rY = y1N - y0N; rZ = z1N - z0N; } // // Interface mouseDragged method. // public void mouseDragged(int x, int y) { int xEnd = x; int yEnd = y; go.load(saveModelview); double delta = (xEnd - xStart) / 2.0; go.translate(centerX, centerY, centerZ); go.rotate(delta, rX, rY, rZ); go.translate(-centerX, -centerY, -centerZ); rerender(); } // // Read helicopter data from file. // void readData(InputStream is) throws IOException, FileFormatException { double vert[] = new double[HELICOPTER_VERTEX_NUM * 3]; int nvert = 0; double currentColorR = 0; double currentColorG = 1; double currentColorB = 0; int count = 0; StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(is))); st.eolIsSignificant(true); st.commentChar('#'); scan: while (true) { switch (st.nextToken()) { default: break scan; case StreamTokenizer.TT_EOL: break; case StreamTokenizer.TT_WORD: 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; } } int index = nvert * 3; vert[index] = x; vert[index + 1] = y; vert[index + 2] = z; nvert++; while (st.ttype != StreamTokenizer.TT_EOL && st.ttype != StreamTokenizer.TT_EOF) { st.nextToken(); } } else if ("f".equals(st.sval) || "fo".equals(st.sval) || "l".equals(st.sval)) { int start = -1; int prev = -1; int n = -1; while (true) { if (st.nextToken() == StreamTokenizer.TT_NUMBER) { n = (int) st.nval; if (prev >= 0) { if (prev != start) { data.xyz(count, vert[(start-1)*3], vert[(start-1)*3+1], vert[(start-1)*3+2]); data.rgb(count, currentColorR, currentColorG, currentColorB); data.xyz(count+1, vert[(prev-1)*3], vert[(prev-1)*3+1], vert[(prev-1)*3+2]); data.rgb(count+1, currentColorR, currentColorG, currentColorB); data.xyz(count+2, vert[(n-1)*3], vert[(n-1)*3+1], vert[(n-1)*3+2]); data.rgb(count+2, currentColorR, currentColorG, currentColorB); count += 3; } } if (start < 0) { start = n; } prev = n; } else if (st.ttype == '/') { st.nextToken(); } else { break; } } if (st.ttype != StreamTokenizer.TT_EOL) { break scan; } } else if ("g".equals(st.sval)) { st.nextToken(); if("TURBINE".equals(st.sval)) { currentColorR = 160.0/256.0; currentColorG = 160.0/256.0; currentColorB = 160.0/256.0; } else if("ROTOR".equals(st.sval)) { currentColorR = 159.0/256.0; currentColorG = 128.0/256.0; currentColorB = 27.0/256.0; } else if("FEET".equals(st.sval)) { currentColorR = 128.0/256.0; currentColorG = 128.0/256.0; currentColorB = 128.0/256.0; } else if("LEGS".equals(st.sval)) { currentColorR = 128.0/256.0; currentColorG = 128.0/256.0; currentColorB = 128.0/256.0; } else if("TAIL".equals(st.sval)) { currentColorR = 63.0/256.0; currentColorG = 155.0/256.0; currentColorB = 236.0/256.0; } else if("BODY".equals(st.sval)) { currentColorR = 47.0/256.0; currentColorG = 117.0/256.0; currentColorB = 178.0/256.0; } else if("TUBE".equals(st.sval)) { currentColorR = 63.0/256.0; currentColorG = 155.0/256.0; currentColorB = 236.0/256.0; } } else { while (st.nextToken() != StreamTokenizer.TT_EOL && st.ttype != StreamTokenizer.TT_EOF); } } } is.close(); if (st.ttype != StreamTokenizer.TT_EOF) throw new FileFormatException(st.toString()); } } class FileFormatException extends Exception { public FileFormatException(String s) { super(s); } } public class helicopter extends Applet { GoHelicopter helicopter; public void init() { InputStream is = null; try { is = new URL(getDocumentBase(), "helicopter.obj").openStream(); helicopter = new GoHelicopter(is); } catch(Exception e) { } setLayout(new GridLayout(1, 1)); add("Center", helicopter); } }