Crossfire JXClient, Trunk
ThreeD.java
Go to the documentation of this file.
1 /*
2  * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * - Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * - Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * - Neither the name of Oracle nor the names of its
16  * contributors may be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * This source code is provided to illustrate the usage of a given feature
34  * or technique and has been deliberately simplified. Additional steps
35  * required for a production-quality application, such as security checks,
36  * input validation and proper error handling, might not be present in
37  * this sample code.
38  */
39 
40 
41 
42 import java.applet.Applet;
43 import java.awt.Graphics;
44 import java.awt.Color;
45 import java.awt.event.*;
46 import java.io.*;
47 import java.net.URL;
48 
49 
50 /* A set of classes to parse, represent and display 3D wireframe models
51 represented in Wavefront .obj format. */
52 @SuppressWarnings("serial")
53 class FileFormatException extends Exception {
54 
55  public FileFormatException(String s) {
56  super(s);
57  }
58 }
59 
60 
62 final class Model3D {
63 
64  float vert[];
65  int tvert[];
66  int nvert, maxvert;
67  int con[];
68  int ncon, maxcon;
69  boolean transformed;
70  Matrix3D mat;
71  float xmin, xmax, ymin, ymax, zmin, zmax;
72 
73  Model3D() {
74  mat = new Matrix3D();
75  mat.xrot(20);
76  mat.yrot(30);
77  }
78 
80  Model3D(InputStream is) throws IOException, FileFormatException {
81  this();
82  StreamTokenizer st = new StreamTokenizer(
83  new BufferedReader(new InputStreamReader(is, "UTF-8")));
84  st.eolIsSignificant(true);
85  st.commentChar('#');
86  scan:
87  while (true) {
88  switch (st.nextToken()) {
89  default:
90  break scan;
91  case StreamTokenizer.TT_EOL:
92  break;
93  case StreamTokenizer.TT_WORD:
94  if ("v".equals(st.sval)) {
95  double x = 0, y = 0, z = 0;
96  if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
97  x = st.nval;
98  if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
99  y = st.nval;
100  if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
101  z = st.nval;
102  }
103  }
104  }
105  addVert((float) x, (float) y, (float) z);
106  while (st.ttype != StreamTokenizer.TT_EOL && st.ttype
107  != StreamTokenizer.TT_EOF) {
108  st.nextToken();
109  }
110  } else if ("f".equals(st.sval) || "fo".equals(st.sval) || "l".
111  equals(st.sval)) {
112  int start = -1;
113  int prev = -1;
114  int n = -1;
115  while (true) {
116  if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
117  n = (int) st.nval;
118  if (prev >= 0) {
119  add(prev - 1, n - 1);
120  }
121  if (start < 0) {
122  start = n;
123  }
124  prev = n;
125  } else if (st.ttype == '/') {
126  st.nextToken();
127  } else {
128  break;
129  }
130  }
131  if (start >= 0) {
132  add(start - 1, prev - 1);
133  }
134  if (st.ttype != StreamTokenizer.TT_EOL) {
135  break scan;
136  }
137  } else {
138  while (st.nextToken() != StreamTokenizer.TT_EOL
139  && st.ttype != StreamTokenizer.TT_EOF) {
140  // no-op
141  }
142  }
143  }
144  }
145  is.close();
146  if (st.ttype != StreamTokenizer.TT_EOF) {
147  throw new FileFormatException(st.toString());
148  }
149  }
150 
152  int addVert(float x, float y, float z) {
153  int i = nvert;
154  if (i >= maxvert) {
155  if (vert == null) {
156  maxvert = 100;
157  vert = new float[maxvert * 3];
158  } else {
159  maxvert *= 2;
160  float nv[] = new float[maxvert * 3];
161  System.arraycopy(vert, 0, nv, 0, vert.length);
162  vert = nv;
163  }
164  }
165  i *= 3;
166  vert[i] = x;
167  vert[i + 1] = y;
168  vert[i + 2] = z;
169  return nvert++;
170  }
171 
173  void add(int p1, int p2) {
174  int i = ncon;
175  if (p1 >= nvert || p2 >= nvert) {
176  return;
177  }
178  if (i >= maxcon) {
179  if (con == null) {
180  maxcon = 100;
181  con = new int[maxcon];
182  } else {
183  maxcon *= 2;
184  int nv[] = new int[maxcon];
185  System.arraycopy(con, 0, nv, 0, con.length);
186  con = nv;
187  }
188  }
189  if (p1 > p2) {
190  int t = p1;
191  p1 = p2;
192  p2 = t;
193  }
194  con[i] = (p1 << 16) | p2;
195  ncon = i + 1;
196  }
197 
199  void transform() {
200  if (transformed || nvert <= 0) {
201  return;
202  }
203  if (tvert == null || tvert.length < nvert * 3) {
204  tvert = new int[nvert * 3];
205  }
206  mat.transform(vert, tvert, nvert);
207  transformed = true;
208  }
209 
210  /* Quick Sort implementation
211  */
212  private void quickSort(int a[], int left, int right) {
213  int leftIndex = left;
214  int rightIndex = right;
215  int partionElement;
216  if (right > left) {
217 
218  /* Arbitrarily establishing partition element as the midpoint of
219  * the array.
220  */
221  partionElement = a[(left + right) / 2];
222 
223  // loop through the array until indices cross
224  while (leftIndex <= rightIndex) {
225  /* find the first element that is greater than or equal to
226  * the partionElement starting from the leftIndex.
227  */
228  while ((leftIndex < right) && (a[leftIndex] < partionElement)) {
229  ++leftIndex;
230  }
231 
232  /* find an element that is smaller than or equal to
233  * the partionElement starting from the rightIndex.
234  */
235  while ((rightIndex > left) && (a[rightIndex] > partionElement)) {
236  --rightIndex;
237  }
238 
239  // if the indexes have not crossed, swap
240  if (leftIndex <= rightIndex) {
241  swap(a, leftIndex, rightIndex);
242  ++leftIndex;
243  --rightIndex;
244  }
245  }
246 
247  /* If the right index has not reached the left side of array
248  * must now sort the left partition.
249  */
250  if (left < rightIndex) {
251  quickSort(a, left, rightIndex);
252  }
253 
254  /* If the left index has not reached the right side of array
255  * must now sort the right partition.
256  */
257  if (leftIndex < right) {
258  quickSort(a, leftIndex, right);
259  }
260 
261  }
262  }
263 
264  private void swap(int a[], int i, int j) {
265  int T;
266  T = a[i];
267  a[i] = a[j];
268  a[j] = T;
269  }
270 
272  void compress() {
273  int limit = ncon;
274  int c[] = con;
275  quickSort(con, 0, ncon - 1);
276  int d = 0;
277  int pp1 = -1;
278  for (int i = 0; i < limit; i++) {
279  int p1 = c[i];
280  if (pp1 != p1) {
281  c[d] = p1;
282  d++;
283  }
284  pp1 = p1;
285  }
286  ncon = d;
287  }
288  static Color gr[];
289 
294  void paint(Graphics g) {
295  if (vert == null || nvert <= 0) {
296  return;
297  }
298  transform();
299  if (gr == null) {
300  gr = new Color[16];
301  for (int i = 0; i < 16; i++) {
302  int grey = (int) (170 * (1 - Math.pow(i / 15.0, 2.3)));
303  gr[i] = new Color(grey, grey, grey);
304  }
305  }
306  int lg = 0;
307  int lim = ncon;
308  int c[] = con;
309  int v[] = tvert;
310  if (lim <= 0 || nvert <= 0) {
311  return;
312  }
313  for (int i = 0; i < lim; i++) {
314  int T = c[i];
315  int p1 = ((T >> 16) & 0xFFFF) * 3;
316  int p2 = (T & 0xFFFF) * 3;
317  int grey = v[p1 + 2] + v[p2 + 2];
318  if (grey < 0) {
319  grey = 0;
320  }
321  if (grey > 15) {
322  grey = 15;
323  }
324  if (grey != lg) {
325  lg = grey;
326  g.setColor(gr[grey]);
327  }
328  g.drawLine(v[p1], v[p1 + 1],
329  v[p2], v[p2 + 1]);
330  }
331  }
332 
334  void findBB() {
335  if (nvert <= 0) {
336  return;
337  }
338  float v[] = vert;
339  float _xmin = v[0], _xmax = _xmin;
340  float _ymin = v[1], _ymax = _ymin;
341  float _zmin = v[2], _zmax = _zmin;
342  for (int i = nvert * 3; (i -= 3) > 0;) {
343  float x = v[i];
344  if (x < _xmin) {
345  _xmin = x;
346  }
347  if (x > _xmax) {
348  _xmax = x;
349  }
350  float y = v[i + 1];
351  if (y < _ymin) {
352  _ymin = y;
353  }
354  if (y > _ymax) {
355  _ymax = y;
356  }
357  float z = v[i + 2];
358  if (z < _zmin) {
359  _zmin = z;
360  }
361  if (z > _zmax) {
362  _zmax = z;
363  }
364  }
365  this.xmax = _xmax;
366  this.xmin = _xmin;
367  this.ymax = _ymax;
368  this.ymin = _ymin;
369  this.zmax = _zmax;
370  this.zmin = _zmin;
371  }
372 }
373 
374 
376 @SuppressWarnings("serial")
377 public class ThreeD extends Applet
378  implements Runnable, MouseListener, MouseMotionListener {
379 
380  Model3D md;
381  boolean painted = true;
382  float xfac;
383  int prevx, prevy;
384  float scalefudge = 1;
385  Matrix3D amat = new Matrix3D(), tmat = new Matrix3D();
386  String mdname = null;
387  String message = null;
388 
389  @Override
390  public void init() {
391  mdname = getParameter("model");
392  try {
393  scalefudge = Float.valueOf(getParameter("scale")).floatValue();
394  } catch (Exception ignored) {
395  // fall back to default scalefudge = 1
396  }
397  amat.yrot(20);
398  amat.xrot(20);
399  if (mdname == null) {
400  mdname = "model.obj";
401  }
402  resize(getSize().width <= 20 ? 400 : getSize().width,
403  getSize().height <= 20 ? 400 : getSize().height);
404  addMouseListener(this);
405  addMouseMotionListener(this);
406  }
407 
408  @Override
409  public void destroy() {
410  removeMouseListener(this);
411  removeMouseMotionListener(this);
412  }
413 
414  @Override
415  public void run() {
416  InputStream is = null;
417  try {
418  Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
419  is = getClass().getResourceAsStream(mdname);
420  Model3D m = new Model3D(is);
421  md = m;
422  m.findBB();
423  m.compress();
424  float xw = m.xmax - m.xmin;
425  float yw = m.ymax - m.ymin;
426  float zw = m.zmax - m.zmin;
427  if (yw > xw) {
428  xw = yw;
429  }
430  if (zw > xw) {
431  xw = zw;
432  }
433  float f1 = getSize().width / xw;
434  float f2 = getSize().height / xw;
435  xfac = 0.7f * (f1 < f2 ? f1 : f2) * scalefudge;
436  } catch (Exception e) {
437  md = null;
438  message = e.toString();
439  }
440  try {
441  if (is != null) {
442  is.close();
443  }
444  } catch (Exception e) {
445  }
446  repaint();
447  }
448 
449  @Override
450  public void start() {
451  if (md == null && message == null) {
452  new Thread(this).start();
453  }
454  }
455 
456  @Override
457  public void stop() {
458  }
459 
460  @Override
461  public void mouseClicked(MouseEvent e) {
462  }
463 
464  @Override
465  public void mousePressed(MouseEvent e) {
466  prevx = e.getX();
467  prevy = e.getY();
468  e.consume();
469  }
470 
471  @Override
472  public void mouseReleased(MouseEvent e) {
473  }
474 
475  @Override
476  public void mouseEntered(MouseEvent e) {
477  }
478 
479  @Override
480  public void mouseExited(MouseEvent e) {
481  }
482 
483  @Override
484  public void mouseDragged(MouseEvent e) {
485  int x = e.getX();
486  int y = e.getY();
487 
488  tmat.unit();
489  float xtheta = (prevy - y) * 360.0f / getSize().width;
490  float ytheta = (x - prevx) * 360.0f / getSize().height;
491  tmat.xrot(xtheta);
492  tmat.yrot(ytheta);
493  amat.mult(tmat);
494  if (painted) {
495  painted = false;
496  repaint();
497  }
498  prevx = x;
499  prevy = y;
500  e.consume();
501  }
502 
503  @Override
504  public void mouseMoved(MouseEvent e) {
505  }
506 
507  @Override
508  public void paint(Graphics g) {
509  if (md != null) {
510  md.mat.unit();
511  md.mat.translate(-(md.xmin + md.xmax) / 2,
512  -(md.ymin + md.ymax) / 2,
513  -(md.zmin + md.zmax) / 2);
514  md.mat.mult(amat);
515  md.mat.scale(xfac, -xfac, 16 * xfac / getSize().width);
516  md.mat.translate(getSize().width / 2, getSize().height / 2, 8);
517  md.transformed = false;
518  md.paint(g);
519  setPainted();
520  } else if (message != null) {
521  g.drawString("Error in model:", 3, 20);
522  g.drawString(message, 10, 40);
523  }
524  }
525 
526  private synchronized void setPainted() {
527  painted = true;
528  notifyAll();
529  }
530 
531  @Override
532  public String getAppletInfo() {
533  return "Title: ThreeD \nAuthor: James Gosling? \n"
534  + "An applet to put a 3D model into a page.";
535  }
536 
537  @Override
538  public String[][] getParameterInfo() {
539  String[][] info = {
540  { "model", "path string", "The path to the model to be displayed." },
541  { "scale", "float", "The scale of the model. Default is 1." }
542  };
543  return info;
544  }
545 }
ThreeD.mouseMoved
void mouseMoved(MouseEvent e)
Definition: ThreeD.java:504
ThreeD.getParameterInfo
String[][] getParameterInfo()
Definition: ThreeD.java:538
ThreeD.mouseDragged
void mouseDragged(MouseEvent e)
Definition: ThreeD.java:484
ThreeD.mousePressed
void mousePressed(MouseEvent e)
Definition: ThreeD.java:465
ThreeD.mouseClicked
void mouseClicked(MouseEvent e)
Definition: ThreeD.java:461
ThreeD.paint
void paint(Graphics g)
Definition: ThreeD.java:508
ThreeD
Definition: ThreeD.java:377
ThreeD.getAppletInfo
String getAppletInfo()
Definition: ThreeD.java:532
ThreeD.mouseReleased
void mouseReleased(MouseEvent e)
Definition: ThreeD.java:472
ThreeD.setPainted
synchronized void setPainted()
Definition: ThreeD.java:526
ThreeD.mouseEntered
void mouseEntered(MouseEvent e)
Definition: ThreeD.java:476
ThreeD.start
void start()
Definition: ThreeD.java:450
ThreeD.mouseExited
void mouseExited(MouseEvent e)
Definition: ThreeD.java:480
ThreeD.destroy
void destroy()
Definition: ThreeD.java:409
ThreeD.stop
void stop()
Definition: ThreeD.java:457
ThreeD.run
void run()
Definition: ThreeD.java:415
ThreeD.init
void init()
Definition: ThreeD.java:390