001: /*
002: * $RCSfile: NormalGenerator.java,v $
003: *
004: * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * - Redistribution of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * - Redistribution in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * Neither the name of Sun Microsystems, Inc. or the names of
019: * contributors may be used to endorse or promote products derived
020: * from this software without specific prior written permission.
021: *
022: * This software is provided "AS IS," without a warranty of any
023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
034: * POSSIBILITY OF SUCH DAMAGES.
035: *
036: * You acknowledge that this software is not designed, licensed or
037: * intended for use in the design, construction, operation or
038: * maintenance of any nuclear facility.
039: *
040: * $Revision: 1.4 $
041: * $Date: 2007/02/09 17:20:19 $
042: * $State: Exp $
043: */
044:
045: package com.sun.j3d.utils.geometry;
046:
047: import com.sun.j3d.utils.geometry.GeometryInfo;
048: import com.sun.j3d.utils.geometry.EdgeTable;
049: import java.util.ArrayList;
050: import javax.vecmath.Vector3f;
051: import javax.vecmath.Point3f;
052:
053: /**
054: * The NormalGenerator utility will calculate and fill in the normals
055: * of a GeometryInfo object. The calculated normals are estimated based
056: * on an analysis of the indexed coordinate information. If your data
057: * isn't indexed, index lists will be created.<p>
058: * <p>
059: * If two (or more) triangles in the model share the same coordinate
060: * index then the normal generator will attempt to generate one normal
061: * for the vertex, resulting in a "smooth" looking surface. If two
062: * coordinates don't have the same index then they will have two
063: * separate normals, even if they have the same position. This will
064: * result in a "crease" in your object. If you suspect that your
065: * data isn't properly indexed, call GeometryInfo.recomputeIndexes(). <p>
066: * <p>
067: * Of course, sometimes your model *has* a crease in it. That's what
068: * creaseAngle is. If two triangles' normals differ by more than
069: * creaseAngle, then the vertex will get two separate normals, creating a
070: * discontinuous crease in the model. This is perfect for the edge
071: * of a table or the corner of a cube, for instance.
072: */
073:
074: public class NormalGenerator {
075:
076: private double creaseAngle;
077: private Vector3f facetNorms[];
078: private ArrayList tally;
079: private GeometryInfo gi;
080: private int coordInds[];
081: private int normalInds[];
082: private int colorInds[];
083: private int texInds[][];
084: private int stripCounts[];
085: private static long t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0;
086: private Triangulator tr = null;
087: private int numTexSets;
088:
089: // 0 - No debug info
090: // 1 - Facet Normals
091: // 2 - Connection info
092: // 4 - Normals
093: // 8 - StripCounts
094: // 16 - Timing
095: // 32 - Hard edges
096: // 64 - Coordinate and normal indices
097: // 128 - Vertex normal calculation info
098: private static final int DEBUG = 0;
099:
100: // Calculate the normal of each triangle in the list by finding
101: // the cross product
102: private void calculatefacetNorms() {
103: Point3f coordinates[] = gi.getCoordinates();
104: facetNorms = new Vector3f[coordInds.length / 3];
105: Vector3f a = new Vector3f();
106: Vector3f b = new Vector3f();
107: if ((DEBUG & 1) != 0)
108: System.out.println("Facet normals:");
109:
110: if (gi.getOldPrim() != gi.QUAD_ARRAY) {
111: for (int t = 0; t < coordInds.length; t += 3) {
112: a.sub(coordinates[coordInds[t + 2]],
113: coordinates[coordInds[t + 1]]);
114: b.sub(coordinates[coordInds[t + 0]],
115: coordinates[coordInds[t + 1]]);
116: facetNorms[t / 3] = new Vector3f();
117: facetNorms[t / 3].cross(a, b);
118: facetNorms[t / 3].normalize();
119:
120: if (Float.isNaN(facetNorms[t / 3].x)) {
121: // Normal isn't valid
122: facetNorms[t / 3].x = 1.0f;
123: facetNorms[t / 3].y = facetNorms[t / 3].z = 0.0f;
124: }
125: if ((DEBUG & 1) != 0) {
126: System.out.println(" " + (t / 3) + " "
127: + facetNorms[t / 3]);
128: }
129: }
130: } else {
131: // For quads, the facet normal of both triangles is the cross
132: // product of the two vectors that make an 'X' across the quad.
133: for (int t = 0; t < coordInds.length; t += 6) {
134: a.sub(coordinates[coordInds[t + 2]],
135: coordinates[coordInds[t + 0]]);
136: b.sub(coordinates[coordInds[t + 5]],
137: coordinates[coordInds[t + 1]]);
138: facetNorms[t / 3] = new Vector3f();
139: facetNorms[t / 3].cross(a, b);
140: facetNorms[t / 3].normalize();
141:
142: if (Float.isNaN(facetNorms[t / 3].x)) {
143: // Normal isn't valid
144: facetNorms[t / 3].x = 1.0f;
145: facetNorms[t / 3].y = facetNorms[t / 3].z = 0.0f;
146: }
147:
148: // Second triangle of quad
149: facetNorms[t / 3 + 1] = new Vector3f(facetNorms[t / 3]);
150:
151: if ((DEBUG & 1) != 0) {
152: System.out.println(" " + (t / 3) + "&"
153: + (t / 3 + 1) + " " + facetNorms[t / 3]);
154: }
155: }
156: }
157: } // End of calculatefacetNorms
158:
159: // The vertex normals will be calculated by averaging the facet normals
160: // of groups of triangles sharing the vertex. At the end of this routine
161: // the groups of coordinate indexes will all be made, and the normal
162: // indices will point to these groups.
163: //
164: // The routine works by going through each vertex of each triangle.
165: // Starting at a triangle, we see if the vertex normal can be shared
166: // with the neighbor triangle (their facet normals differ by less than
167: // creaseAngle). If they can be shared, then we move from that triangle
168: // to the next and the next in a circle around the vertex.
169: //
170: // If we hit the edge of the model or a Hard Edge (crease) then we stop
171: // and then try going around the vertex in the other direction.
172: //
173: // Each time we step from one triangle to the next around the center
174: // vertex, the triangle is added to the group of triangles whose normals
175: // will be averaged to make the vertex normal.
176: //
177: // Returns the largest number of triangles that share a single normal.
178: //
179: private int createHardEdges() {
180: EdgeTable et = new EdgeTable(coordInds);
181: tally = new ArrayList();
182: int normalMap[] = new int[coordInds.length];
183: int maxShare = 1;
184: float cosine;
185: boolean smooth;
186: float threshold = (float) Math.cos(creaseAngle);
187: boolean goingRight;
188:
189: // Set Normal Indices array values to a flag
190: for (int c = 0; c < coordInds.length; c++)
191: normalMap[c] = Integer.MAX_VALUE;
192:
193: // Cycle through each vertex
194: for (int c = 0; c < coordInds.length; c++) {
195: // See if this vertex's normal has already been done
196: if (normalMap[c] == Integer.MAX_VALUE) {
197: if ((DEBUG & 32) != 0) {
198: System.out.println("Coordinate Index " + c
199: + ": vertex " + coordInds[c]);
200: }
201: // Create a list of vertices used for calculating this normal
202: ArrayList sharers = new ArrayList();
203: tally.add(sharers);
204: // Put this coordinate in the list
205: sharers.add(new Integer(c));
206: // Point this coordinate's index at its list
207: normalMap[c] = tally.size() - 1;
208:
209: // First do right edge
210: goingRight = true;
211: Edge edge = new Edge(coordInds[c],
212: coordInds[(c + 1) % 3 == 0 ? c - 2 : c + 1]);
213: if ((DEBUG & 32) != 0)
214: System.out.println(" Right edge: " + edge);
215:
216: // This is how we'll know we've gone all the way around
217: int endVertex = coordInds[c % 3 == 0 ? c + 2 : c - 1];
218:
219: // Start at current triangle
220: int cur = c;
221:
222: // Proceed from one triangle to the next
223: do {
224: // Look up edge in Edge Table to find neighbor triangle
225: Integer tableVal = et.get(edge.v2, edge.v1);
226: if ((DEBUG & 32) != 0) {
227: System.out.println(" Search Edge: "
228: + (new Edge(edge.v2, edge.v1)));
229: }
230:
231: // See if there is no triangle on the other side of this edge
232: if (tableVal == null) {
233: smooth = false;
234: if ((DEBUG & 32) != 0)
235: System.out
236: .println(" No neighboring triangle found.");
237: } else {
238:
239: int n = tableVal.intValue();
240: if ((DEBUG & 32) != 0) {
241: System.out
242: .println(" Table lookup result: "
243: + n
244: + " (vertex "
245: + coordInds[n] + ")");
246: System.out.print(" Triangles "
247: + (cur / 3) + " & " + (n / 3)
248: + ": ");
249: }
250:
251: cosine = facetNorms[cur / 3]
252: .dot(facetNorms[n / 3]);
253: smooth = cosine > threshold;
254: if (smooth) {
255: // The center coordinate (c) shares the same normal in these
256: // two triangles. Find that coordinate and set its index
257: // normalMap[n] = normalMap[cur];
258: int centerv = (((n + 1) % 3) == 0 ? n - 2
259: : n + 1);
260: if (coordInds[c] != coordInds[centerv]) {
261: centerv = ((n % 3) == 0 ? n + 2 : n - 1);
262: }
263:
264: if ((DEBUG & 32) != 0)
265: System.out.println("Smooth! Adding "
266: + centerv);
267:
268: if (normalMap[centerv] != Integer.MAX_VALUE) {
269: smooth = false;
270: if ((DEBUG & 32) != 0)
271: System.out
272: .println(" Error: Coordinate aleady has normal (bad data).");
273: } else {
274:
275: normalMap[centerv] = tally.size() - 1;
276:
277: // Consider this triangle's facet normal when calculating the
278: // vertex's normal
279: sharers.add(new Integer(centerv));
280: if (sharers.size() > maxShare)
281: maxShare = sharers.size();
282:
283: // Continue on around the vertex to the next triangle
284: cur = n;
285: if (goingRight)
286: edge.v2 = coordInds[cur];
287: else
288: edge.v1 = coordInds[cur];
289: }
290: } else if ((DEBUG & 32) != 0)
291: System.out.println("Hard Edge!");
292: }
293:
294: if (!smooth && goingRight) {
295:
296: // We've hit an impasse going right, so now try going left
297: // from the original triangle
298: goingRight = false;
299: smooth = true; // Trick do loop
300: cur = c; // Go back to original triangle
301:
302: edge = new Edge(coordInds[(c % 3) == 0 ? c + 2
303: : c - 1], coordInds[c]);
304: if ((DEBUG & 32) != 0)
305: System.out.println(" Left edge: " + edge);
306:
307: }
308:
309: } while (smooth
310: && ((goingRight && (edge.v2 != endVertex)) || !goingRight));
311:
312: if (((DEBUG & 32) != 0) && goingRight
313: && (edge.v2 == endVertex))
314: System.out.println(" Went all the way around!");
315: }
316: }
317:
318: if ((DEBUG & 32) != 0) {
319: System.out.println("Tally:");
320: for (int i = 0; i < tally.size(); i++) {
321: System.out.print(" " + i + ": ");
322: ArrayList sharers = (ArrayList) (tally.get(i));
323: for (int j = 0; j < sharers.size(); j++) {
324: System.out.print(" " + sharers.get(j));
325: }
326: System.out.println();
327: }
328:
329: System.out.println("Normal Indexes:");
330: for (int i = 0; i < normalMap.length; i++) {
331: System.out.println(" " + i + ": " + normalMap[i]);
332: }
333: }
334:
335: return maxShare;
336: } // End of createHardEdges
337:
338: // Now take all of the triangles who share a vertex (who have
339: // been grouped by the hard edge process) and average their facet
340: // normals to get the vertex normal
341: //
342: // This routine has something of a hack in it. We found that our
343: // method of breaking up data into individual triangles before
344: // calculating normals was causing a bug. If a polygon was broken
345: // into two triangles at a particular vertex, then that facet's
346: // normal would get averaged into the vertex normal *twice*,
347: // skewing the normal toward the decomposed facet. So what we did
348: // was to check for duplicate facet normals as we're averaging,
349: // not allowing the same facet normal to be counted twice.
350: //
351: // What should be done is to put the facets' normals into a separate,
352: // indexed, table. That way, to tell if two triangles have the
353: // same normal, we just need to compare indexes. This would speed up
354: // the process of checking for duplicates.
355: private void calculateVertexNormals(int maxShare) {
356: Vector3f normals[];
357: ArrayList sharers;
358: int triangle;
359: Vector3f fn[]; // Normals of facets joined by this vertex
360: int fnsize; // Number of elements currently ised in fn
361:
362: if (creaseAngle != 0.0) {
363: fn = new Vector3f[maxShare];
364: normals = new Vector3f[tally.size()];
365: normalInds = new int[coordInds.length];
366: for (int n = 0; n < tally.size(); n++) {
367: sharers = (ArrayList) (tally.get(n));
368: if ((DEBUG & 128) != 0) {
369: System.out.println(n + ": " + sharers.size()
370: + " triangles:");
371: }
372: fnsize = 0;
373: normals[n] = new Vector3f();
374: for (int t = 0; t < sharers.size(); t++) {
375: int v = ((Integer) sharers.get(t)).intValue();
376: // See if index removed by hard edge process
377: if (v != -1) {
378: triangle = v / 3;
379: if (!Float.isNaN(facetNorms[triangle].x)) {
380:
381: int f;
382: // Don't add the same facet normal twice
383: for (f = 0; f < fnsize; f++) {
384: if (fn[f].equals(facetNorms[triangle]))
385: break;
386: }
387:
388: normalInds[v] = n;
389: if (f == fnsize) {
390: // Didn't find this triangle's normal already in the list
391: normals[n].add(facetNorms[triangle]);
392: fn[fnsize++] = facetNorms[triangle];
393: } else if ((DEBUG & 128) != 0) {
394: System.out.println(" triangle " + t
395: + " ignored.");
396: }
397: }
398: }
399: }
400: normals[n].normalize();
401: if (Float.isNaN(normals[n].x)) {
402: // Normal isn't valid
403: normals[n].x = 1.0f;
404: normals[n].y = normals[n].z = 0.0f;
405: }
406: if ((DEBUG & 128) != 0) {
407: for (int t = 0; t < sharers.size(); t++) {
408: int v = ((Integer) sharers.get(t)).intValue();
409: if (v != -1) {
410: triangle = v / 3;
411: System.out.println(" "
412: + facetNorms[triangle]);
413: }
414: }
415: System.out.println(" Result: " + normals[n]);
416: System.out.println();
417: }
418: }
419: } else {
420: // This code renders the facet normals
421: normals = facetNorms;
422:
423: normalInds = new int[facetNorms.length * 3];
424: for (int i = 0; i < facetNorms.length; i++) {
425: normalInds[i * 3 + 0] = i;
426: normalInds[i * 3 + 1] = i;
427: normalInds[i * 3 + 2] = i;
428: }
429: }
430: gi.setNormals(normals);
431:
432: if ((DEBUG & 4) != 0) {
433: System.out.println("Normals:");
434: for (int i = 0; i < normals.length; i++) {
435: System.out.println(" " + i + " " + normals[i]);
436: }
437: System.out.println("Indices:");
438: for (int i = 0; i < normalInds.length; i++) {
439: System.out.println(" " + i + " " + normalInds[i]);
440: }
441: }
442: } // End of calculateVertexNormals
443:
444: // The original data was in quads and we converted it to triangles to
445: // calculate the normals. Now we are converting it back to quads.
446: // It's a very simple algorithm.
447: // Since both sub-triangles of a quad have the same facet normal,
448: // there should never be a hard edge down the middle of the quad.
449: // Therefore, the vertices of the shared edge of the two subtriangles
450: // should have the same normal index in both triangles.
451: private int[] triToQuadIndices(int oldList[]) {
452: if (oldList == null)
453: return null;
454:
455: int newList[] = new int[oldList.length / 6 * 4];
456: // index list to pass back
457: // Cycle through each pair of triangles and put them together
458: for (int q = 0; q < oldList.length / 6; q++) {
459: newList[q * 4 + 0] = oldList[q * 6 + 0];
460: newList[q * 4 + 1] = oldList[q * 6 + 1];
461: newList[q * 4 + 2] = oldList[q * 6 + 2];
462: newList[q * 4 + 3] = oldList[q * 6 + 5];
463: }
464:
465: return newList;
466: } // End of triToQuadIndices
467:
468: // The original data was in quads. We converted it to triangles to
469: // calculate normals. Now we need to convert it back to quads.
470: private void convertTriToQuad(GeometryInfo geom) {
471: // Create the new arrays
472: geom.setCoordinateIndices(triToQuadIndices(geom
473: .getCoordinateIndices()));
474: geom.setColorIndices(triToQuadIndices(geom.getColorIndices()));
475: geom
476: .setNormalIndices(triToQuadIndices(geom
477: .getNormalIndices()));
478: int num = geom.getTexCoordSetCount();
479: for (int i = 0; i < num; i++) {
480: geom.setTextureCoordinateIndices(i, triToQuadIndices(geom
481: .getTextureCoordinateIndices(i)));
482: }
483: geom.setPrimitive(gi.QUAD_ARRAY);
484: } // End of convertTriToQuad()
485:
486: // The original data was in fans and we converted it to triangles to
487: // calculate the normals. Now we are converting it back to fans.
488: // We have already calculated the new stripCounts, so now we need
489: // to change the index lists so they match up with the stripCounts.
490: // It's a very simple algorithm. The paramater oldList is the
491: // index list being compressed back into fans (could be coordinate,
492: // color, normal, or texCoord indices) and numVerts is the pre-
493: // calculated total of all entries of the stripCounts array.
494: private int[] triToFanIndices(int sc[], int oldList[], int numVerts) {
495: if (oldList == null)
496: return null;
497:
498: int newList[] = new int[numVerts]; // index list to pass back
499: int vert1 = 0; // 1st vertex of triangle
500: int n = 0; // index into newList
501: // Cycle through each fan in the new list
502: for (int f = 0; f < sc.length; f++) {
503: // Copy entire first triangle into new array
504: newList[n++] = oldList[vert1++];
505: newList[n++] = oldList[vert1++];
506: newList[n++] = oldList[vert1++];
507: // Each additional triangle in the fan only needs one vertex
508: for (int t = 3; t < sc[f]; t++) {
509: newList[n++] = oldList[vert1 + 2];
510: vert1 += 3;
511: }
512: }
513: return newList;
514: } // End of triToFanIndices
515:
516: //
517: // The original data was in fans. We converted it to triangles to
518: // calculate normals. Now we need to convert it back to fans.
519: // The hard part is that, if we found a hard edge in the middle of
520: // a fan, we need to split the fan into two. To tell if there's
521: // a hard edge there, we compare the normal indices of both
522: // vertices comprising the edge.
523: private void convertTriToFan(GeometryInfo geom,
524: int oldStripCounts[]) {
525: int ni[] = geom.getNormalIndices();
526:
527: //
528: // Calculate new stripCounts array
529: //
530: int tri = 0; // Which triangle currently being converted
531: ArrayList newStripCounts;
532: newStripCounts = new ArrayList(oldStripCounts.length + 100);
533:
534: // Use the original stripCounts array
535: for (int f = 0; f < oldStripCounts.length; f++) {
536: int stripCount = 3;
537:
538: // Cycle through each triangle in the fan, comparing to the
539: // next triangle in the fan. Compare the normal indices of
540: // both vertices of the edge to see if the two triangles
541: // can be mated
542: for (int t = 0; t < oldStripCounts[f] - 3; t++) {
543: // The first vertex of this triangle must match the first
544: // vertex of the next, AND the third vertex of this
545: // triangle must match the second vertex of the next.
546: if ((ni[tri * 3] == ni[(tri + 1) * 3])
547: && (ni[tri * 3 + 2] == ni[(tri + 1) * 3 + 1])) {
548: // OK to extend fan
549: stripCount++;
550: } else {
551: // hard edge within fan
552: newStripCounts.add(new Integer(stripCount));
553: stripCount = 3;
554: }
555: tri++;
556: }
557: tri++;
558: newStripCounts.add(new Integer(stripCount));
559: }
560:
561: // Convert from ArrayList to int[]
562: int sc[] = new int[newStripCounts.size()];
563: for (int i = 0; i < sc.length; i++)
564: sc[i] = ((Integer) newStripCounts.get(i)).intValue();
565: newStripCounts = null;
566:
567: //
568: // Change the index lists so they match up with the new stripCounts
569: //
570:
571: // See how many vertices we'll need
572: int c = 0;
573: for (int i = 0; i < sc.length; i++)
574: c += sc[i];
575:
576: // Create the new arrays
577: geom.setCoordinateIndices(triToFanIndices(sc, geom
578: .getCoordinateIndices(), c));
579: geom.setColorIndices(triToFanIndices(sc,
580: geom.getColorIndices(), c));
581: geom.setNormalIndices(triToFanIndices(sc, geom
582: .getNormalIndices(), c));
583: int num = geom.getTexCoordSetCount();
584: for (int i = 0; i < num; i++) {
585: geom.setTextureCoordinateIndices(i, triToFanIndices(sc,
586: geom.getTextureCoordinateIndices(i), c));
587: }
588:
589: if ((DEBUG & 8) != 0) {
590: System.out.print("Old stripCounts:");
591: for (int i = 0; i < oldStripCounts.length; i++) {
592: System.out.print(" " + oldStripCounts[i]);
593: }
594: System.out.println();
595: System.out.print("New stripCounts:");
596: for (int i = 0; i < sc.length; i++) {
597: System.out.print(" " + sc[i]);
598: }
599: System.out.println();
600: }
601:
602: geom.setStripCounts(sc);
603: geom.setPrimitive(gi.TRIANGLE_FAN_ARRAY);
604: } // End of convertTriToFan()
605:
606: // The original data was in strips and we converted it to triangles to
607: // calculate the normals. Now we are converting it back to strips.
608: // We have already calculated the new stripCounts, so now we need
609: // to change the index lists so they match up with the stripCounts.
610: // It's a very simple algorithm. The paramater oldList is the
611: // index list being compressed back into strips (could be coordinate,
612: // color, normal, or texCoord indices) and numVerts is the pre-
613: // calculated total of all entries of the stripCounts array.
614: private int[] triToStripIndices(int sc[], int oldList[],
615: int numVerts) {
616: if (oldList == null)
617: return null;
618:
619: int newList[] = new int[numVerts]; // index list to pass back
620: int vert1 = 0; // 1st vertex of triangle
621: int n = 0; // index into newList
622: // Cycle through each strip in the new list
623: for (int f = 0; f < sc.length; f++) {
624: // Copy entire first triangle into new array
625: newList[n++] = oldList[vert1++];
626: newList[n++] = oldList[vert1++];
627: newList[n++] = oldList[vert1++];
628: // Each additional triangle in the fan only needs one vertex
629: for (int t = 3; t < sc[f]; t++) {
630: // Every other triangle has been reversed to preserve winding
631: newList[n++] = oldList[vert1 + 2 - (t % 2)];
632: vert1 += 3;
633: }
634: }
635: return newList;
636: } // End of triToStripIndices
637:
638: private void convertTriToStrip(GeometryInfo geom,
639: int oldStripCounts[]) {
640: int ni[] = geom.getNormalIndices();
641:
642: //
643: // Calculate new stripCounts array
644: //
645: int tri = 0; // Which triangle currently being converted
646: ArrayList newStripCounts;
647: newStripCounts = new ArrayList(oldStripCounts.length + 100);
648:
649: // Use the original stripCounts array
650: for (int f = 0; f < oldStripCounts.length; f++) {
651: int stripCount = 3;
652:
653: // Cycle through each triangle in the strip, comparing to the
654: // next triangle in the strip. Compare the normal indices of
655: // both vertices of the edge to see if the two triangles
656: // can be mated.
657: for (int t = 0; t < oldStripCounts[f] - 3; t++) {
658: // Every other triangle has been reversed to preserve winding
659: if (t % 2 == 0) {
660: // The middle vertex of this triangle needs to match the
661: // first vertex of the next, AND the third vertices of
662: // the two triangles must match
663: if ((ni[tri * 3 + 1] == ni[(tri + 1) * 3])
664: && (ni[tri * 3 + 2] == ni[(tri + 1) * 3 + 2])) {
665: // OK to extend strip
666: stripCount++;
667: } else {
668: // hard edge within strip
669: newStripCounts.add(new Integer(stripCount));
670: stripCount = 3;
671:
672: // Can't start a new strip on an odd edge so output
673: // isolated triangle
674: if (t < oldStripCounts[f] - 4) {
675: newStripCounts.add(new Integer(3));
676: t++;
677: }
678: }
679: } else {
680: // The middle vertex of this triangle must match the middle
681: // vertex of the next, AND the third vertex of this triangle
682: // must match the first vertex of the next
683: if ((ni[tri * 3 + 1] == ni[(tri + 1) * 3 + 1])
684: && (ni[tri * 3 + 2] == ni[(tri + 1) * 3])) {
685: // OK to extend strip
686: stripCount++;
687: } else {
688: // hard edge within strip
689: newStripCounts.add(new Integer(stripCount));
690: stripCount = 3;
691: }
692: }
693: tri++;
694: }
695: tri++;
696: newStripCounts.add(new Integer(stripCount));
697: }
698:
699: // Convert from ArrayList to int[]
700: int sc[] = new int[newStripCounts.size()];
701: for (int i = 0; i < sc.length; i++)
702: sc[i] = ((Integer) newStripCounts.get(i)).intValue();
703: newStripCounts = null;
704:
705: //
706: // Change the index lists so they match up with the new stripCounts
707: //
708:
709: // See how many vertices we'll need
710: int c = 0;
711: for (int i = 0; i < sc.length; i++)
712: c += sc[i];
713:
714: // Create the new arrays
715: geom.setCoordinateIndices(triToStripIndices(sc, geom
716: .getCoordinateIndices(), c));
717: geom.setColorIndices(triToStripIndices(sc, geom
718: .getColorIndices(), c));
719: geom.setNormalIndices(triToStripIndices(sc, geom
720: .getNormalIndices(), c));
721: int num = geom.getTexCoordSetCount();
722: for (int i = 0; i < num; i++) {
723: geom.setTextureCoordinateIndices(i, triToStripIndices(sc,
724: geom.getTextureCoordinateIndices(i), c));
725: }
726:
727: if ((DEBUG & 8) != 0) {
728: System.out.print("Old stripCounts:");
729: for (int i = 0; i < oldStripCounts.length; i++) {
730: System.out.print(" " + oldStripCounts[i]);
731: }
732: System.out.println();
733: System.out.print("New stripCounts:");
734: for (int i = 0; i < sc.length; i++) {
735: System.out.print(" " + sc[i]);
736: }
737: System.out.println();
738: }
739:
740: geom.setStripCounts(sc);
741: geom.setPrimitive(gi.TRIANGLE_STRIP_ARRAY);
742: }// End of convertTriToStrip()
743:
744: /**
745: * Used when the user calls the NormalGenerator and not
746: * the Stripifier or Triangulator. We had to convert
747: * the user's data to indexed triangles before we could
748: * generate normals, so now we need to switch back to
749: * the original format.
750: */
751: void convertBackToOldPrim(GeometryInfo geom, int oldPrim,
752: int oldStripCounts[]) {
753: if (oldPrim == geom.TRIANGLE_ARRAY)
754: return;
755:
756: switch (oldPrim) {
757: case GeometryInfo.QUAD_ARRAY:
758: convertTriToQuad(geom);
759: break;
760: case GeometryInfo.TRIANGLE_FAN_ARRAY:
761: convertTriToFan(geom, oldStripCounts);
762: break;
763: case GeometryInfo.TRIANGLE_STRIP_ARRAY:
764: convertTriToStrip(geom, oldStripCounts);
765: break;
766: }
767:
768: if ((DEBUG & 64) != 0) {
769: System.out
770: .println("Coordinate and normal indices (original):");
771: for (int i = 0; i < coordInds.length; i++) {
772: System.out.println(i + " " + coordInds[i] + " "
773: + normalInds[i]);
774: }
775: }
776: } // End of convertBackToOldPrim
777:
778: /**
779: * Generate normals for the GeometryInfo object. If the GeometryInfo
780: * object didn't previously contain indexed data, indexes are made
781: * by collapsing identical positions into a single index. Any
782: * normal information previously contained in the GeometryInfo
783: * object is lost. Strips and Fans are converted into individual
784: * triangles for Normal generation, but are stitched back together
785: * if GeometryInfo.getGeometryArray() (or getIndexedGeometryArray())
786: * is called without stripifying first.
787: */
788: public void generateNormals(GeometryInfo geom) {
789: gi = geom;
790: gi.setNormals((Vector3f[]) null);
791: gi.setNormalIndices(null);
792:
793: long time = 0L;
794: if ((DEBUG & 16) != 0) {
795: time = System.currentTimeMillis();
796: }
797:
798: if (gi.getPrimitive() == gi.POLYGON_ARRAY) {
799: if (tr == null)
800: tr = new Triangulator();
801: tr.triangulate(gi);
802: } else {
803: // NOTE: We should calculate facet normals before converting to
804: // triangles.
805: gi.rememberOldPrim();
806: gi.convertToIndexedTriangles();
807: }
808:
809: // Cache some of the GeometryInfo fields
810: coordInds = gi.getCoordinateIndices();
811: colorInds = gi.getColorIndices();
812: normalInds = gi.getNormalIndices();
813: numTexSets = gi.getTexCoordSetCount();
814: texInds = new int[numTexSets][];
815: for (int i = 0; i < numTexSets; i++) {
816: texInds[i] = gi.getTextureCoordinateIndices(i);
817: }
818: stripCounts = gi.getStripCounts();
819:
820: if ((DEBUG & 16) != 0) {
821: t1 += System.currentTimeMillis() - time;
822: System.out.println("Convert to triangles: " + t1 + " ms");
823: time = System.currentTimeMillis();
824: }
825:
826: calculatefacetNorms();
827: if ((DEBUG & 16) != 0) {
828: t2 += System.currentTimeMillis() - time;
829: System.out
830: .println("Calculate Facet Normals: " + t2 + " ms");
831: time = System.currentTimeMillis();
832: }
833:
834: int maxShare = createHardEdges();
835: if ((DEBUG & 16) != 0) {
836: t3 += System.currentTimeMillis() - time;
837: System.out.println("Hard Edges: " + t3 + " ms");
838: time = System.currentTimeMillis();
839: }
840:
841: calculateVertexNormals(maxShare);
842: if ((DEBUG & 16) != 0) {
843: t5 += System.currentTimeMillis() - time;
844: System.out.println("Vertex Normals: " + t5 + " ms");
845: time = System.currentTimeMillis();
846: }
847:
848: if ((DEBUG & 64) != 0) {
849: System.out
850: .println("Coordinate and normal indices (triangles):");
851: for (int i = 0; i < coordInds.length; i++) {
852: System.out.println(i + " " + coordInds[i] + " "
853: + normalInds[i]);
854: }
855: }
856:
857: // We have been caching some info from the GeometryInfo, so we need
858: // to update it.
859: gi.setCoordinateIndices(coordInds);
860: gi.setColorIndices(colorInds);
861: gi.setNormalIndices(normalInds);
862: for (int i = 0; i < numTexSets; i++) {
863: gi.setTextureCoordinateIndices(i, texInds[i]);
864: }
865: gi.setStripCounts(stripCounts);
866: } // End of generateNormals
867:
868: /**
869: * Set the crease angle.
870: * If two triangles' normals differ by more than
871: * creaseAngle, then the vertex will get two separate normals, creating a
872: * discontinuous crease in the model. This is perfect for the edge
873: * of a table or the corner of a cube, for instance. Clamped to
874: * 0 <= creaseAngle <= PI. Optimizations are made for creaseAngle == 0
875: * (facet normals) and creaseAngle == PI (smooth shading).
876: */
877: public void setCreaseAngle(double radians) {
878: if (radians > Math.PI)
879: radians = Math.PI;
880: if (radians < 0.0)
881: radians = 0.0;
882: creaseAngle = radians;
883: } // End of setCreaseAngle
884:
885: /**
886: * Returns the current value of the crease angle, in radians.
887: */
888: public double getCreaseAngle() {
889: return creaseAngle;
890: } // End of getCreaseAngle
891:
892: /**
893: * Constructor. Construct a NormalGenerator object with creaseAngle
894: * set to the given value.
895: */
896: public NormalGenerator(double radians) {
897: creaseAngle = radians;
898: } // End of NormalGenerator(double)
899:
900: /**
901: * Constructor. Construct a NormalGenerator object with creaseAngle
902: * set to 44 degrees (0.767944871 radians).
903: */
904: public NormalGenerator() {
905: this (44.0 * Math.PI / 180.0);
906: } // End of NormalGenerator()
907: } // End of class NormalGenerator
908:
909: // End of file NormalGenerator.java
|