KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > ThreeD


1 /*
2  * @(#)ThreeD.java 1.18 06/02/22
3  *
4  * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * -Redistribution of source code must retain the above copyright notice, this
10  * list of conditions and the following disclaimer.
11  *
12  * -Redistribution in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  *
16  * Neither the name of Sun Microsystems, Inc. or the names of contributors may
17  * be used to endorse or promote products derived from this software without
18  * specific prior written permission.
19  *
20  * This software is provided "AS IS," without a warranty of any kind. ALL
21  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
22  * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23  * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN")
24  * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
25  * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
26  * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
27  * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
28  * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
29  * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
30  * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
31  *
32  * You acknowledge that this software is not designed, licensed or intended
33  * for use in the design, construction, operation or maintenance of any
34  * nuclear facility.
35  */

36
37 /*
38  * @(#)ThreeD.java 1.18 06/02/22
39  */

40
41 /* A set of classes to parse, represent and display 3D wireframe models
42    represented in Wavefront .obj format. */

43
44 import java.applet.Applet JavaDoc;
45 import java.awt.Graphics JavaDoc;
46 import java.awt.Color JavaDoc;
47 import java.awt.Event JavaDoc;
48 import java.awt.event.*;
49 import java.io.*;
50 import java.net.URL JavaDoc;
51
52 class FileFormatException extends Exception JavaDoc {
53     public FileFormatException(String JavaDoc s) {
54     super(s);
55     }
56 }
57
58 /** The representation of a 3D model */
59 class Model3D {
60     float vert[];
61     int tvert[];
62     int nvert, maxvert;
63     int con[];
64     int ncon, maxcon;
65     boolean transformed;
66     Matrix3D mat;
67
68     float xmin, xmax, ymin, ymax, zmin, zmax;
69
70     Model3D () {
71     mat = new Matrix3D ();
72     mat.xrot(20);
73     mat.yrot(30);
74     }
75     /** Create a 3D model by parsing an input stream */
76     Model3D (InputStream is) throws IOException, FileFormatException {
77       this();
78       StreamTokenizer st = new StreamTokenizer(
79               new BufferedReader(new InputStreamReader(is, "UTF-8")));
80       st.eolIsSignificant(true);
81       st.commentChar('#');
82     scan:
83     while (true) {
84         switch (st.nextToken()) {
85           default:
86         break scan;
87           case StreamTokenizer.TT_EOL:
88         break;
89           case StreamTokenizer.TT_WORD:
90         if ("v".equals(st.sval)) {
91             double x = 0, y = 0, z = 0;
92             if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
93             x = st.nval;
94             if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
95                 y = st.nval;
96                 if (st.nextToken() == StreamTokenizer.TT_NUMBER)
97                 z = st.nval;
98             }
99             }
100             addVert((float) x, (float) y, (float) z);
101             while (st.ttype != StreamTokenizer.TT_EOL &&
102                 st.ttype != StreamTokenizer.TT_EOF)
103             st.nextToken();
104         } else if ("f".equals(st.sval) || "fo".equals(st.sval) || "l".equals(st.sval)) {
105             int start = -1;
106             int prev = -1;
107             int n = -1;
108             while (true)
109             if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
110                 n = (int) st.nval;
111                 if (prev >= 0)
112                 add(prev - 1, n - 1);
113                 if (start < 0)
114                 start = n;
115                 prev = n;
116             } else if (st.ttype == '/')
117                 st.nextToken();
118             else
119                 break;
120             if (start >= 0)
121             add(start - 1, prev - 1);
122             if (st.ttype != StreamTokenizer.TT_EOL)
123             break scan;
124         } else {
125             while (st.nextToken() != StreamTokenizer.TT_EOL
126                 && st.ttype != StreamTokenizer.TT_EOF);
127         }
128         }
129     }
130     is.close();
131     if (st.ttype != StreamTokenizer.TT_EOF)
132         throw new FileFormatException(st.toString());
133     }
134
135     /** Add a vertex to this model */
136     int addVert(float x, float y, float z) {
137     int i = nvert;
138     if (i >= maxvert)
139         if (vert == null) {
140         maxvert = 100;
141         vert = new float[maxvert * 3];
142         } else {
143         maxvert *= 2;
144         float nv[] = new float[maxvert * 3];
145         System.arraycopy(vert, 0, nv, 0, vert.length);
146         vert = nv;
147         }
148     i *= 3;
149     vert[i] = x;
150     vert[i + 1] = y;
151     vert[i + 2] = z;
152     return nvert++;
153     }
154     /** Add a line from vertex p1 to vertex p2 */
155     void add(int p1, int p2) {
156     int i = ncon;
157     if (p1 >= nvert || p2 >= nvert)
158         return;
159     if (i >= maxcon)
160         if (con == null) {
161         maxcon = 100;
162         con = new int[maxcon];
163         } else {
164         maxcon *= 2;
165         int nv[] = new int[maxcon];
166         System.arraycopy(con, 0, nv, 0, con.length);
167         con = nv;
168         }
169     if (p1 > p2) {
170         int t = p1;
171         p1 = p2;
172         p2 = t;
173     }
174     con[i] = (p1 << 16) | p2;
175     ncon = i + 1;
176     }
177     /** Transform all the points in this model */
178     void transform() {
179     if (transformed || nvert <= 0)
180         return;
181     if (tvert == null || tvert.length < nvert * 3)
182         tvert = new int[nvert*3];
183     mat.transform(vert, tvert, nvert);
184     transformed = true;
185     }
186
187    /* Quick Sort implementation
188     */

189    private void quickSort(int a[], int left, int right)
190    {
191       int leftIndex = left;
192       int rightIndex = right;
193       int partionElement;
194       if ( right > left)
195       {
196
197          /* Arbitrarily establishing partition element as the midpoint of
198           * the array.
199           */

200          partionElement = a[ ( left + right ) / 2 ];
201
202          // loop through the array until indices cross
203
while( leftIndex <= rightIndex )
204          {
205             /* find the first element that is greater than or equal to
206              * the partionElement starting from the leftIndex.
207              */

208             while( ( leftIndex < right ) && ( a[leftIndex] < partionElement ) )
209                ++leftIndex;
210
211             /* find an element that is smaller than or equal to
212              * the partionElement starting from the rightIndex.
213              */

214             while( ( rightIndex > left ) &&
215                    ( a[rightIndex] > partionElement ) )
216                --rightIndex;
217
218             // if the indexes have not crossed, swap
219
if( leftIndex <= rightIndex )
220             {
221                swap(a, leftIndex, rightIndex);
222                ++leftIndex;
223                --rightIndex;
224             }
225          }
226
227          /* If the right index has not reached the left side of array
228           * must now sort the left partition.
229           */

230          if( left < rightIndex )
231             quickSort( a, left, rightIndex );
232
233          /* If the left index has not reached the right side of array
234           * must now sort the right partition.
235           */

236          if( leftIndex < right )
237             quickSort( a, leftIndex, right );
238
239       }
240    }
241
242    private void swap(int a[], int i, int j)
243    {
244       int T;
245       T = a[i];
246       a[i] = a[j];
247       a[j] = T;
248    }
249
250
251     /** eliminate duplicate lines */
252     void compress() {
253     int limit = ncon;
254     int c[] = con;
255     quickSort(con, 0, ncon - 1);
256     int d = 0;
257     int pp1 = -1;
258     for (int i = 0; i < limit; i++) {
259         int p1 = c[i];
260         if (pp1 != p1) {
261         c[d] = p1;
262         d++;
263         }
264         pp1 = p1;
265     }
266     ncon = d;
267     }
268
269     static Color JavaDoc gr[];
270
271     /** Paint this model to a graphics context. It uses the matrix associated
272     with this model to map from model space to screen space.
273     The next version of the browser should have double buffering,
274     which will make this *much* nicer */

275     void paint(Graphics JavaDoc g) {
276     if (vert == null || nvert <= 0)
277         return;
278     transform();
279     if (gr == null) {
280         gr = new Color JavaDoc[16];
281         for (int i = 0; i < 16; i++) {
282         int grey = (int) (170*(1-Math.pow(i/15.0, 2.3)));
283         gr[i] = new Color JavaDoc(grey, grey, grey);
284         }
285     }
286     int lg = 0;
287     int lim = ncon;
288     int c[] = con;
289     int v[] = tvert;
290     if (lim <= 0 || nvert <= 0)
291         return;
292     for (int i = 0; i < lim; i++) {
293         int T = c[i];
294         int p1 = ((T >> 16) & 0xFFFF) * 3;
295         int p2 = (T & 0xFFFF) * 3;
296         int grey = v[p1 + 2] + v[p2 + 2];
297         if (grey < 0)
298         grey = 0;
299         if (grey > 15)
300         grey = 15;
301         if (grey != lg) {
302         lg = grey;
303         g.setColor(gr[grey]);
304         }
305         g.drawLine(v[p1], v[p1 + 1],
306                v[p2], v[p2 + 1]);
307     }
308     }
309
310     /** Find the bounding box of this model */
311     void findBB() {
312     if (nvert <= 0)
313         return;
314     float v[] = vert;
315     float xmin = v[0], xmax = xmin;
316     float ymin = v[1], ymax = ymin;
317     float zmin = v[2], zmax = zmin;
318     for (int i = nvert * 3; (i -= 3) > 0;) {
319         float x = v[i];
320         if (x < xmin)
321         xmin = x;
322         if (x > xmax)
323         xmax = x;
324         float y = v[i + 1];
325         if (y < ymin)
326         ymin = y;
327         if (y > ymax)
328         ymax = y;
329         float z = v[i + 2];
330         if (z < zmin)
331         zmin = z;
332         if (z > zmax)
333         zmax = z;
334     }
335     this.xmax = xmax;
336     this.xmin = xmin;
337     this.ymax = ymax;
338     this.ymin = ymin;
339     this.zmax = zmax;
340     this.zmin = zmin;
341     }
342 }
343
344 /** An applet to put a 3D model into a page */
345 public class ThreeD extends Applet JavaDoc
346   implements Runnable JavaDoc, MouseListener, MouseMotionListener {
347     Model3D md;
348     boolean painted = true;
349     float xfac;
350     int prevx, prevy;
351     float xtheta, ytheta;
352     float scalefudge = 1;
353     Matrix3D amat = new Matrix3D(), tmat = new Matrix3D();
354     String JavaDoc mdname = null;
355     String JavaDoc message = null;
356
357     public void init() {
358     mdname = getParameter("model");
359     try {
360         scalefudge = Float.valueOf(getParameter("scale")).floatValue();
361     }catch(Exception JavaDoc e){};
362     amat.yrot(20);
363     amat.xrot(20);
364     if (mdname == null)
365         mdname = "model.obj";
366     resize(getSize().width <= 20 ? 400 : getSize().width,
367            getSize().height <= 20 ? 400 : getSize().height);
368     addMouseListener(this);
369     addMouseMotionListener(this);
370     }
371
372     public void destroy() {
373         removeMouseListener(this);
374         removeMouseMotionListener(this);
375     }
376
377     public void run() {
378     InputStream is = null;
379     try {
380         Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
381         is = new URL JavaDoc(getDocumentBase(), mdname).openStream();
382         Model3D m = new Model3D (is);
383         md = m;
384         m.findBB();
385         m.compress();
386         float xw = m.xmax - m.xmin;
387         float yw = m.ymax - m.ymin;
388         float zw = m.zmax - m.zmin;
389         if (yw > xw)
390         xw = yw;
391         if (zw > xw)
392         xw = zw;
393         float f1 = getSize().width / xw;
394         float f2 = getSize().height / xw;
395         xfac = 0.7f * (f1 < f2 ? f1 : f2) * scalefudge;
396     } catch(Exception JavaDoc e) {
397         md = null;
398         message = e.toString();
399     }
400     try {
401         if (is != null)
402         is.close();
403     } catch(Exception JavaDoc e) {
404     }
405     repaint();
406     }
407
408     public void start() {
409     if (md == null && message == null)
410         new Thread JavaDoc(this).start();
411     }
412
413     public void stop() {
414     }
415
416     public void mouseClicked(MouseEvent e) {
417     }
418
419     public void mousePressed(MouseEvent e) {
420         prevx = e.getX();
421         prevy = e.getY();
422         e.consume();
423     }
424
425     public void mouseReleased(MouseEvent e) {
426     }
427
428     public void mouseEntered(MouseEvent e) {
429     }
430
431     public void mouseExited(MouseEvent e) {
432     }
433
434     public void mouseDragged(MouseEvent e) {
435         int x = e.getX();
436         int y = e.getY();
437
438         tmat.unit();
439         float xtheta = (prevy - y) * 360.0f / getSize().width;
440         float ytheta = (x - prevx) * 360.0f / getSize().height;
441         tmat.xrot(xtheta);
442         tmat.yrot(ytheta);
443         amat.mult(tmat);
444         if (painted) {
445             painted = false;
446             repaint();
447         }
448         prevx = x;
449         prevy = y;
450         e.consume();
451     }
452
453     public void mouseMoved(MouseEvent e) {
454     }
455
456     public void paint(Graphics JavaDoc g) {
457     if (md != null) {
458         md.mat.unit();
459         md.mat.translate(-(md.xmin + md.xmax) / 2,
460                  -(md.ymin + md.ymax) / 2,
461                  -(md.zmin + md.zmax) / 2);
462         md.mat.mult(amat);
463         md.mat.scale(xfac, -xfac, 16 * xfac / getSize().width);
464         md.mat.translate(getSize().width / 2, getSize().height / 2, 8);
465         md.transformed = false;
466         md.paint(g);
467         setPainted();
468     } else if (message != null) {
469         g.drawString("Error in model:", 3, 20);
470         g.drawString(message, 10, 40);
471     }
472     }
473
474     private synchronized void setPainted() {
475     painted = true;
476     notifyAll();
477     }
478 // private synchronized void waitPainted() {
479
// while (!painted)
480
// wait();
481
// painted = false;
482
// }
483

484     public String JavaDoc getAppletInfo() {
485         return "Title: ThreeD \nAuthor: James Gosling? \nAn applet to put a 3D model into a page.";
486     }
487
488     public String JavaDoc[][] getParameterInfo() {
489         String JavaDoc[][] info = {
490             {"model", "path string", "The path to the model to be displayed."},
491             {"scale", "float", "The scale of the model. Default is 1."}
492         };
493         return info;
494     }
495 }
496
Popular Tags