View Javadoc
1   /*
2    * Created on 2005-01-06
3    *
4    * wersja 1.6:
5    *  - refactoring kodu createBox
6    *  - obsługa współrzędnych tekstur przy multiteksturingu
7    */
8   package org.sourceforge.jvb3d.Loader;
9   
10  import java.util.LinkedList;
11  import java.util.List;
12  import java.util.Vector;
13  
14  import javax.media.j3d.BoundingBox;
15  import javax.vecmath.Point2f;
16  import javax.vecmath.Point3d;
17  import javax.vecmath.TexCoord2f;
18  import javax.vecmath.Vector3d;
19  
20  import com.sun.j3d.utils.geometry.GeometryInfo;
21  import com.sun.j3d.utils.geometry.NormalGenerator;
22  
23  /***
24   * @author Łukasz Krzyżak
25   * 
26   * klasa odpowiada za tworzenie subpowierzchni i różne operacje na jej geometrii
27   */
28  public class SubSurface {
29  	protected class TextureCoordinates {
30  		TexCoord2f[] textureCoords = new TexCoord2f[8];
31  
32  		int textureLevel = 0;
33  
34  		TextureCoordinates(int texNo) {
35  			textureLevel = texNo;
36  		}
37  	}
38  
39  	protected Point3d[] vertices = new Point3d[4];
40  
41  	protected List<TextureCoordinates> textureCoords = new LinkedList<TextureCoordinates>();
42  
43  	BoundingBox surfaceBounds = null;
44  
45  	static final double wallDefaultWidth = 0.01;
46  
47  	static final double minSurfaceWidth = 0.01;
48  
49  	/***
50  	 * tworzy nową podpowierzchnię na podstawie tablicy 4 punktów
51  	 * 
52  	 * @param vert
53  	 *            tablica współrzędnych punktów
54  	 */
55  	public SubSurface(Point3d[] vert) {
56  		for (int i = 0; i < 4; i++) {
57  			vertices[i] = vert[i];
58  		}
59  
60  		Point3d[] corners = getBoundingCorners(vert);
61  		surfaceBounds = new BoundingBox(corners[0], corners[1]);
62  	}
63  
64  	private Point3d[] getBoundingCorners(Point3d[] vertex) {
65  		Point3d[] coords = new Point3d[2];
66  
67  		double minX = vertex[0].x, minY = vertex[0].y, minZ = vertex[0].z, maxX = vertex[0].x, maxY = vertex[0].y, maxZ = vertex[0].z;
68  
69  		for (int i = 0; i < 4; i++) {
70  			if (vertex[i].x < minX)
71  				minX = vertex[i].x;
72  			if (vertex[i].x > maxX)
73  				maxX = vertex[i].x;
74  			if (vertex[i].y < minY)
75  				minY = vertex[i].y;
76  			if (vertex[i].y > maxY)
77  				maxY = vertex[i].y;
78  			if (vertex[i].z < minZ)
79  				minZ = vertex[i].z;
80  			if (vertex[i].z > maxZ)
81  				maxZ = vertex[i].z;
82  		}
83  
84  		coords[0] = new Point3d(minX, minY, minZ);
85  		coords[1] = new Point3d(maxX, maxY, maxZ);
86  		return coords;
87  	}
88  
89  	/***
90  	 * tworzy nową subpowierzchnię na podstawie podanych wierzchołków
91  	 * 
92  	 * @param p0
93  	 *            lewy górny
94  	 * @param p1
95  	 *            prawy górny
96  	 * @param p2
97  	 *            prawy dolny
98  	 * @param p3
99  	 *            lewy dolny
100 	 */
101 	public SubSurface(Point3d p0, Point3d p1, Point3d p2, Point3d p3) {
102 		vertices[0] = p0;
103 		vertices[1] = p1;
104 		vertices[2] = p2;
105 		vertices[3] = p3;
106 
107 		surfaceBounds = new BoundingBox(vertices[0], vertices[1]);
108 		surfaceBounds.combine(vertices[2]);
109 		surfaceBounds.combine(vertices[3]);
110 	}
111 
112 	void setTextureCoordinate(int vertexNumber, Point2f texCoord,
113 			int textureNumber) {
114 
115 		if (textureCoords.size() < textureNumber + 1
116 				|| textureCoords.get(textureNumber) == null) {
117 			textureCoords.add(new TextureCoordinates(textureNumber));
118 		}
119 
120 		for (TextureCoordinates coords : textureCoords)
121 			if (coords.textureLevel == textureNumber)
122 				coords.textureCoords[vertexNumber] = new TexCoord2f(texCoord.x,
123 						texCoord.y);
124 	}
125 
126 	Point3d getVertex(int vertexNumber) {
127 		return vertices[vertexNumber];
128 	}
129 
130 	/***
131 	 * testuje czy podany punkt znajduje sie na powierzchni
132 	 * 
133 	 * @param point
134 	 *            współrzędne punktu
135 	 * @return czy punktjest na powierzchni
136 	 */
137 	boolean isInside(Point3d point) {
138 		return surfaceBounds.intersect(point);
139 	}
140 
141 	/***
142 	 * dzieli podpowierzchnię tak, aby powstał otwór
143 	 * 
144 	 * @param vert
145 	 *            współrzędne dziury
146 	 * @return tablica nowych podpowierzchni
147 	 */
148 	SubSurface[] cutOpening(Point3d[] vert) {
149 		Vector3d surfEdge = null, translation = null;
150 		double transLength = 0;
151 		Point3d[] splitPoints = new Point3d[4];
152 		List<SubSurface> subsurfaces = new Vector<SubSurface>(4);
153 
154 		surfEdge = new Vector3d(vertices[1]);
155 		surfEdge.sub(vertices[0]);
156 		surfEdge.normalize();
157 		translation = new Vector3d(selectClosest(vertices[0], vert));
158 		translation.sub(vertices[0]);
159 		transLength = translation.dot(surfEdge);
160 		translation.scale(transLength, surfEdge);
161 
162 		splitPoints[0] = new Point3d(vertices[0]);
163 		splitPoints[0].add(translation);
164 
165 		surfEdge = new Vector3d(vertices[0]);
166 		surfEdge.sub(vertices[1]);
167 		surfEdge.normalize();
168 		translation = new Vector3d(selectClosest(vertices[1], vert));
169 		translation.sub(vertices[1]);
170 		transLength = translation.dot(surfEdge);
171 		translation.scale(transLength, surfEdge);
172 
173 		splitPoints[1] = new Point3d(vertices[1]);
174 		splitPoints[1].add(translation);
175 
176 		surfEdge = new Vector3d(vertices[3]);
177 		surfEdge.sub(vertices[2]);
178 		surfEdge.normalize();
179 		translation = new Vector3d(selectClosest(vertices[2], vert));
180 		translation.sub(vertices[2]);
181 		transLength = translation.dot(surfEdge);
182 		translation.scale(transLength, surfEdge);
183 
184 		splitPoints[2] = new Point3d(vertices[2]);
185 		splitPoints[2].add(translation);
186 
187 		surfEdge = new Vector3d(vertices[2]);
188 		surfEdge.sub(vertices[3]);
189 		surfEdge.normalize();
190 		translation = new Vector3d(selectClosest(vertices[3], vert));
191 		translation.sub(vertices[3]);
192 		transLength = translation.dot(surfEdge);
193 		translation.scale(transLength, surfEdge);
194 
195 		splitPoints[3] = new Point3d(vertices[3]);
196 		splitPoints[3].add(translation);
197 
198 		if (vertices[0].distance(splitPoints[0]) > minSurfaceWidth) {
199 			subsurfaces.add(new SubSurface(vertices[0], splitPoints[0],
200 					splitPoints[3], vertices[3]));
201 		}
202 
203 		if (splitPoints[0].distance(vert[0]) > minSurfaceWidth
204 				&& splitPoints[0].distance(splitPoints[1]) > minSurfaceWidth) {
205 			subsurfaces.add(new SubSurface(splitPoints[0], splitPoints[1],
206 					selectClosest(vertices[1], vert), selectClosest(
207 							vertices[0], vert)));
208 		}
209 
210 		if (splitPoints[3].distance(vert[3]) > minSurfaceWidth
211 				&& splitPoints[3].distance(splitPoints[2]) > minSurfaceWidth) {
212 			subsurfaces.add(new SubSurface(selectClosest(vertices[3], vert),
213 					selectClosest(vertices[2], vert), splitPoints[2],
214 					splitPoints[3]));
215 		}
216 
217 		if (splitPoints[1].distance(vertices[1]) > minSurfaceWidth
218 				&& vertices[1].distance(vertices[2]) > minSurfaceWidth) {
219 			subsurfaces.add(new SubSurface(splitPoints[1], vertices[1],
220 					vertices[2], splitPoints[2]));
221 		}
222 
223 		SubSurface[] result = new SubSurface[3];
224 		result = subsurfaces.toArray(result);
225 		return result;
226 	}
227 
228 	private Point3d selectClosest(Point3d source, Point3d[] vertex) {
229 		Point3d closest = vertex[0];
230 		double dist = source.distanceSquared(vertex[0]);
231 
232 		for (int i = 1; i < 4; i++)
233 			if (source.distanceSquared(vertex[i]) < dist) {
234 				dist = source.distanceSquared(vertex[i]);
235 				closest = vertex[i];
236 			}
237 
238 		return closest;
239 	}
240 
241 	/***
242 	 * przekształca płaską powierzchnię w trójwymiarowy obiekt
243 	 * 
244 	 * @return tablica quadów opisujących obiekt
245 	 */
246 	GeometryInfo[] createBox() {
247 		GeometryInfo geometryArray[] = new GeometryInfo[6];
248 
249 		Point3d[] wallCoords = generateBoxCoords(vertices, wallDefaultWidth);
250 
251 		for (int i = 0; i < 6; i++)
252 			geometryArray[i] = new GeometryInfo(GeometryInfo.QUAD_ARRAY);
253 
254 		geometryArray[0].setCoordinates(generateQuadCoords(new int[] { 0, 1, 2,
255 				3 }, wallCoords));
256 		geometryArray[1].setCoordinates(generateQuadCoords(new int[] { 7, 6, 5,
257 				4 }, wallCoords));
258 		geometryArray[2].setCoordinates(generateQuadCoords(new int[] { 0, 3, 7,
259 				4 }, wallCoords));
260 		geometryArray[3].setCoordinates(generateQuadCoords(new int[] { 5, 6, 2,
261 				1 }, wallCoords));
262 		geometryArray[4].setCoordinates(generateQuadCoords(new int[] { 0, 4, 5,
263 				1 }, wallCoords));
264 		geometryArray[5].setCoordinates(generateQuadCoords(new int[] { 2, 6, 7,
265 				3 }, wallCoords));
266 
267 		if (textureCoords.size() > 0) {
268 			geometryArray[0]
269 					.setTextureCoordinateParams(textureCoords.size(), 2);
270 			geometryArray[1]
271 					.setTextureCoordinateParams(textureCoords.size(), 2);
272 			for (TextureCoordinates coords : textureCoords) {
273 				geometryArray[0].setTextureCoordinates(coords.textureLevel,
274 						generateTexCoords(new int[] { 0, 1, 2, 3 },
275 								coords.textureCoords));
276 				geometryArray[1].setTextureCoordinates(coords.textureLevel,
277 						generateTexCoords(new int[] { 7, 6, 5, 4 },
278 								coords.textureCoords));
279 			}
280 		}
281 
282 		geometryArray[0].compact();
283 		NormalGenerator ng = new NormalGenerator();
284 		for (int i = 0; i < 6; i++)
285 			ng.generateNormals(geometryArray[i]);
286 		return geometryArray;
287 	}
288 
289 	/***
290 	 * kopiuje dane o wsp tekstury z tablicy źródłowej w podanej kolejności
291 	 * 
292 	 * @param source
293 	 *            kolejność wartości w tablicy docelowej
294 	 * @param sourceArray
295 	 *            tablica źródłowa
296 	 * @return tablica docelowa
297 	 */
298 	private TexCoord2f[] generateTexCoords(int[] source,
299 			TexCoord2f[] sourceArray) {
300 		TexCoord2f[] coords = new TexCoord2f[4];
301 
302 		for (int i = 0; i < 4; i++)
303 			coords[i] = new TexCoord2f(sourceArray[source[i]]);
304 
305 		return coords;
306 	}
307 
308 	/***
309 	 * kopiuje dane o wsp wierzchołków z tablicy źródłowej w podanej kolejności
310 	 * 
311 	 * @param source
312 	 *            kolejność wartości w tablicy docelowej
313 	 * @param sourceArray
314 	 *            tablica źródłowa
315 	 * @return tablica docelowa
316 	 */
317 	private Point3d[] generateQuadCoords(int[] source, Point3d[] sourceArray) {
318 		Point3d[] coords = new Point3d[4];
319 
320 		for (int i = 0; i < 4; i++)
321 			coords[i] = new Point3d(sourceArray[source[i]]);
322 
323 		return coords;
324 	}
325 
326 	/***
327 	 * na podstawie podanych wierzchołków oraz grubości tworzy koordynaty
328 	 * wierzchołków boxa o podanej grubości
329 	 * 
330 	 * @param flatCoords
331 	 *            koordynaty płaskiego obszaru
332 	 * @param depth
333 	 *            grubość utworzonego pudełka
334 	 * @return koordynaty wierzchołków boxa
335 	 */
336 	private Point3d[] generateBoxCoords(Point3d[] flatCoords, double depth) {
337 		//		1 - znajdujemy wektory dwóch sąsiadujących krawędzi
338 		Point3d tempPoint = new Point3d(flatCoords[0]);
339 		tempPoint.sub(vertices[1]);
340 		Vector3d edge1 = new Vector3d(tempPoint);
341 		tempPoint.set(vertices[0]);
342 		tempPoint.sub(vertices[2]);
343 		Vector3d edge2 = new Vector3d(tempPoint);
344 
345 		Vector3d normal = new Vector3d();
346 		normal.cross(edge1, edge2);
347 		normal.normalize();
348 
349 		//		2 - tworzymy komplet punktów przesuniętych o wektor
350 		Point3d[] wallCoords = new Point3d[8];
351 
352 		for (int i = 0; i < 4; i++) {
353 			wallCoords[i] = new Point3d(flatCoords[i]);
354 			wallCoords[i + 4] = new Point3d(flatCoords[i]);
355 		}
356 
357 		Vector3d wallScale = new Vector3d(normal);
358 		wallScale.scale(depth);
359 
360 		for (int i = 0; i < 4; i++) {
361 			wallCoords[i].add(wallScale);
362 			wallCoords[i + 4].sub(wallScale);
363 		}
364 
365 		return wallCoords;
366 
367 	}
368 }