ThreeD.java revision 2362
0N/A/*
2362N/A * Copyright (c) 1995, 2006, Oracle and/or its affiliates. All rights reserved.
0N/A *
0N/A * Redistribution and use in source and binary forms, with or without
0N/A * modification, are permitted provided that the following conditions
0N/A * are met:
0N/A *
0N/A * - Redistributions of source code must retain the above copyright
0N/A * notice, this list of conditions and the following disclaimer.
0N/A *
0N/A * - Redistributions in binary form must reproduce the above copyright
0N/A * notice, this list of conditions and the following disclaimer in the
0N/A * documentation and/or other materials provided with the distribution.
0N/A *
2362N/A * - Neither the name of Oracle nor the names of its
0N/A * contributors may be used to endorse or promote products derived
0N/A * from this software without specific prior written permission.
0N/A *
0N/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
0N/A * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
0N/A * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
0N/A * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
0N/A * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0N/A * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0N/A * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
0N/A * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
0N/A * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
0N/A * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0N/A * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0N/A */
0N/A
0N/A/*
0N/A */
0N/A
0N/A/* A set of classes to parse, represent and display 3D wireframe models
0N/A represented in Wavefront .obj format. */
0N/A
0N/Aimport java.applet.Applet;
0N/Aimport java.awt.Graphics;
0N/Aimport java.awt.Color;
0N/Aimport java.awt.Event;
0N/Aimport java.awt.event.*;
0N/Aimport java.io.*;
0N/Aimport java.net.URL;
0N/A
0N/Aclass FileFormatException extends Exception {
0N/A public FileFormatException(String s) {
0N/A super(s);
0N/A }
0N/A}
0N/A
0N/A/** The representation of a 3D model */
0N/Aclass Model3D {
0N/A float vert[];
0N/A int tvert[];
0N/A int nvert, maxvert;
0N/A int con[];
0N/A int ncon, maxcon;
0N/A boolean transformed;
0N/A Matrix3D mat;
0N/A
0N/A float xmin, xmax, ymin, ymax, zmin, zmax;
0N/A
0N/A Model3D () {
0N/A mat = new Matrix3D ();
0N/A mat.xrot(20);
0N/A mat.yrot(30);
0N/A }
0N/A /** Create a 3D model by parsing an input stream */
0N/A Model3D (InputStream is) throws IOException, FileFormatException {
0N/A this();
0N/A StreamTokenizer st = new StreamTokenizer(
0N/A new BufferedReader(new InputStreamReader(is, "UTF-8")));
0N/A st.eolIsSignificant(true);
0N/A st.commentChar('#');
0N/A scan:
0N/A while (true) {
0N/A switch (st.nextToken()) {
0N/A default:
0N/A break scan;
0N/A case StreamTokenizer.TT_EOL:
0N/A break;
0N/A case StreamTokenizer.TT_WORD:
0N/A if ("v".equals(st.sval)) {
0N/A double x = 0, y = 0, z = 0;
0N/A if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
0N/A x = st.nval;
0N/A if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
0N/A y = st.nval;
0N/A if (st.nextToken() == StreamTokenizer.TT_NUMBER)
0N/A z = st.nval;
0N/A }
0N/A }
0N/A addVert((float) x, (float) y, (float) z);
0N/A while (st.ttype != StreamTokenizer.TT_EOL &&
0N/A st.ttype != StreamTokenizer.TT_EOF)
0N/A st.nextToken();
0N/A } else if ("f".equals(st.sval) || "fo".equals(st.sval) || "l".equals(st.sval)) {
0N/A int start = -1;
0N/A int prev = -1;
0N/A int n = -1;
0N/A while (true)
0N/A if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
0N/A n = (int) st.nval;
0N/A if (prev >= 0)
0N/A add(prev - 1, n - 1);
0N/A if (start < 0)
0N/A start = n;
0N/A prev = n;
0N/A } else if (st.ttype == '/')
0N/A st.nextToken();
0N/A else
0N/A break;
0N/A if (start >= 0)
0N/A add(start - 1, prev - 1);
0N/A if (st.ttype != StreamTokenizer.TT_EOL)
0N/A break scan;
0N/A } else {
0N/A while (st.nextToken() != StreamTokenizer.TT_EOL
0N/A && st.ttype != StreamTokenizer.TT_EOF);
0N/A }
0N/A }
0N/A }
0N/A is.close();
0N/A if (st.ttype != StreamTokenizer.TT_EOF)
0N/A throw new FileFormatException(st.toString());
0N/A }
0N/A
0N/A /** Add a vertex to this model */
0N/A int addVert(float x, float y, float z) {
0N/A int i = nvert;
0N/A if (i >= maxvert)
0N/A if (vert == null) {
0N/A maxvert = 100;
0N/A vert = new float[maxvert * 3];
0N/A } else {
0N/A maxvert *= 2;
0N/A float nv[] = new float[maxvert * 3];
0N/A System.arraycopy(vert, 0, nv, 0, vert.length);
0N/A vert = nv;
0N/A }
0N/A i *= 3;
0N/A vert[i] = x;
0N/A vert[i + 1] = y;
0N/A vert[i + 2] = z;
0N/A return nvert++;
0N/A }
0N/A /** Add a line from vertex p1 to vertex p2 */
0N/A void add(int p1, int p2) {
0N/A int i = ncon;
0N/A if (p1 >= nvert || p2 >= nvert)
0N/A return;
0N/A if (i >= maxcon)
0N/A if (con == null) {
0N/A maxcon = 100;
0N/A con = new int[maxcon];
0N/A } else {
0N/A maxcon *= 2;
0N/A int nv[] = new int[maxcon];
0N/A System.arraycopy(con, 0, nv, 0, con.length);
0N/A con = nv;
0N/A }
0N/A if (p1 > p2) {
0N/A int t = p1;
0N/A p1 = p2;
0N/A p2 = t;
0N/A }
0N/A con[i] = (p1 << 16) | p2;
0N/A ncon = i + 1;
0N/A }
0N/A /** Transform all the points in this model */
0N/A void transform() {
0N/A if (transformed || nvert <= 0)
0N/A return;
0N/A if (tvert == null || tvert.length < nvert * 3)
0N/A tvert = new int[nvert*3];
0N/A mat.transform(vert, tvert, nvert);
0N/A transformed = true;
0N/A }
0N/A
0N/A /* Quick Sort implementation
0N/A */
0N/A private void quickSort(int a[], int left, int right)
0N/A {
0N/A int leftIndex = left;
0N/A int rightIndex = right;
0N/A int partionElement;
0N/A if ( right > left)
0N/A {
0N/A
0N/A /* Arbitrarily establishing partition element as the midpoint of
0N/A * the array.
0N/A */
0N/A partionElement = a[ ( left + right ) / 2 ];
0N/A
0N/A // loop through the array until indices cross
0N/A while( leftIndex <= rightIndex )
0N/A {
0N/A /* find the first element that is greater than or equal to
0N/A * the partionElement starting from the leftIndex.
0N/A */
0N/A while( ( leftIndex < right ) && ( a[leftIndex] < partionElement ) )
0N/A ++leftIndex;
0N/A
0N/A /* find an element that is smaller than or equal to
0N/A * the partionElement starting from the rightIndex.
0N/A */
0N/A while( ( rightIndex > left ) &&
0N/A ( a[rightIndex] > partionElement ) )
0N/A --rightIndex;
0N/A
0N/A // if the indexes have not crossed, swap
0N/A if( leftIndex <= rightIndex )
0N/A {
0N/A swap(a, leftIndex, rightIndex);
0N/A ++leftIndex;
0N/A --rightIndex;
0N/A }
0N/A }
0N/A
0N/A /* If the right index has not reached the left side of array
0N/A * must now sort the left partition.
0N/A */
0N/A if( left < rightIndex )
0N/A quickSort( a, left, rightIndex );
0N/A
0N/A /* If the left index has not reached the right side of array
0N/A * must now sort the right partition.
0N/A */
0N/A if( leftIndex < right )
0N/A quickSort( a, leftIndex, right );
0N/A
0N/A }
0N/A }
0N/A
0N/A private void swap(int a[], int i, int j)
0N/A {
0N/A int T;
0N/A T = a[i];
0N/A a[i] = a[j];
0N/A a[j] = T;
0N/A }
0N/A
0N/A
0N/A /** eliminate duplicate lines */
0N/A void compress() {
0N/A int limit = ncon;
0N/A int c[] = con;
0N/A quickSort(con, 0, ncon - 1);
0N/A int d = 0;
0N/A int pp1 = -1;
0N/A for (int i = 0; i < limit; i++) {
0N/A int p1 = c[i];
0N/A if (pp1 != p1) {
0N/A c[d] = p1;
0N/A d++;
0N/A }
0N/A pp1 = p1;
0N/A }
0N/A ncon = d;
0N/A }
0N/A
0N/A static Color gr[];
0N/A
0N/A /** Paint this model to a graphics context. It uses the matrix associated
0N/A with this model to map from model space to screen space.
0N/A The next version of the browser should have double buffering,
0N/A which will make this *much* nicer */
0N/A void paint(Graphics g) {
0N/A if (vert == null || nvert <= 0)
0N/A return;
0N/A transform();
0N/A if (gr == null) {
0N/A gr = new Color[16];
0N/A for (int i = 0; i < 16; i++) {
0N/A int grey = (int) (170*(1-Math.pow(i/15.0, 2.3)));
0N/A gr[i] = new Color(grey, grey, grey);
0N/A }
0N/A }
0N/A int lg = 0;
0N/A int lim = ncon;
0N/A int c[] = con;
0N/A int v[] = tvert;
0N/A if (lim <= 0 || nvert <= 0)
0N/A return;
0N/A for (int i = 0; i < lim; i++) {
0N/A int T = c[i];
0N/A int p1 = ((T >> 16) & 0xFFFF) * 3;
0N/A int p2 = (T & 0xFFFF) * 3;
0N/A int grey = v[p1 + 2] + v[p2 + 2];
0N/A if (grey < 0)
0N/A grey = 0;
0N/A if (grey > 15)
0N/A grey = 15;
0N/A if (grey != lg) {
0N/A lg = grey;
0N/A g.setColor(gr[grey]);
0N/A }
0N/A g.drawLine(v[p1], v[p1 + 1],
0N/A v[p2], v[p2 + 1]);
0N/A }
0N/A }
0N/A
0N/A /** Find the bounding box of this model */
0N/A void findBB() {
0N/A if (nvert <= 0)
0N/A return;
0N/A float v[] = vert;
0N/A float xmin = v[0], xmax = xmin;
0N/A float ymin = v[1], ymax = ymin;
0N/A float zmin = v[2], zmax = zmin;
0N/A for (int i = nvert * 3; (i -= 3) > 0;) {
0N/A float x = v[i];
0N/A if (x < xmin)
0N/A xmin = x;
0N/A if (x > xmax)
0N/A xmax = x;
0N/A float y = v[i + 1];
0N/A if (y < ymin)
0N/A ymin = y;
0N/A if (y > ymax)
0N/A ymax = y;
0N/A float z = v[i + 2];
0N/A if (z < zmin)
0N/A zmin = z;
0N/A if (z > zmax)
0N/A zmax = z;
0N/A }
0N/A this.xmax = xmax;
0N/A this.xmin = xmin;
0N/A this.ymax = ymax;
0N/A this.ymin = ymin;
0N/A this.zmax = zmax;
0N/A this.zmin = zmin;
0N/A }
0N/A}
0N/A
0N/A/** An applet to put a 3D model into a page */
0N/Apublic class ThreeD extends Applet
0N/A implements Runnable, MouseListener, MouseMotionListener {
0N/A Model3D md;
0N/A boolean painted = true;
0N/A float xfac;
0N/A int prevx, prevy;
0N/A float xtheta, ytheta;
0N/A float scalefudge = 1;
0N/A Matrix3D amat = new Matrix3D(), tmat = new Matrix3D();
0N/A String mdname = null;
0N/A String message = null;
0N/A
0N/A public void init() {
0N/A mdname = getParameter("model");
0N/A try {
0N/A scalefudge = Float.valueOf(getParameter("scale")).floatValue();
0N/A }catch(Exception e){};
0N/A amat.yrot(20);
0N/A amat.xrot(20);
0N/A if (mdname == null)
0N/A mdname = "model.obj";
0N/A resize(getSize().width <= 20 ? 400 : getSize().width,
0N/A getSize().height <= 20 ? 400 : getSize().height);
0N/A addMouseListener(this);
0N/A addMouseMotionListener(this);
0N/A }
0N/A
0N/A public void destroy() {
0N/A removeMouseListener(this);
0N/A removeMouseMotionListener(this);
0N/A }
0N/A
0N/A public void run() {
0N/A InputStream is = null;
0N/A try {
0N/A Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
0N/A is = new URL(getDocumentBase(), mdname).openStream();
0N/A Model3D m = new Model3D (is);
0N/A md = m;
0N/A m.findBB();
0N/A m.compress();
0N/A float xw = m.xmax - m.xmin;
0N/A float yw = m.ymax - m.ymin;
0N/A float zw = m.zmax - m.zmin;
0N/A if (yw > xw)
0N/A xw = yw;
0N/A if (zw > xw)
0N/A xw = zw;
0N/A float f1 = getSize().width / xw;
0N/A float f2 = getSize().height / xw;
0N/A xfac = 0.7f * (f1 < f2 ? f1 : f2) * scalefudge;
0N/A } catch(Exception e) {
0N/A md = null;
0N/A message = e.toString();
0N/A }
0N/A try {
0N/A if (is != null)
0N/A is.close();
0N/A } catch(Exception e) {
0N/A }
0N/A repaint();
0N/A }
0N/A
0N/A public void start() {
0N/A if (md == null && message == null)
0N/A new Thread(this).start();
0N/A }
0N/A
0N/A public void stop() {
0N/A }
0N/A
0N/A public void mouseClicked(MouseEvent e) {
0N/A }
0N/A
0N/A public void mousePressed(MouseEvent e) {
0N/A prevx = e.getX();
0N/A prevy = e.getY();
0N/A e.consume();
0N/A }
0N/A
0N/A public void mouseReleased(MouseEvent e) {
0N/A }
0N/A
0N/A public void mouseEntered(MouseEvent e) {
0N/A }
0N/A
0N/A public void mouseExited(MouseEvent e) {
0N/A }
0N/A
0N/A public void mouseDragged(MouseEvent e) {
0N/A int x = e.getX();
0N/A int y = e.getY();
0N/A
0N/A tmat.unit();
0N/A float xtheta = (prevy - y) * 360.0f / getSize().width;
0N/A float ytheta = (x - prevx) * 360.0f / getSize().height;
0N/A tmat.xrot(xtheta);
0N/A tmat.yrot(ytheta);
0N/A amat.mult(tmat);
0N/A if (painted) {
0N/A painted = false;
0N/A repaint();
0N/A }
0N/A prevx = x;
0N/A prevy = y;
0N/A e.consume();
0N/A }
0N/A
0N/A public void mouseMoved(MouseEvent e) {
0N/A }
0N/A
0N/A public void paint(Graphics g) {
0N/A if (md != null) {
0N/A md.mat.unit();
0N/A md.mat.translate(-(md.xmin + md.xmax) / 2,
0N/A -(md.ymin + md.ymax) / 2,
0N/A -(md.zmin + md.zmax) / 2);
0N/A md.mat.mult(amat);
0N/A md.mat.scale(xfac, -xfac, 16 * xfac / getSize().width);
0N/A md.mat.translate(getSize().width / 2, getSize().height / 2, 8);
0N/A md.transformed = false;
0N/A md.paint(g);
0N/A setPainted();
0N/A } else if (message != null) {
0N/A g.drawString("Error in model:", 3, 20);
0N/A g.drawString(message, 10, 40);
0N/A }
0N/A }
0N/A
0N/A private synchronized void setPainted() {
0N/A painted = true;
0N/A notifyAll();
0N/A }
0N/A// private synchronized void waitPainted() {
0N/A// while (!painted)
0N/A// wait();
0N/A// painted = false;
0N/A// }
0N/A
0N/A public String getAppletInfo() {
0N/A return "Title: ThreeD \nAuthor: James Gosling? \nAn applet to put a 3D model into a page.";
0N/A }
0N/A
0N/A public String[][] getParameterInfo() {
0N/A String[][] info = {
0N/A {"model", "path string", "The path to the model to be displayed."},
0N/A {"scale", "float", "The scale of the model. Default is 1."}
0N/A };
0N/A return info;
0N/A }
0N/A}