View Javadoc

1   /* -------------------------------------------------------------------
2    * Java source file for the class SurfaceModeler
3    * 
4    * Copyright (c), 2003, Masahiro Takatsuka.
5    * All Rights Researved.
6    * 
7    * Original Author: Masahiro Takatsuka (masa@jbeans.net)
8    * $Author: takatsukam $
9    * 
10   * $Date: 2004/11/25 22:02:31 $
11   * 
12   * $Id: SurfaceModeler.java,v 1.4 2004/11/25 22:02:31 takatsukam Exp $
13   * 
14   * Reference:		Document no:
15   * ___				___
16   * 
17   * To Do:
18   * ___
19   * 
20  ------------------------------------------------------------------- */
21  
22  /* --------------------------- Package ---------------------------- */
23  package net.jbeans.j3d.modeler.geometry;
24  
25  /* ------------------ Import classes (packages) ------------------- *//package-summary/html">class="comment"> ------------------ Import classes (packages) ------------------- *//package-summary.html">class="comment">/* ------------------ Import classes (packages) ------------------- *//package-summary.html">class="comment"> ------------------ Import classes (packages) ------------------- */
26  import java.awt.*;
27  import java.awt.event.*;
28  import java.io.*;
29  import java.util.*;
30  import javax.media.j3d.*;
31  import javax.vecmath.*;
32  //import com.sun.j3d.utils.geometry.*;
33  
34  import net.jbeans.j3d.data.*;
35  import net.jbeans.j3d.modeler.geometry.util.*;
36  
37  import net.jbeans.util.debug.*;
38  
39  /*====================================================================
40                  Implementation of class SurfaceModeler                
41  ====================================================================*/
42  /***
43   * SurfaceModeler generates a suface geometry from the specified data.
44   * 
45   * @version $Revision: 1.4 $
46   * @author Masahiro Takatsuka (masa@jbeans.net)
47   * @see GeometryModeler
48   */
49  
50  public abstract class SurfaceModeler extends GeometryModeler {
51  	private static final boolean DEBUG = Debug.getDebugFlag(SurfaceModeler.class);
52  
53  	private static final int INVALID_DATA = -1;
54  	private static final int USE_INDICES = 0;
55  	private static final int NO_INDICES = 1;
56  	
57  	public static final int NAIVE_NORMAL = 0;
58  	public static final int ADVANCE_NORMAL = 1;
59  	
60  	static com.sun.j3d.utils.geometry.Triangulator sTriangulator = new com.sun.j3d.utils.geometry.Triangulator();
61  	static com.sun.j3d.utils.geometry.Stripifier sStripifier = new com.sun.j3d.utils.geometry.Stripifier();
62  	
63  	/* ------------------------ not serialized ------------------------ */
64  	transient protected int totalIndex;
65  	/*
66  	* Essential attributes to construct a polygonal surface.
67  	*/
68  	transient protected int[] essentials;
69  	
70  	transient protected Object[] entryArray;
71  	
72  	transient protected double[] tmp;
73  	transient protected NormalGenerator normalgen;
74  	transient protected GeometryInfo geomInfo;
75  	transient protected double[] x;
76  	transient protected double[] y;
77  	transient protected double[] z;
78  	transient protected double[] color;
79  	transient protected double[] alpha;	
80  	transient protected double[][] normal;
81  	transient protected double[][] tCoords;
82  	transient protected int[] coordinateIndices;
83  	transient protected int[] colorIndices;
84  	transient protected int[] normalIndices;
85  	transient protected int[] texCoordIndices;
86  	transient protected int[] stripCounts;
87  	transient protected int[] contourCounts;
88  	
89  	// these are for updateData(Geometry) method
90  	transient protected Point3f[] coords3f;// for update
91  	transient protected Color3f[] colors3f;
92  	transient protected Color4f[] colors4f;
93  	transient protected Vector3f[] normals3f;
94  	transient protected TexCoord2f[] texCoords2f;
95  	transient protected TexCoord3f[] texCoords3f;
96  
97  	transient protected boolean hasColors = false;
98  	transient protected boolean hasTCoords = false;
99  
100 	transient protected int coordDim = 3;
101 	transient protected int colorDim = 4;
102 	transient protected int normalDim = 3;
103 	transient protected int tCoordDim = 2;
104 
105 	transient protected int primitive = GeometryInfo.POLYGON_ARRAY;
106 	
107 	/* -------------------------- serialized -------------------------- */
108 	protected boolean computeNormalsOnUpdate;
109 	protected ColorLookupTable lookupTable; // color lookup table
110 	protected double creaseAngle;
111 	
112 	public SurfaceModeler() {
113 		super();
114 		this.lookupTable = new ColorLookupTable();
115 		setComputeNormalsOnUpdate(true); // default true
116 		
117 		initialize();
118 	}
119 
120 	protected void initialize() {
121 		this.tmp = new double[3];
122 		this.normalgen = new NormalGenerator();
123 		this.creaseAngle = this.normalgen.getCreaseAngle();
124 		this.geomInfo = new GeometryInfo(this.primitive);
125 		this.x = null;
126 		this.y = null;
127 		this.z = null;
128 		this.color = null;
129 		this.alpha = null;
130 		this.normal = null;
131 		this.tCoords = null;
132 		initGeometry();
133 	}
134 
135 	public void setCreaseAngle(double angle) {
136 		this.creaseAngle = angle;
137 		if (this.normalgen != null) {
138 			this.normalgen.setCreaseAngle(this.creaseAngle);
139 		}
140 	}
141 
142 	public double getCreaseAngle() {
143 		return this.creaseAngle;
144 	}
145 	
146 	protected abstract void initGeometry();
147 	
148 	/***
149 	 * Get the value of computeNormalsOnUpdate.
150 	 * @return value of computeNormalsOnUpdate.
151 	 */
152 	public boolean isComputeNormalsOnUpdate() {
153 		return getComputeNormalsOnUpdate();
154 	}
155 	
156 	public boolean getComputeNormalsOnUpdate() {
157 		return computeNormalsOnUpdate;
158 	}
159 	
160 	/***
161 	 * Set the value of computeNormalsOnUpdate.
162 	 * @param v  Value to assign to computeNormalsOnUpdate.
163 	 */
164 	public void setComputeNormalsOnUpdate(boolean  v) {
165 		this.computeNormalsOnUpdate = v;
166 	}
167 
168 	public void setColorLookupTable(ColorLookupTable lookupTable) {
169 		this.lookupTable = lookupTable;
170 		setGeometryData();
171     }
172 	
173 	public ColorLookupTable getLookupTable() {
174 		return this.lookupTable;
175 	} 
176 
177 	public void setColorTable(Color[] colors) {
178 		this.lookupTable.setColorLookupTable(colors);
179 		setGeometryData();
180 	}
181 
182 	public void setPrimitive(int primitive) {
183 		switch (primitive) {
184 		case GeometryInfo.POLYGON_ARRAY:
185 		default:
186 			primitive = GeometryInfo.POLYGON_ARRAY;
187 			break;
188 		case GeometryInfo.QUAD_ARRAY:
189 		case GeometryInfo.TRIANGLE_ARRAY:
190 		case GeometryInfo.TRIANGLE_FAN_ARRAY:
191 		case GeometryInfo.TRIANGLE_STRIP_ARRAY:
192 			break;
193 		}
194 		this.primitive = primitive;
195 	}
196 
197 	public int getPrimitive() {
198 		return this.primitive;
199 	}
200 	
201     /***
202      * Serialization methods
203      */
204     private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException {
205 		s.defaultReadObject();
206 		initialize();
207     }
208 
209 	void setXCoordinates(double[] x) {
210 		this.x = x;
211 	}
212 
213 	void setYCoordinates(double[] y) {
214 		this.y = y;
215 	}
216 
217 	void setZCoordinates(double[] z) {
218 		this.z = z;
219 	}
220 
221 	void setColors(double[] color) {
222 		this.color = color;
223 	}
224 
225 	void setTransparency(double[] alpha) {
226 		this.alpha = alpha;
227 	}
228 	
229 	void setNormals(double[][] normal) {
230 		this.normal = normal;
231 	}
232 
233 	void setTextureCoords(double[][] tCoords) {
234 		this.tCoords = tCoords;
235 	}
236 
237 	void setStripCounts(int[] stripcounts) {
238 		this.stripCounts = stripcounts;
239 	}
240 
241 	void setContourCounts(int[] contourcounts) {
242 		this.contourCounts = contourcounts;
243 	}
244 	
245 	void setCoordinateIndices(int[] coordinateIndices) {
246 		this.coordinateIndices = coordinateIndices;
247 	}
248 
249 	void setColorIndices(int[] colorIndices) {
250 		this.colorIndices = colorIndices;
251 	}
252 
253 	void setNormalIndices(int[] normalIndices) {
254 		this.normalIndices = normalIndices;
255 	}
256 
257 	void setTextureCoordinateIndices(int[] texCoordIndices) {
258 		this.texCoordIndices = texCoordIndices;
259 	}
260 
261 	void setupGeometryInfo() {
262 		// fist set primitive type.
263 		if (DEBUG) {
264 			System.out.println("primitive" + this.primitive);
265 		}
266 		this.geomInfo.reset(this.primitive);
267 		
268 		int vertexCount = this.x.length; // assuming y z have the same length.
269 		double[] coords = new double[vertexCount * 3];
270 		int tmpidx;
271 		for (int i = 0; i < vertexCount; i++) {
272 			tmpidx = i * 3;
273 			coords[tmpidx    ] = this.x[i];
274 			coords[tmpidx + 1] = this.y[i];
275 			coords[tmpidx + 2] = this.z[i];
276 		}
277 		this.geomInfo.setCoordinates(coords);
278 
279 		float[] colorValues = null;
280 		if (this.color != null) {
281 			this.hasColors = true;
282 			if (this.alpha != null) {
283 				colorValues = new float[this.color.length * 4];
284 			} else {
285 				colorValues = new float[this.color.length * 3];
286 			}
287 			for (int i = 0; i < this.color.length; i++) {
288 				tmp = this.lookupTable.mapValue(this.color[i]);
289 				for (int j = 0; j < 3; j++) {
290 					colorValues[i * 3 + j] = (float) tmp[j];
291 				}
292 				if (this.alpha != null) {
293 					colorValues[i + 3] = (float) this.alpha[i];
294 				}
295 			}
296 			if (this.alpha != null) {
297 				this.geomInfo.setColors4(colorValues);
298 			} else {
299 				this.geomInfo.setColors3(colorValues);
300 			}
301 		}
302 		
303 		float[] tCoordValues = null;
304 		if (this.tCoords != null) {
305 			this.tCoordDim = this.tCoords[0].length;
306 			this.hasTCoords = true;
307 			tCoordValues = new float[this.tCoords.length * this.tCoordDim];
308 			
309 			for (int i = 0; i < this.tCoords.length; i++) {
310 				System.arraycopy(this.tCoords[i], 0, tCoordValues, i * this.tCoordDim, this.tCoordDim);
311 			}
312 			if (this.tCoordDim == 2) {
313 				this.geomInfo.setTextureCoordinates2(tCoordValues);
314 			} else {
315 				this.geomInfo.setTextureCoordinates2(tCoordValues);
316 			}
317 		}
318 		
319 		float[] normalValues = null;
320 		if (this.normal != null) {
321 			normalValues = new float[this.normal.length * 3];
322 			for (int i = 0; i < this.normal.length; i++) {
323 				System.arraycopy(this.normal[i], 0, normalValues, i * 3, 3);
324 			}
325 			this.geomInfo.setNormals(normalValues);
326 		}
327 
328 		if (this.coordinateIndices != null) {
329 			this.geomInfo.setCoordinateIndices(this.coordinateIndices);
330 		}
331 
332 		if (this.colorIndices != null) {
333 			this.geomInfo.setColorIndices(this.colorIndices);
334 		}
335 
336 		if (this.normalIndices != null) {
337 			this.geomInfo.setNormalIndices(this.normalIndices);
338 		}
339 
340 		if (this.texCoordIndices != null) {
341 			this.geomInfo.setTextureCoordinateIndices(this.texCoordIndices);
342 		}
343 
344 		if (this.stripCounts != null) {
345 			this.geomInfo.setStripCounts(this.stripCounts);
346 		}
347 
348 		if (this.contourCounts != null) {
349 			this.geomInfo.setContourCounts(this.contourCounts);
350 		}
351 
352 		// if all polygons are triangle, no need to triangulate.
353 		if (this.geomInfo.getPrimitive() == GeometryInfo.POLYGON_ARRAY) {
354 			if (DEBUG) {
355 				System.out.print("Triangulating...");
356 			}
357   			sTriangulator.triangulate(this.geomInfo);
358 			if (DEBUG) {
359 				System.out.println("\rTriangulating...finished");
360 			}
361 		}
362 		
363 		/*
364 		  generate normals...this is for lighting.
365 		*/
366 		if (normalValues == null) {
367 			if (DEBUG) {
368 				System.out.print("Generating normals...");
369 			}
370 			this.normalgen.generateNormals(this.geomInfo);
371 			if (DEBUG) {
372 				System.out.println("\rGenerating normals...finished");
373 			}
374 		}
375 		
376 		// stripify
377 		/*
378 		* no stripify for compute normal on update.
379 		* Masahiro Takatsuka/2001-Jun-29
380 		*/
381 		if (false) {
382 			if (DEBUG) {
383 				System.out.print("Stripifiing...");
384 			}
385 			sStripifier.stripify(this.geomInfo);
386 			this.geomInfo.recomputeIndices();
387 			if (DEBUG) {
388 				System.out.println("\rStripifiing...finished");
389 			}
390 		}
391 	}
392 
393 	private void expandNormalRef(IndexedGeometryArray geom) {
394 		Vector3f[] nrmls = geom.getNormalRef3f();
395 		int indexcount = geom.getIndexCount();
396 		int size = Math.max(indexcount, geom.getVertexCount());
397 		if (DEBUG) {
398 			System.out.println("expanded num = " + size);
399 		}
400 		Vector3f[] tmpnrmls = new Vector3f[size];
401 		int[] nrmlInds = new int[indexcount];
402 		geom.getNormalIndices(0, nrmlInds);
403 		for (int i = 0; i < indexcount; i++) {
404 			tmpnrmls[i] = new Vector3f(nrmls[nrmlInds[i]]);
405 			nrmlInds[i] = i;
406 		}
407 		for (int i = indexcount; i < size; i++) {
408 			tmpnrmls[i] = new Vector3f();
409 		}
410 		geom.setNormalRef3f(tmpnrmls);
411 		geom.setNormalIndices(0, nrmlInds);
412 	}
413 	
414 	/***
415 	 * Construct a javax.media.j3d.GeometryArray object from geometry information.
416 	 */
417 	protected void modelGeometry() {
418 		setupGeometryInfo();
419 		
420 		// if you want to dynamically change the geometry, use indexed.
421 		GeometryArray geom = this.geomInfo.getIndexedGeometryArray();
422 		// get references
423 		this.coords3f = ((GeometryArray) geom).getCoordRef3f();
424 		if (this.computeNormalsOnUpdate) {
425 			expandNormalRef((IndexedGeometryArray) geom);
426 		}
427 		this.normals3f = ((GeometryArray) geom).getNormalRef3f();
428 		
429 		if (DEBUG) {
430 			System.out.println("num of x = " + this.x.length);
431 			System.out.println("num of coords3f = " + this.coords3f.length);
432 			System.out.println("num of normals3f = " + this.normals3f.length);
433 		}
434 		if (this.hasColors) {
435 			this.colors3f = ((GeometryArray) geom).getColorRef3f();
436 			this.colors4f = ((GeometryArray) geom).getColorRef4f();
437 		}
438 		
439 		if (this.hasTCoords) {
440 			if (this.tCoordDim == 2) {
441 				this.texCoords2f = ((GeometryArray) geom).getTexCoordRef2f(0);
442 			} else if (this.tCoordDim == 3) {
443 				this.texCoords3f = ((GeometryArray) geom).getTexCoordRef3f(0);
444 			}
445 		}
446 
447 		// register this new geometry.
448 		setGeometry(geom);
449 	}
450 
451 	/***
452 	 *
453 	 */
454 	public void updateData(Geometry geom) {	
455 		int vertexCount = this.x.length; // assuming y z have the same length.
456 		for (int i = 0; i < vertexCount; i++) {
457 			this.coords3f[i].x = (float) this.x[i];
458 			this.coords3f[i].y = (float) this.y[i];
459 			this.coords3f[i].z = (float) this.z[i];
460 		}
461 
462 		if (this.color != null) {
463 			for (int i = 0; i < this.color.length; i++) {
464 				tmp = this.lookupTable.mapValue(this.color[i]);
465 				if (this.alpha != null) {
466 					this.colors4f[i].x = (float) tmp[0];
467 					this.colors4f[i].y = (float) tmp[1];
468 					this.colors4f[i].z = (float) tmp[2];
469 					this.colors4f[i].w = (float) tmp[3];
470 				} else {
471 					this.colors3f[i].x = (float) tmp[0];
472 					this.colors3f[i].y = (float) tmp[1];
473 					this.colors3f[i].z = (float) tmp[2];
474 				}
475 			}
476 		}
477 
478 		if (this.tCoords != null) {
479 			for (int i = 0; i < this.tCoords.length; i++) {
480 				if (tCoordDim == 2) {
481 					this.texCoords2f[i].x = (float) this.tCoords[i][0];
482 					this.texCoords2f[i].y = (float) this.tCoords[i][1];
483 
484 				} else {
485 					this.texCoords3f[i].x = (float) this.tCoords[i][0];
486 					this.texCoords3f[i].y = (float) this.tCoords[i][1];
487 					this.texCoords3f[i].z = (float) this.tCoords[i][2];
488 					
489 				}
490 			}
491 		}
492 
493 		if (this.normal != null) {
494 			for (int i = 0; i < this.normal.length; i++) {
495 				this.normals3f[i].x = (float) this.normal[i][0];
496 				this.normals3f[i].y = (float) this.normal[i][1];
497 				this.normals3f[i].z = (float) this.normal[i][2];
498 			}
499 		} else if (this.computeNormalsOnUpdate) {
500 			this.normalgen.generateNormals((GeometryArray) geom, this.coords3f);
501 		}
502 	}
503 
504 	protected void setValueAt(int index, Object value) {
505 		this.entryArray[index] = value;
506 		if (DEBUG) {
507 			System.out.println("setValuleAt:[" + index + "] = " + value);
508 		}
509 		setGeometryData();
510 	}
511 	
512 	protected boolean isDataReady() {
513 		if (! checkSize()) {
514 			return false;
515 		}
516 
517 		for (int i = 0; i < this.essentials.length; i++) {
518 			if (this.entryArray[this.essentials[i]] == null) {
519 				return false;
520 			}
521 		}
522 		return true;
523 	}
524 
525 	private synchronized void setGeometryData() {
526 		if (isDataReady()) {
527 			boolean needsModel = needsModel();
528 			setData();
529 			
530 			if(needsModel) {
531 				clearGeometry();
532 				if (DEBUG) {
533 					System.out.println("modeling...");
534 				}
535 				model();
536 			} else {//if num of vertex is same as well as strip counts and contour counts is the same.
537 				if (DEBUG) {
538 					System.out.println("updating...");
539 				}
540 				update();
541 			}
542 		}
543 	}
544 	
545 	protected abstract boolean checkSize();
546 	protected abstract boolean needsModel();
547 	protected abstract void setData();
548 
549     private static Vector3f sV1 = new Vector3f();
550     private static Vector3f sV2 = new Vector3f();
551     private static Vector3f sNormal = new Vector3f();
552     protected static Vector3f geomCalcTriNormals(Point3f point3f, Point3f point3f1, Point3f point3f2) {
553         sV1.sub(point3f1, point3f);
554         sV2.sub(point3f2, point3f);
555         sNormal.cross(sV1, sV2);
556         sNormal.normalize();
557         return sNormal;
558     }
559 }
560