Source Code Cross Referenced for CompressionStream.java in  » 6.0-JDK-Modules » java-3d » com » sun » j3d » utils » geometry » compression » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » 6.0 JDK Modules » java 3d » com.sun.j3d.utils.geometry.compression 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * $RCSfile: CompressionStream.java,v $
0003:         *
0004:         * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
0005:         *
0006:         * Redistribution and use in source and binary forms, with or without
0007:         * modification, are permitted provided that the following conditions
0008:         * are met:
0009:         *
0010:         * - Redistribution of source code must retain the above copyright
0011:         *   notice, this list of conditions and the following disclaimer.
0012:         *
0013:         * - Redistribution in binary form must reproduce the above copyright
0014:         *   notice, this list of conditions and the following disclaimer in
0015:         *   the documentation and/or other materials provided with the
0016:         *   distribution.
0017:         *
0018:         * Neither the name of Sun Microsystems, Inc. or the names of
0019:         * contributors may be used to endorse or promote products derived
0020:         * from this software without specific prior written permission.
0021:         *
0022:         * This software is provided "AS IS," without a warranty of any
0023:         * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
0024:         * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
0025:         * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
0026:         * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
0027:         * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
0028:         * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
0029:         * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
0030:         * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
0031:         * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
0032:         * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
0033:         * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
0034:         * POSSIBILITY OF SUCH DAMAGES.
0035:         *
0036:         * You acknowledge that this software is not designed, licensed or
0037:         * intended for use in the design, construction, operation or
0038:         * maintenance of any nuclear facility.
0039:         *
0040:         * $Revision: 1.3 $
0041:         * $Date: 2007/02/09 17:20:22 $
0042:         * $State: Exp $
0043:         */
0044:
0045:        package com.sun.j3d.utils.geometry.compression;
0046:
0047:        import com.sun.j3d.internal.BufferWrapper;
0048:        import com.sun.j3d.internal.ByteBufferWrapper;
0049:        import com.sun.j3d.internal.DoubleBufferWrapper;
0050:        import com.sun.j3d.internal.FloatBufferWrapper;
0051:        import com.sun.j3d.utils.geometry.GeometryInfo;
0052:        import java.util.Collection;
0053:        import java.util.Iterator;
0054:        import java.util.LinkedList;
0055:        import javax.media.j3d.Appearance;
0056:        import javax.media.j3d.Geometry;
0057:        import javax.media.j3d.GeometryArray;
0058:        import javax.media.j3d.GeometryStripArray;
0059:        import javax.media.j3d.IndexedGeometryArray;
0060:        import javax.media.j3d.IndexedGeometryStripArray;
0061:        import javax.media.j3d.IndexedLineArray;
0062:        import javax.media.j3d.IndexedLineStripArray;
0063:        import javax.media.j3d.IndexedQuadArray;
0064:        import javax.media.j3d.IndexedTriangleArray;
0065:        import javax.media.j3d.IndexedTriangleFanArray;
0066:        import javax.media.j3d.IndexedTriangleStripArray;
0067:        import javax.media.j3d.J3DBuffer;
0068:        import javax.media.j3d.LineArray;
0069:        import javax.media.j3d.LineStripArray;
0070:        import javax.media.j3d.Material;
0071:        import javax.media.j3d.QuadArray;
0072:        import javax.media.j3d.Shape3D;
0073:        import javax.media.j3d.TriangleArray;
0074:        import javax.media.j3d.TriangleFanArray;
0075:        import javax.media.j3d.TriangleStripArray;
0076:        import javax.vecmath.Color3f;
0077:        import javax.vecmath.Color4f;
0078:        import javax.vecmath.Point3d;
0079:        import javax.vecmath.Point3f;
0080:        import javax.vecmath.Point3i;
0081:        import javax.vecmath.Vector3f;
0082:
0083:        /**
0084:         * This class is used as input to a geometry compressor.  It collects elements
0085:         * such as vertices, normals, colors, mesh references, and quantization
0086:         * parameters in an ordered stream.  This stream is then traversed during
0087:         * the compression process and used to build the compressed output buffer.
0088:         *
0089:         * @see GeometryCompressor
0090:         *
0091:         * @since Java 3D 1.5
0092:         */
0093:        public class CompressionStream {
0094:            //
0095:            // NOTE: For now, copies are made of all GeometryArray vertex components
0096:            // even when by-reference access is available.
0097:            //
0098:            // TODO: Retrofit all CompressionStreamElements and MeshBuffer to handle
0099:            // offsets to vertex data array references so that vertex components don't
0100:            // have to be copied.  New CompressionStreamElements could be defined to
0101:            // set the current array reference during the quantization pass, or the
0102:            // reference could be included in every CompressionStreamElement along
0103:            // with the data offsets.
0104:            //
0105:            // TODO: Quantize on-the-fly when adding GeometryArray vertex data so that
0106:            // CompressionStreamElements don't need references to the original float,
0107:            // double, or byte data.  Quantization is currently a separate pass since
0108:            // the 1st pass adds vertex data and gets the total object bounds, but
0109:            // this can be computed by merging the bounds of each GeometryArray
0110:            // compressed into a single object.  The 2nd pass quantization is still
0111:            // needed for vertex data which isn't retrieved from a GeometryArray; for
0112:            // example, apps that might use the addVertex() methods directly instead
0113:            // of addGeometryArray().
0114:            //
0115:            // TODO: To further optimize memory, create new subclasses of
0116:            // CompressionStream{Color, Normal} for bundled attributes and add them as
0117:            // explicit stream elements.  Then CompressionStreamVertex won't need to
0118:            // carry references to them.  This memory savings might be negated by the
0119:            // extra overhead of adding more elements to the stream, however.
0120:            //
0121:            // TODO: Keep the absolute quantized values in the mesh buffer mirror so
0122:            // that unmeshed CompressionStreamElements don't need to carry them.
0123:            //
0124:            // TODO: Support texture coordinate compression even though Level II is
0125:            // not supported by any hardware decompressor on any graphics card.
0126:            // Software decompression is still useful for applications interested in
0127:            // minimizing file space, transmission time, and object loading time.
0128:            //
0129:            private static final boolean debug = false;
0130:            private static final boolean benchmark = false;
0131:
0132:            // Mesh buffer normal substitution is unavailable in Level I.
0133:            private static final boolean noMeshNormalSubstitution = true;
0134:
0135:            /**
0136:             * This flag indicates that a vertex starts a new triangle or line strip.
0137:             */
0138:            static final int RESTART = 1;
0139:
0140:            /**
0141:             * This flag indicates that the next triangle in the strip is defined by
0142:             * replacing the middle vertex of the previous triangle in the strip.
0143:             * Equivalent to REPLACE_OLDEST for line strips.
0144:             */
0145:            static final int REPLACE_MIDDLE = 2;
0146:
0147:            /**
0148:             * This flag indicates that the next triangle in the strip is defined by
0149:             * replacing the oldest vertex of the previous triangle in the strip.
0150:             * Equivalent to REPLACE_MIDDLE for line strips.
0151:             */
0152:            static final int REPLACE_OLDEST = 3;
0153:
0154:            /**
0155:             * This flag indicates that a vertex is to be pushed into the mesh buffer.
0156:             */
0157:            static final int MESH_PUSH = 1;
0158:
0159:            /**
0160:             * This flag indicates that a vertex does not use the mesh buffer.
0161:             */
0162:            static final int NO_MESH_PUSH = 0;
0163:
0164:            /**
0165:             * Byte to float scale factor for scaling byte color components.
0166:             */
0167:            static final float ByteToFloatScale = 1.0f / 255.0f;
0168:
0169:            /**
0170:             * Type of this stream, either CompressedGeometryData.Header.POINT_BUFFER,
0171:             * CompressedGeometryData.Header.LINE_BUFFER, or
0172:             * CompressedGeometryData.Header.TRIANGLE_BUFFER
0173:             */
0174:            int streamType;
0175:
0176:            /**
0177:             * A mask indicating which components are present in each vertex, as
0178:             * defined by GeometryArray.
0179:             */
0180:            int vertexComponents;
0181:
0182:            /**
0183:             * Boolean indicating colors are bundled with the vertices.
0184:             */
0185:            boolean vertexColors;
0186:
0187:            /**
0188:             * Boolean indicating RGB colors are bundled with the vertices.
0189:             */
0190:            boolean vertexColor3;
0191:
0192:            /**
0193:             * Boolean indicating RGBA colors are bundled with the vertices.
0194:             */
0195:            boolean vertexColor4;
0196:
0197:            /**
0198:             * Boolean indicating normals are bundled with the vertices.
0199:             */
0200:            boolean vertexNormals;
0201:
0202:            /**
0203:             * Boolean indicating texture coordinates are present.
0204:             */
0205:            boolean vertexTextures;
0206:
0207:            /**
0208:             * Boolean indicating that 2D texture coordinates are used.
0209:             * Currently only used to skip over textures in interleaved data.
0210:             */
0211:            boolean vertexTexture2;
0212:
0213:            /**
0214:             * Boolean indicating that 3D texture coordinates are used.
0215:             * Currently only used to skip over textures in interleaved data.
0216:             */
0217:            boolean vertexTexture3;
0218:
0219:            /**
0220:             * Boolean indicating that 4D texture coordinates are used.
0221:             * Currently only used to skip over textures in interleaved data.
0222:             */
0223:            boolean vertexTexture4;
0224:
0225:            /**
0226:             * Axes-aligned box enclosing all vertices in model coordinates.
0227:             */
0228:            Point3d mcBounds[] = new Point3d[2];
0229:
0230:            /**
0231:             * Axes-aligned box enclosing all vertices in normalized coordinates.
0232:             */
0233:            Point3d ncBounds[] = new Point3d[2];
0234:
0235:            /**
0236:             * Axes-aligned box enclosing all vertices in quantized coordinates.
0237:             */
0238:            Point3i qcBounds[] = new Point3i[2];
0239:
0240:            /**
0241:             * Center for normalizing positions to the unit cube.
0242:             */
0243:            double center[] = new double[3];
0244:
0245:            /**
0246:             * Maximum position range along the 3 axes.
0247:             */
0248:            double positionRangeMaximum;
0249:
0250:            /**
0251:             * Scale for normalizing positions to the unit cube.
0252:             */
0253:            double scale;
0254:
0255:            /**
0256:             * Current position component (X, Y, and Z) quantization value.  This can
0257:             * range from 1 to 16 bits and has a default of 16.<p>
0258:             *
0259:             * At 1 bit of quantization it is not possible to express positive
0260:             * absolute or delta positions.
0261:             */
0262:            int positionQuant;
0263:
0264:            /**
0265:             * Current color component (R, G, B, A) quantization value.  This can
0266:             * range from 2 to 16 bits and has a default of 9.<p>
0267:             *
0268:             * A color component is represented with a signed fixed-point value in
0269:             * order to be able express negative deltas; the default of 9 bits
0270:             * corresponds to the 8-bit color component range of the graphics hardware
0271:             * commonly available.  Colors must be non-negative, so the lower limit of
0272:             * quantization is 2 bits.
0273:             */
0274:            int colorQuant;
0275:
0276:            /**
0277:             * Current normal component (U and V) quantization value.  This can range
0278:             * from 0 to 6 bits and has a default of 6.<p>
0279:             *
0280:             * At 0 bits of quantization normals are represented only as 6 bit
0281:             * sextant/octant pairs and 14 specially encoded normals (the 6 axis
0282:             * normals and the 8 octant midpoint normals); since U and V can only be 0
0283:             * at the minimum quantization, the totally number of unique normals is 
0284:             * 12 + 14 = 26.
0285:             */
0286:            int normalQuant;
0287:
0288:            /**
0289:             * Flag indicating position quantization change.
0290:             */
0291:            boolean positionQuantChanged;
0292:
0293:            /**
0294:             * Flag indicating color quantization change.
0295:             */
0296:            boolean colorQuantChanged;
0297:
0298:            /**
0299:             * Flag indicating normal quantization change.
0300:             */
0301:            boolean normalQuantChanged;
0302:
0303:            /**
0304:             * Last quantized position.
0305:             */
0306:            int lastPosition[] = new int[3];
0307:
0308:            /**
0309:             * Last quantized color.
0310:             */
0311:            int lastColor[] = new int[4];
0312:
0313:            /**
0314:             * Last quantized normal's sextant.
0315:             */
0316:            int lastSextant;
0317:
0318:            /**
0319:             * Last quantized normal's octant.
0320:             */
0321:            int lastOctant;
0322:
0323:            /**
0324:             * Last quantized normal's U encoding parameter.
0325:             */
0326:            int lastU;
0327:
0328:            /**
0329:             * Last quantized normal's V encoding parameter.
0330:             */
0331:            int lastV;
0332:
0333:            /**
0334:             * Flag indicating last normal used a special encoding.
0335:             */
0336:            boolean lastSpecialNormal;
0337:
0338:            /**
0339:             * Flag indicating the first position in this stream.
0340:             */
0341:            boolean firstPosition;
0342:
0343:            /**
0344:             * Flag indicating the first color in this stream.
0345:             */
0346:            boolean firstColor;
0347:
0348:            /**
0349:             * Flag indicating the first normal in this stream.
0350:             */
0351:            boolean firstNormal;
0352:
0353:            /**
0354:             * The total number of bytes used to create the uncompressed geometric
0355:             * elements in this stream, useful for performance analysis.  This
0356:             * excludes mesh buffer references.
0357:             */
0358:            int byteCount;
0359:
0360:            /**
0361:             * The number of vertices created for this stream, excluding mesh buffer
0362:             * references.
0363:             */
0364:            int vertexCount;
0365:
0366:            /**
0367:             * The number of mesh buffer references created for this stream.
0368:             */
0369:            int meshReferenceCount;
0370:
0371:            /**
0372:             * Mesh buffer mirror used for computing deltas during quantization pass
0373:             * and a limited meshing algorithm for unstripped data.
0374:             */
0375:            MeshBuffer meshBuffer = new MeshBuffer();
0376:
0377:            // Collection which holds the elements of this stream.
0378:            private Collection stream;
0379:
0380:            // True if preceding stream elements were colors or normals.  Used to flag
0381:            // color and normal mesh buffer substitution when computing deltas during
0382:            // quantization pass.
0383:            private boolean lastElementColor = false;
0384:            private boolean lastLastElementColor = false;
0385:            private boolean lastElementNormal = false;
0386:            private boolean lastLastElementNormal = false;
0387:
0388:            // Some convenient temporary holding variables.
0389:            private Point3f p3f = new Point3f();
0390:            private Color3f c3f = new Color3f();
0391:            private Color4f c4f = new Color4f();
0392:            private Vector3f n3f = new Vector3f();
0393:
0394:            // Private constructor for common initializations.
0395:            private CompressionStream() {
0396:                this .stream = new LinkedList();
0397:
0398:                byteCount = 0;
0399:                vertexCount = 0;
0400:                meshReferenceCount = 0;
0401:
0402:                mcBounds[0] = new Point3d(Double.POSITIVE_INFINITY,
0403:                        Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
0404:                mcBounds[1] = new Point3d(Double.NEGATIVE_INFINITY,
0405:                        Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
0406:
0407:                qcBounds[0] = new Point3i(Integer.MAX_VALUE, Integer.MAX_VALUE,
0408:                        Integer.MAX_VALUE);
0409:                qcBounds[1] = new Point3i(Integer.MIN_VALUE, Integer.MIN_VALUE,
0410:                        Integer.MIN_VALUE);
0411:
0412:                /* normalized bounds computed from quantized bounds */
0413:                ncBounds[0] = new Point3d();
0414:                ncBounds[1] = new Point3d();
0415:            }
0416:
0417:            /**
0418:             * Creates a new CompressionStream for the specified geometry type and
0419:             * vertex format.<p>
0420:             * 
0421:             * @param streamType type of data in this stream, either
0422:             * CompressedGeometryData.Header.POINT_BUFFER,
0423:             * CompressedGeometryData.Header.LINE_BUFFER, or
0424:             * CompressedGeometryData.Header.TRIANGLE_BUFFER
0425:             * @param vertexComponents a mask indicating which components are present
0426:             * in each vertex, as defined by GeometryArray: COORDINATES, NORMALS, and
0427:             * COLOR_3 or COLOR_4.
0428:             * @see GeometryCompressor
0429:             * @see GeometryArray
0430:             */
0431:            CompressionStream(int streamType, int vertexComponents) {
0432:                this ();
0433:                this .streamType = streamType;
0434:                this .vertexComponents = getVertexComponents(vertexComponents);
0435:            }
0436:
0437:            // See what vertex geometry components are present.  The byReference,
0438:            // interleaved, useNIOBuffer, and useCoordIndexOnly flags are not
0439:            // examined.
0440:            private int getVertexComponents(int vertexFormat) {
0441:                int components = 0;
0442:
0443:                vertexColors = vertexColor3 = vertexColor4 = vertexNormals = vertexTextures = vertexTexture2 = vertexTexture3 = vertexTexture4 = false;
0444:
0445:                if ((vertexFormat & GeometryArray.NORMALS) != 0) {
0446:                    vertexNormals = true;
0447:                    components &= GeometryArray.NORMALS;
0448:                    if (debug)
0449:                        System.out.println("vertexNormals");
0450:                }
0451:
0452:                if ((vertexFormat & GeometryArray.COLOR_3) != 0) {
0453:                    vertexColors = true;
0454:
0455:                    if ((vertexFormat & GeometryArray.COLOR_4) != 0) {
0456:                        vertexColor4 = true;
0457:                        components &= GeometryArray.COLOR_4;
0458:                        if (debug)
0459:                            System.out.println("vertexColor4");
0460:                    } else {
0461:                        vertexColor3 = true;
0462:                        components &= GeometryArray.COLOR_3;
0463:                        if (debug)
0464:                            System.out.println("vertexColor3");
0465:                    }
0466:                }
0467:
0468:                if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
0469:                    vertexTextures = true;
0470:                    vertexTexture2 = true;
0471:                    components &= GeometryArray.TEXTURE_COORDINATE_2;
0472:                    if (debug)
0473:                        System.out.println("vertexTexture2");
0474:                } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
0475:                    vertexTextures = true;
0476:                    vertexTexture3 = true;
0477:                    components &= GeometryArray.TEXTURE_COORDINATE_3;
0478:                    if (debug)
0479:                        System.out.println("vertexTexture3");
0480:                } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
0481:                    vertexTextures = true;
0482:                    vertexTexture4 = true;
0483:                    components &= GeometryArray.TEXTURE_COORDINATE_4;
0484:                    if (debug)
0485:                        System.out.println("vertexTexture4");
0486:                }
0487:
0488:                if (vertexTextures)
0489:                    // Throw exception for now until texture is supported.
0490:                    throw new UnsupportedOperationException(
0491:                            "\ncompression of texture coordinates is not supported");
0492:
0493:                return components;
0494:            }
0495:
0496:            // Get the streamType associated with a GeometryArray instance.
0497:            private int getStreamType(GeometryArray ga) {
0498:                if (ga instanceof  TriangleStripArray
0499:                        || ga instanceof  IndexedTriangleStripArray
0500:                        || ga instanceof  TriangleFanArray
0501:                        || ga instanceof  IndexedTriangleFanArray
0502:                        || ga instanceof  TriangleArray
0503:                        || ga instanceof  IndexedTriangleArray
0504:                        || ga instanceof  QuadArray
0505:                        || ga instanceof  IndexedQuadArray)
0506:
0507:                    return CompressedGeometryData.Header.TRIANGLE_BUFFER;
0508:
0509:                else if (ga instanceof  LineArray
0510:                        || ga instanceof  IndexedLineArray
0511:                        || ga instanceof  LineStripArray
0512:                        || ga instanceof  IndexedLineStripArray)
0513:
0514:                    return CompressedGeometryData.Header.LINE_BUFFER;
0515:
0516:                else
0517:                    return CompressedGeometryData.Header.POINT_BUFFER;
0518:            }
0519:
0520:            /**
0521:             * Iterates across all compression stream elements and applies
0522:             * quantization parameters, encoding consecutive vertices as delta values
0523:             * whenever possible.  Each geometric element is mapped to a HuffmanNode
0524:             * object containing its resulting bit length, right shift (trailing 0
0525:             * count), and absolute or relative status.<p>
0526:             * 
0527:             * Positions are normalized to span a unit cube via an offset and a
0528:             * uniform scale factor that maps the midpoint of the object extents along
0529:             * each dimension to the origin, and the longest dimension of the object to
0530:             * the open interval (-1.0 .. +1.0).  The geometric endpoints along that
0531:             * dimension are both one quantum away from unity; for example, at a
0532:             * position quantization of 6 bits, an object would be normalized so that
0533:             * its most negative dimension is at (-1 + 1/64) and the most positive is
0534:             * at (1 - 1/64).<p>
0535:             * 
0536:             * Normals are assumed to be of unit length.  Color components are clamped
0537:             * to the [0..1) range, where the right endpoint is one quantum less
0538:             * than 1.0.<p>
0539:             *
0540:             * @param huffmanTable Table which will map geometric compression stream
0541:             * elements to HuffmanNode objects describing each element's data
0542:             * representation.  This table can then be processed with Huffman's
0543:             * algorithm to optimize the bit length of descriptor tags according to
0544:             * the number of geometric elements mapped to each tag.
0545:             */
0546:            void quantize(HuffmanTable huffmanTable) {
0547:                // Set up default initial quantization parameters.  The position and
0548:                // color parameters specify the number of bits for each X, Y, Z, R, G,
0549:                // B, or A component.  The normal quantization parameter specifies the
0550:                // number of bits for each U and V component.
0551:                positionQuant = 16;
0552:                colorQuant = 9;
0553:                normalQuant = 6;
0554:
0555:                // Compute position center and scaling for normalization to the unit
0556:                // cube.  This is a volume bounded by the open intervals (-1..1) on
0557:                // each axis.
0558:                center[0] = (mcBounds[1].x + mcBounds[0].x) / 2.0;
0559:                center[1] = (mcBounds[1].y + mcBounds[0].y) / 2.0;
0560:                center[2] = (mcBounds[1].z + mcBounds[0].z) / 2.0;
0561:
0562:                double xRange = mcBounds[1].x - mcBounds[0].x;
0563:                double yRange = mcBounds[1].y - mcBounds[0].y;
0564:                double zRange = mcBounds[1].z - mcBounds[0].z;
0565:
0566:                if (xRange > yRange)
0567:                    positionRangeMaximum = xRange;
0568:                else
0569:                    positionRangeMaximum = yRange;
0570:
0571:                if (zRange > positionRangeMaximum)
0572:                    positionRangeMaximum = zRange;
0573:
0574:                // Adjust the range of the unit cube to match the default
0575:                // quantization.
0576:                //
0577:                // This scale factor along with the center values computed above will
0578:                // produce 16-bit integer representations of the floating point
0579:                // position coordinates ranging symmetrically about 0 from -32767 to
0580:                // +32767.  -32768 is not used and the normalized floating point
0581:                // position coordinates of -1.0 as well as +1.0 will not be
0582:                // represented.
0583:                //
0584:                // Applications which wish to seamlessly stitch together compressed
0585:                // objects will need to be aware that the range of normalized
0586:                // positions will be one quantum away from the [-1..1] endpoints of
0587:                // the unit cube and should adjust scale factors accordingly.
0588:                scale = (2.0 / positionRangeMaximum) * (32767.0 / 32768.0);
0589:
0590:                // Flag quantization change.
0591:                positionQuantChanged = colorQuantChanged = normalQuantChanged = true;
0592:
0593:                // Flag first position, color, and normal.
0594:                firstPosition = firstColor = firstNormal = true;
0595:
0596:                // Apply quantization.
0597:                Iterator i = stream.iterator();
0598:                while (i.hasNext()) {
0599:                    Object o = i.next();
0600:
0601:                    if (o instanceof  CompressionStreamElement) {
0602:                        ((CompressionStreamElement) o).quantize(this ,
0603:                                huffmanTable);
0604:
0605:                        // Keep track of whether last two elements were colors or
0606:                        // normals for mesh buffer component substitution semantics.
0607:                        lastLastElementColor = lastElementColor;
0608:                        lastLastElementNormal = lastElementNormal;
0609:                        lastElementColor = lastElementNormal = false;
0610:
0611:                        if (o instanceof  CompressionStreamColor)
0612:                            lastElementColor = true;
0613:                        else if (o instanceof  CompressionStreamNormal)
0614:                            lastElementNormal = true;
0615:                    }
0616:                }
0617:
0618:                // Compute the bounds in normalized coordinates.
0619:                ncBounds[0].x = (double) qcBounds[0].x / 32768.0;
0620:                ncBounds[0].y = (double) qcBounds[0].y / 32768.0;
0621:                ncBounds[0].z = (double) qcBounds[0].z / 32768.0;
0622:
0623:                ncBounds[1].x = (double) qcBounds[1].x / 32768.0;
0624:                ncBounds[1].y = (double) qcBounds[1].y / 32768.0;
0625:                ncBounds[1].z = (double) qcBounds[1].z / 32768.0;
0626:            }
0627:
0628:            /**
0629:             * Iterates across all compression stream elements and builds the
0630:             * compressed geometry command stream output.<p>
0631:             *
0632:             * @param huffmanTable Table which maps geometric elements in this stream
0633:             * to tags describing the encoding parameters (length, shift, and
0634:             * absolute/relative status) to be used for their representations in the
0635:             * compressed output.  All tags must be 6 bits or less in length, and the
0636:             * sum of the number of bits in the tag plus the number of bits in the
0637:             * data it describes must be at least 6 bits in length.
0638:             *
0639:             * @param outputBuffer CommandStream to use for collecting the compressed
0640:             * bits.
0641:             */
0642:            void outputCommands(HuffmanTable huffmanTable,
0643:                    CommandStream outputBuffer) {
0644:                //
0645:                // The first command output is setState to indicate what data is
0646:                // bundled with each vertex.  Although the semantics of geometry
0647:                // decompression allow setState to appear anywhere in the stream, this
0648:                // cannot be handled by the current Java 3D software decompressor,
0649:                // which internally decompresses an entire compressed buffer into a
0650:                // single retained object sharing a single consistent vertex format.
0651:                // This limitation may be removed in subsequent releases of Java 3D.
0652:                //
0653:                int bnv = (vertexNormals ? 1 : 0);
0654:                int bcv = ((vertexColor3 || vertexColor4) ? 1 : 0);
0655:                int cap = (vertexColor4 ? 1 : 0);
0656:
0657:                int command = CommandStream.SET_STATE | bnv;
0658:                long data = (bcv << 2) | (cap << 1);
0659:
0660:                // Output the setState command.
0661:                outputBuffer.addCommand(command, 8, data, 3);
0662:
0663:                // Output the Huffman table commands.
0664:                huffmanTable.outputCommands(outputBuffer);
0665:
0666:                // Output each compression stream element's data.
0667:                Iterator i = stream.iterator();
0668:                while (i.hasNext()) {
0669:                    Object o = i.next();
0670:                    if (o instanceof  CompressionStreamElement)
0671:                        ((CompressionStreamElement) o).outputCommand(
0672:                                huffmanTable, outputBuffer);
0673:                }
0674:
0675:                // Finish the header-forwarding interleave and long-word align.
0676:                outputBuffer.end();
0677:            }
0678:
0679:            /**
0680:             * Retrieve the total size of the uncompressed geometric data in bytes,
0681:             * excluding mesh buffer references.
0682:             * @return uncompressed byte count
0683:             */
0684:            int getByteCount() {
0685:                return byteCount;
0686:            }
0687:
0688:            /**
0689:             * Retrieve the the number of vertices created for this stream, excluding
0690:             * mesh buffer references.
0691:             * @return vertex count
0692:             */
0693:            int getVertexCount() {
0694:                return vertexCount;
0695:            }
0696:
0697:            /**
0698:             * Retrieve the number of mesh buffer references created for this stream. 
0699:             * @return mesh buffer reference count
0700:             */
0701:            int getMeshReferenceCount() {
0702:                return meshReferenceCount;
0703:            }
0704:
0705:            /**
0706:             * Stream element that sets position quantization during quantize pass.
0707:             */
0708:            private class PositionQuant extends CompressionStreamElement {
0709:                int value;
0710:
0711:                PositionQuant(int value) {
0712:                    this .value = value;
0713:                }
0714:
0715:                void quantize(CompressionStream s, HuffmanTable t) {
0716:                    positionQuant = value;
0717:                    positionQuantChanged = true;
0718:
0719:                    // Adjust range of unit cube scaling to match quantization.
0720:                    scale = (2.0 / positionRangeMaximum)
0721:                            * (((double) ((1 << (value - 1)) - 1)) / ((double) (1 << (value - 1))));
0722:                }
0723:
0724:                public String toString() {
0725:                    return "positionQuant: " + value;
0726:                }
0727:            }
0728:
0729:            /**
0730:             * Stream element that sets normal quantization during quantize pass.
0731:             */
0732:            private class NormalQuant extends CompressionStreamElement {
0733:                int value;
0734:
0735:                NormalQuant(int value) {
0736:                    this .value = value;
0737:                }
0738:
0739:                void quantize(CompressionStream s, HuffmanTable t) {
0740:                    normalQuant = value;
0741:                    normalQuantChanged = true;
0742:                }
0743:
0744:                public String toString() {
0745:                    return "normalQuant: " + value;
0746:                }
0747:            }
0748:
0749:            /**
0750:             * Stream element that sets color quantization during quantize pass.
0751:             */
0752:            private class ColorQuant extends CompressionStreamElement {
0753:                int value;
0754:
0755:                ColorQuant(int value) {
0756:                    this .value = value;
0757:                }
0758:
0759:                void quantize(CompressionStream s, HuffmanTable t) {
0760:                    colorQuant = value;
0761:                    colorQuantChanged = true;
0762:                }
0763:
0764:                public String toString() {
0765:                    return "colorQuant: " + value;
0766:                }
0767:            }
0768:
0769:            /**
0770:             * Stream element that references the mesh buffer.
0771:             */
0772:            private class MeshReference extends CompressionStreamElement {
0773:                int stripFlag, meshIndex;
0774:
0775:                MeshReference(int stripFlag, int meshIndex) {
0776:                    this .stripFlag = stripFlag;
0777:                    this .meshIndex = meshIndex;
0778:                    meshReferenceCount++;
0779:                }
0780:
0781:                void quantize(CompressionStream s, HuffmanTable t) {
0782:                    // Retrieve the vertex from the mesh buffer mirror and set up the
0783:                    // data needed for the next stream element to compute its deltas.
0784:                    CompressionStreamVertex v = meshBuffer.getVertex(meshIndex);
0785:                    lastPosition[0] = v.xAbsolute;
0786:                    lastPosition[1] = v.yAbsolute;
0787:                    lastPosition[2] = v.zAbsolute;
0788:
0789:                    // Set up last color data if it exists and previous elements
0790:                    // don't override it.
0791:                    if (v.color != null && !lastElementColor
0792:                            && !(lastElementNormal && lastLastElementColor)) {
0793:                        lastColor[0] = v.color.rAbsolute;
0794:                        lastColor[1] = v.color.gAbsolute;
0795:                        lastColor[2] = v.color.bAbsolute;
0796:                        lastColor[3] = v.color.aAbsolute;
0797:                    }
0798:
0799:                    // Set up last normal data if it exists and previous element
0800:                    // doesn't override it.
0801:                    if (v.normal != null && !lastElementNormal
0802:                            && !(lastElementColor && lastLastElementNormal)) {
0803:                        lastSextant = v.normal.sextant;
0804:                        lastOctant = v.normal.octant;
0805:                        lastU = v.normal.uAbsolute;
0806:                        lastV = v.normal.vAbsolute;
0807:                        lastSpecialNormal = v.normal.specialNormal;
0808:                    }
0809:                }
0810:
0811:                void outputCommand(HuffmanTable t, CommandStream outputBuffer) {
0812:                    int command = CommandStream.MESH_B_R;
0813:                    long data = stripFlag & 0x1;
0814:
0815:                    command |= (((meshIndex & 0xf) << 1) | (stripFlag >> 1));
0816:                    outputBuffer.addCommand(command, 8, data, 1);
0817:                }
0818:
0819:                public String toString() {
0820:                    return "meshReference: stripFlag " + stripFlag
0821:                            + " meshIndex " + meshIndex;
0822:                }
0823:            }
0824:
0825:            /**
0826:             * Copy vertex data and add it to the end of this stream.
0827:             * @param pos position data
0828:             * @param stripFlag vertex replacement flag, either RESTART,
0829:             * REPLACE_OLDEST, or REPLACE_MIDDLE
0830:             */
0831:            void addVertex(Point3f pos, int stripFlag) {
0832:                stream.add(new CompressionStreamVertex(this , pos,
0833:                        (Vector3f) null, (Color3f) null, stripFlag,
0834:                        NO_MESH_PUSH));
0835:            }
0836:
0837:            /**
0838:             * Copy vertex data and add it to the end of this stream.
0839:             * @param pos position data
0840:             * @param norm normal data
0841:             * @param stripFlag vertex replacement flag, either RESTART,
0842:             * REPLACE_OLDEST, or REPLACE_MIDDLE
0843:             */
0844:            void addVertex(Point3f pos, Vector3f norm, int stripFlag) {
0845:                stream.add(new CompressionStreamVertex(this , pos, norm,
0846:                        (Color3f) null, stripFlag, NO_MESH_PUSH));
0847:            }
0848:
0849:            /**
0850:             * Copy vertex data and add it to the end of this stream.
0851:             * @param pos position data
0852:             * @param color color data
0853:             * @param stripFlag vertex replacement flag, either RESTART,
0854:             * REPLACE_OLDEST, or REPLACE_MIDDLE
0855:             */
0856:            void addVertex(Point3f pos, Color3f color, int stripFlag) {
0857:                stream.add(new CompressionStreamVertex(this , pos,
0858:                        (Vector3f) null, color, stripFlag, NO_MESH_PUSH));
0859:            }
0860:
0861:            /**
0862:             * Copy vertex data and add it to the end of this stream.
0863:             * @param pos position data
0864:             * @param color color data
0865:             * @param stripFlag vertex replacement flag, either RESTART,
0866:             * REPLACE_OLDEST, or REPLACE_MIDDLE
0867:             */
0868:            void addVertex(Point3f pos, Color4f color, int stripFlag) {
0869:                stream.add(new CompressionStreamVertex(this , pos,
0870:                        (Vector3f) null, color, stripFlag, NO_MESH_PUSH));
0871:            }
0872:
0873:            /**
0874:             * Copy vertex data and add it to the end of this stream.
0875:             * @param pos position data
0876:             * @param norm normal data
0877:             * @param color color data
0878:             * @param stripFlag vertex replacement flag, either RESTART,
0879:             * REPLACE_OLDEST, or REPLACE_MIDDLE
0880:             */
0881:            void addVertex(Point3f pos, Vector3f norm, Color3f color,
0882:                    int stripFlag) {
0883:                stream.add(new CompressionStreamVertex(this , pos, norm, color,
0884:                        stripFlag, NO_MESH_PUSH));
0885:            }
0886:
0887:            /**
0888:             * Copy vertex data and add it to the end of this stream.
0889:             * @param pos position data
0890:             * @param norm normal data
0891:             * @param color color data
0892:             * @param stripFlag vertex replacement flag, either RESTART,
0893:             * REPLACE_OLDEST, or REPLACE_MIDDLE
0894:             */
0895:            void addVertex(Point3f pos, Vector3f norm, Color4f color,
0896:                    int stripFlag) {
0897:                stream.add(new CompressionStreamVertex(this , pos, norm, color,
0898:                        stripFlag, NO_MESH_PUSH));
0899:            }
0900:
0901:            /**
0902:             * Copy vertex data and add it to the end of this stream.
0903:             * @param pos position data
0904:             * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
0905:             * or REPLACE_MIDDLE
0906:             * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
0907:             */
0908:            void addVertex(Point3f pos, int stripFlag, int meshFlag) {
0909:                stream.add(new CompressionStreamVertex(this , pos,
0910:                        (Vector3f) null, (Color3f) null, stripFlag, meshFlag));
0911:            }
0912:
0913:            /**
0914:             * Copy vertex data and add it to the end of this stream.
0915:             * @param pos position data
0916:             * @param norm normal data
0917:             * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
0918:             * or REPLACE_MIDDLE
0919:             * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
0920:             */
0921:            void addVertex(Point3f pos, Vector3f norm, int stripFlag,
0922:                    int meshFlag) {
0923:                stream.add(new CompressionStreamVertex(this , pos, norm,
0924:                        (Color3f) null, stripFlag, meshFlag));
0925:            }
0926:
0927:            /**
0928:             * Copy vertex data and add it to the end of this stream.
0929:             * @param pos position data
0930:             * @param color color data
0931:             * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
0932:             * or REPLACE_MIDDLE
0933:             * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
0934:             */
0935:            void addVertex(Point3f pos, Color3f color, int stripFlag,
0936:                    int meshFlag) {
0937:                stream.add(new CompressionStreamVertex(this , pos,
0938:                        (Vector3f) null, color, stripFlag, meshFlag));
0939:            }
0940:
0941:            /**
0942:             * Copy vertex data and add it to the end of this stream.
0943:             * @param pos position data
0944:             * @param color color data
0945:             * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
0946:             * or REPLACE_MIDDLE
0947:             * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
0948:             */
0949:            void addVertex(Point3f pos, Color4f color, int stripFlag,
0950:                    int meshFlag) {
0951:                stream.add(new CompressionStreamVertex(this , pos,
0952:                        (Vector3f) null, color, stripFlag, meshFlag));
0953:            }
0954:
0955:            /**
0956:             * Copy vertex data and add it to the end of this stream.
0957:             * @param pos position data
0958:             * @param norm normal data
0959:             * @param color color data
0960:             * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
0961:             * or REPLACE_MIDDLE
0962:             * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
0963:             */
0964:            void addVertex(Point3f pos, Vector3f norm, Color3f color,
0965:                    int stripFlag, int meshFlag) {
0966:                stream.add(new CompressionStreamVertex(this , pos, norm, color,
0967:                        stripFlag, meshFlag));
0968:            }
0969:
0970:            /**
0971:             * Copy vertex data and add it to the end of this stream.
0972:             * @param pos position data
0973:             * @param norm normal data
0974:             * @param color color data
0975:             * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
0976:             * or REPLACE_MIDDLE
0977:             * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
0978:             */
0979:            void addVertex(Point3f pos, Vector3f norm, Color4f color,
0980:                    int stripFlag, int meshFlag) {
0981:                stream.add(new CompressionStreamVertex(this , pos, norm, color,
0982:                        stripFlag, meshFlag));
0983:            }
0984:
0985:            /**
0986:             * Copy vertex data and add it to the end of this stream.
0987:             * @param pos position data
0988:             * @param norm normal data
0989:             * @param color color data, either Color3f or Color4f, determined by
0990:             * current vertex format
0991:             * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
0992:             * or REPLACE_MIDDLE
0993:             * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
0994:             */
0995:            void addVertex(Point3f pos, Vector3f norm, Object color,
0996:                    int stripFlag, int meshFlag) {
0997:
0998:                if (vertexColor3)
0999:                    stream.add(new CompressionStreamVertex(this , pos, norm,
1000:                            (Color3f) color, stripFlag, meshFlag));
1001:                else
1002:                    stream.add(new CompressionStreamVertex(this , pos, norm,
1003:                            (Color4f) color, stripFlag, meshFlag));
1004:            }
1005:
1006:            /**
1007:             * Add a mesh buffer reference to this stream.
1008:             * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
1009:             * or REPLACE_MIDDLE
1010:             * @param meshIndex index of vertex to retrieve from the mesh buffer
1011:             */
1012:            void addMeshReference(int stripFlag, int meshIndex) {
1013:                stream.add(new MeshReference(stripFlag, meshIndex));
1014:            }
1015:
1016:            /**
1017:             * Copy the given color to the end of this stream and use it as a global
1018:             * state change that applies to all subsequent vertices.
1019:             */
1020:            void addColor(Color3f c3f) {
1021:                stream.add(new CompressionStreamColor(this , c3f));
1022:            }
1023:
1024:            /**
1025:             * Copy the given color to the end of this stream and use it as a global
1026:             * state change that applies to all subsequent vertices.
1027:             */
1028:            void addColor(Color4f c4f) {
1029:                stream.add(new CompressionStreamColor(this , c4f));
1030:            }
1031:
1032:            /**
1033:             * Copy the given normal to the end of this stream and use it as a global
1034:             * state change that applies to all subsequent vertices.
1035:             */
1036:            void addNormal(Vector3f n) {
1037:                stream.add(new CompressionStreamNormal(this , n));
1038:            }
1039:
1040:            /**
1041:             * Add a new position quantization value to the end of this stream that
1042:             * will apply to all subsequent vertex positions.
1043:             *
1044:             * @param value number of bits to quantize each position's X, Y,
1045:             * and Z components, ranging from 1 to 16 with a default of 16
1046:             */
1047:            void addPositionQuantization(int value) {
1048:                stream.add(new PositionQuant(value));
1049:            }
1050:
1051:            /**
1052:             * Add a new color quantization value to the end of this stream that will
1053:             * apply to all subsequent colors.
1054:             *
1055:             * @param value number of bits to quantize each color's R, G, B, and
1056:             * alpha components, ranging from 2 to 16 with a default of 9
1057:             */
1058:            void addColorQuantization(int value) {
1059:                stream.add(new ColorQuant(value));
1060:            }
1061:
1062:            /**
1063:             * Add a new normal quantization value to the end of this stream that will
1064:             * apply to all subsequent normals.  This value specifies the number of
1065:             * bits for each normal's U and V components.
1066:             *
1067:             * @param value number of bits for quantizing U and V, ranging from 0 to
1068:             * 6 with a default of 6
1069:             */
1070:            void addNormalQuantization(int value) {
1071:                stream.add(new NormalQuant(value));
1072:            }
1073:
1074:            /**
1075:             * Interface to access GeometryArray vertex components and add them to the
1076:             * compression stream.
1077:             * 
1078:             * A processVertex() implementation retrieves vertex components using the
1079:             * appropriate access semantics of a particular GeometryArray, and adds
1080:             * them to the compression stream.
1081:             * 
1082:             * The implementation always pushes vertices into the mesh buffer unless
1083:             * they match ones already there; if they do, it generates mesh buffer
1084:             * references instead.  This reduces the number of vertices when
1085:             * non-stripped abutting facets are added to the stream.
1086:             * 
1087:             * Note: Level II geometry compression semantics allow the mesh buffer
1088:             * normals to be substituted with the value of an immediately
1089:             * preceding SetNormal command, but this is unavailable in Level I.
1090:             *
1091:             * @param index vertex offset from the beginning of its data array
1092:             * @param stripFlag RESTART, REPLACE_MIDDLE, or REPLACE_OLDEST
1093:             */
1094:            private interface GeometryAccessor {
1095:                void processVertex(int index, int stripFlag);
1096:            }
1097:
1098:            /**
1099:             * This class implements the GeometryAccessor interface for geometry
1100:             * arrays accessed with by-copy semantics.
1101:             */
1102:            private class ByCopyGeometry implements  GeometryAccessor {
1103:                Point3f[] positions = null;
1104:                Vector3f[] normals = null;
1105:                Color3f[] colors3 = null;
1106:                Color4f[] colors4 = null;
1107:
1108:                ByCopyGeometry(GeometryArray ga) {
1109:                    this (ga, ga.getInitialVertexIndex(), ga
1110:                            .getValidVertexCount());
1111:                }
1112:
1113:                ByCopyGeometry(GeometryArray ga, int firstVertex,
1114:                        int validVertexCount) {
1115:                    int i;
1116:                    positions = new Point3f[validVertexCount];
1117:                    for (i = 0; i < validVertexCount; i++)
1118:                        positions[i] = new Point3f();
1119:
1120:                    ga.getCoordinates(firstVertex, positions);
1121:
1122:                    if (vertexNormals) {
1123:                        normals = new Vector3f[validVertexCount];
1124:                        for (i = 0; i < validVertexCount; i++)
1125:                            normals[i] = new Vector3f();
1126:
1127:                        ga.getNormals(firstVertex, normals);
1128:                    }
1129:
1130:                    if (vertexColor3) {
1131:                        colors3 = new Color3f[validVertexCount];
1132:                        for (i = 0; i < validVertexCount; i++)
1133:                            colors3[i] = new Color3f();
1134:
1135:                        ga.getColors(firstVertex, colors3);
1136:                    } else if (vertexColor4) {
1137:                        colors4 = new Color4f[validVertexCount];
1138:                        for (i = 0; i < validVertexCount; i++)
1139:                            colors4[i] = new Color4f();
1140:
1141:                        ga.getColors(firstVertex, colors4);
1142:                    }
1143:                }
1144:
1145:                public void processVertex(int v, int stripFlag) {
1146:                    Point3f p = positions[v];
1147:                    int r = meshBuffer.getMeshReference(p);
1148:
1149:                    if ((r == meshBuffer.NOT_FOUND)
1150:                            || (vertexNormals && noMeshNormalSubstitution && (!normals[v]
1151:                                    .equals(meshBuffer.getNormal(r))))) {
1152:
1153:                        Vector3f n = vertexNormals ? normals[v] : null;
1154:                        Object c = vertexColor3 ? (Object) colors3[v]
1155:                                : vertexColor4 ? (Object) colors4[v] : null;
1156:
1157:                        addVertex(p, n, c, stripFlag, MESH_PUSH);
1158:                        meshBuffer.push(p, c, n);
1159:                    } else {
1160:                        if (vertexNormals
1161:                                && !noMeshNormalSubstitution
1162:                                && (!normals[v].equals(meshBuffer.getNormal(r))))
1163:                            addNormal(normals[v]);
1164:
1165:                        if (vertexColor3
1166:                                && (!colors3[v].equals(meshBuffer.getColor3(r))))
1167:                            addColor(colors3[v]);
1168:
1169:                        else if (vertexColor4
1170:                                && (!colors4[v].equals(meshBuffer.getColor4(r))))
1171:                            addColor(colors4[v]);
1172:
1173:                        addMeshReference(stripFlag, r);
1174:                    }
1175:                }
1176:            }
1177:
1178:            /**
1179:             * Class which holds index array references for a geometry array.
1180:             */
1181:            private static class IndexArrays {
1182:                int colorIndices[] = null;
1183:                int normalIndices[] = null;
1184:                int positionIndices[] = null;
1185:            }
1186:
1187:            /**
1188:             * Retrieves index array references for the specified IndexedGeometryArray.
1189:             * Index arrays are copied starting from initialIndexIndex.
1190:             */
1191:            private void getIndexArrays(GeometryArray ga, IndexArrays ia) {
1192:                IndexedGeometryArray iga = (IndexedGeometryArray) ga;
1193:
1194:                int initialIndexIndex = iga.getInitialIndexIndex();
1195:                int indexCount = iga.getValidIndexCount();
1196:                int vertexFormat = iga.getVertexFormat();
1197:
1198:                boolean useCoordIndexOnly = false;
1199:                if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) {
1200:                    if (debug)
1201:                        System.out.println("useCoordIndexOnly");
1202:                    useCoordIndexOnly = true;
1203:                }
1204:
1205:                ia.positionIndices = new int[indexCount];
1206:                iga.getCoordinateIndices(initialIndexIndex, ia.positionIndices);
1207:
1208:                if (vertexNormals) {
1209:                    if (useCoordIndexOnly) {
1210:                        ia.normalIndices = ia.positionIndices;
1211:                    } else {
1212:                        ia.normalIndices = new int[indexCount];
1213:                        iga.getNormalIndices(initialIndexIndex,
1214:                                ia.normalIndices);
1215:                    }
1216:                }
1217:                if (vertexColor3 || vertexColor4) {
1218:                    if (useCoordIndexOnly) {
1219:                        ia.colorIndices = ia.positionIndices;
1220:                    } else {
1221:                        ia.colorIndices = new int[indexCount];
1222:                        iga.getColorIndices(initialIndexIndex, ia.colorIndices);
1223:                    }
1224:                }
1225:            }
1226:
1227:            /**
1228:             * Class which holds indices for a specific vertex of an
1229:             * IndexedGeometryArray. 
1230:             */
1231:            private static class VertexIndices {
1232:                int pi, ni, ci;
1233:            }
1234:
1235:            /**
1236:             * Retrieves vertex indices for a specific vertex in an
1237:             * IndexedGeometryArray.
1238:             */
1239:            private void getVertexIndices(int v, IndexArrays ia,
1240:                    VertexIndices vi) {
1241:                vi.pi = ia.positionIndices[v];
1242:                if (vertexNormals)
1243:                    vi.ni = ia.normalIndices[v];
1244:                if (vertexColors)
1245:                    vi.ci = ia.colorIndices[v];
1246:            }
1247:
1248:            /**
1249:             * This class implements the GeometryAccessor interface for indexed
1250:             * geometry arrays accessed with by-copy semantics.
1251:             */
1252:            private class IndexedByCopyGeometry extends ByCopyGeometry {
1253:                IndexArrays ia = new IndexArrays();
1254:                VertexIndices vi = new VertexIndices();
1255:
1256:                IndexedByCopyGeometry(GeometryArray ga) {
1257:                    super (ga, 0, ga.getVertexCount());
1258:                    getIndexArrays(ga, ia);
1259:                }
1260:
1261:                public void processVertex(int v, int stripFlag) {
1262:                    getVertexIndices(v, ia, vi);
1263:                    int r = meshBuffer.getMeshReference(vi.pi);
1264:
1265:                    if ((r == meshBuffer.NOT_FOUND)
1266:                            || (vertexNormals && noMeshNormalSubstitution && (vi.ni != meshBuffer
1267:                                    .getNormalIndex(r)))) {
1268:
1269:                        Point3f p = positions[vi.pi];
1270:                        Vector3f n = vertexNormals ? normals[vi.ni] : null;
1271:                        Object c = vertexColor3 ? (Object) colors3[vi.ci]
1272:                                : vertexColor4 ? (Object) colors4[vi.ci] : null;
1273:
1274:                        addVertex(p, n, c, stripFlag, MESH_PUSH);
1275:                        meshBuffer.push(vi.pi, vi.ci, vi.ni);
1276:                    } else {
1277:                        if (vertexNormals && !noMeshNormalSubstitution
1278:                                && vi.ni != meshBuffer.getNormalIndex(r))
1279:                            addNormal(normals[vi.ni]);
1280:
1281:                        if (vertexColor3
1282:                                && vi.ci != meshBuffer.getColorIndex(r))
1283:                            addColor(colors3[vi.ci]);
1284:
1285:                        else if (vertexColor4
1286:                                && vi.ci != meshBuffer.getColorIndex(r))
1287:                            addColor(colors4[vi.ci]);
1288:
1289:                        addMeshReference(stripFlag, r);
1290:                    }
1291:                }
1292:            }
1293:
1294:            //
1295:            // NOTE: For now, copies are made of all GeometryArray vertex components
1296:            // even when by-reference access is available.  
1297:            //
1298:            private static class VertexCopy {
1299:                Object c = null;
1300:                Point3f p = null;
1301:                Vector3f n = null;
1302:                Color3f c3 = null;
1303:                Color4f c4 = null;
1304:            }
1305:
1306:            private void processVertexCopy(VertexCopy vc, int stripFlag) {
1307:                int r = meshBuffer.getMeshReference(vc.p);
1308:
1309:                if ((r == meshBuffer.NOT_FOUND)
1310:                        || (vertexNormals && noMeshNormalSubstitution && (!vc.n
1311:                                .equals(meshBuffer.getNormal(r))))) {
1312:
1313:                    addVertex(vc.p, vc.n, vc.c, stripFlag, MESH_PUSH);
1314:                    meshBuffer.push(vc.p, vc.c, vc.n);
1315:                } else {
1316:                    if (vertexNormals && !noMeshNormalSubstitution
1317:                            && (!vc.n.equals(meshBuffer.getNormal(r))))
1318:                        addNormal(vc.n);
1319:
1320:                    if (vertexColor3
1321:                            && (!vc.c3.equals(meshBuffer.getColor3(r))))
1322:                        addColor(vc.c3);
1323:
1324:                    else if (vertexColor4
1325:                            && (!vc.c4.equals(meshBuffer.getColor4(r))))
1326:                        addColor(vc.c4);
1327:
1328:                    addMeshReference(stripFlag, r);
1329:                }
1330:            }
1331:
1332:            private void processIndexedVertexCopy(VertexCopy vc,
1333:                    VertexIndices vi, int stripFlag) {
1334:
1335:                int r = meshBuffer.getMeshReference(vi.pi);
1336:
1337:                if ((r == meshBuffer.NOT_FOUND)
1338:                        || (vertexNormals && noMeshNormalSubstitution && (vi.ni != meshBuffer
1339:                                .getNormalIndex(r)))) {
1340:
1341:                    addVertex(vc.p, vc.n, vc.c, stripFlag, MESH_PUSH);
1342:                    meshBuffer.push(vi.pi, vi.ci, vi.ni);
1343:                } else {
1344:                    if (vertexNormals && !noMeshNormalSubstitution
1345:                            && vi.ni != meshBuffer.getNormalIndex(r))
1346:                        addNormal(vc.n);
1347:
1348:                    if (vertexColor3 && vi.ci != meshBuffer.getColorIndex(r))
1349:                        addColor(vc.c3);
1350:
1351:                    else if (vertexColor4
1352:                            && vi.ci != meshBuffer.getColorIndex(r))
1353:                        addColor(vc.c4);
1354:
1355:                    addMeshReference(stripFlag, r);
1356:                }
1357:            }
1358:
1359:            /**
1360:             * This abstract class implements the GeometryAccessor interface for
1361:             * concrete subclasses which handle float and NIO interleaved geometry
1362:             * arrays.
1363:             */
1364:            private abstract class InterleavedGeometry implements 
1365:                    GeometryAccessor {
1366:                VertexCopy vc = new VertexCopy();
1367:
1368:                int vstride = 0;
1369:                int coffset = 0;
1370:                int noffset = 0;
1371:                int poffset = 0;
1372:                int tstride = 0;
1373:                int tcount = 0;
1374:
1375:                InterleavedGeometry(GeometryArray ga) {
1376:                    if (vertexTextures) {
1377:                        if (vertexTexture2)
1378:                            tstride = 2;
1379:                        else if (vertexTexture3)
1380:                            tstride = 3;
1381:                        else if (vertexTexture4)
1382:                            tstride = 4;
1383:
1384:                        tcount = ga.getTexCoordSetCount();
1385:                        vstride += tcount * tstride;
1386:                    }
1387:
1388:                    if (vertexColors) {
1389:                        coffset = vstride;
1390:                        if (vertexColor3)
1391:                            vstride += 3;
1392:                        else
1393:                            vstride += 4;
1394:                    }
1395:
1396:                    if (vertexNormals) {
1397:                        noffset = vstride;
1398:                        vstride += 3;
1399:                    }
1400:
1401:                    poffset = vstride;
1402:                    vstride += 3;
1403:                }
1404:
1405:                abstract void copyVertex(int pi, int ni, int ci, VertexCopy vc);
1406:
1407:                public void processVertex(int v, int stripFlag) {
1408:                    copyVertex(v, v, v, vc);
1409:                    processVertexCopy(vc, stripFlag);
1410:                }
1411:            }
1412:
1413:            /**
1414:             * This class implements the GeometryAccessor interface for float
1415:             * interleaved geometry arrays.
1416:             */
1417:            private class InterleavedGeometryFloat extends InterleavedGeometry {
1418:                float[] vdata = null;
1419:
1420:                InterleavedGeometryFloat(GeometryArray ga) {
1421:                    super (ga);
1422:                    vdata = ga.getInterleavedVertices();
1423:                }
1424:
1425:                void copyVertex(int pi, int ni, int ci, VertexCopy vc) {
1426:                    int voffset;
1427:                    voffset = pi * vstride;
1428:                    vc.p = new Point3f(vdata[voffset + poffset + 0],
1429:                            vdata[voffset + poffset + 1], vdata[voffset
1430:                                    + poffset + 2]);
1431:
1432:                    if (vertexNormals) {
1433:                        voffset = ni * vstride;
1434:                        vc.n = new Vector3f(vdata[voffset + noffset + 0],
1435:                                vdata[voffset + noffset + 1], vdata[voffset
1436:                                        + noffset + 2]);
1437:                    }
1438:                    if (vertexColor3) {
1439:                        voffset = ci * vstride;
1440:                        vc.c3 = new Color3f(vdata[voffset + coffset + 0],
1441:                                vdata[voffset + coffset + 1], vdata[voffset
1442:                                        + coffset + 2]);
1443:                        vc.c = vc.c3;
1444:                    } else if (vertexColor4) {
1445:                        voffset = ci * vstride;
1446:                        vc.c4 = new Color4f(vdata[voffset + coffset + 0],
1447:                                vdata[voffset + coffset + 1], vdata[voffset
1448:                                        + coffset + 2], vdata[voffset + coffset
1449:                                        + 3]);
1450:                        vc.c = vc.c4;
1451:                    }
1452:                }
1453:            }
1454:
1455:            /**
1456:             * This class implements the GeometryAccessor interface for indexed
1457:             * interleaved geometry arrays.
1458:             */
1459:            private class IndexedInterleavedGeometryFloat extends
1460:                    InterleavedGeometryFloat {
1461:
1462:                IndexArrays ia = new IndexArrays();
1463:                VertexIndices vi = new VertexIndices();
1464:
1465:                IndexedInterleavedGeometryFloat(GeometryArray ga) {
1466:                    super (ga);
1467:                    getIndexArrays(ga, ia);
1468:                }
1469:
1470:                public void processVertex(int v, int stripFlag) {
1471:                    getVertexIndices(v, ia, vi);
1472:                    copyVertex(vi.pi, vi.ni, vi.ci, vc);
1473:                    processIndexedVertexCopy(vc, vi, stripFlag);
1474:                }
1475:            }
1476:
1477:            /**
1478:             * This class implements the GeometryAccessor interface for 
1479:             * interleaved NIO geometry arrays.
1480:             */
1481:            private class InterleavedGeometryNIO extends InterleavedGeometry {
1482:                FloatBufferWrapper fbw = null;
1483:
1484:                InterleavedGeometryNIO(GeometryArray ga) {
1485:                    super (ga);
1486:                    J3DBuffer buffer = ga.getInterleavedVertexBuffer();
1487:                    if (BufferWrapper.getBufferType(buffer) == BufferWrapper.TYPE_FLOAT) {
1488:                        fbw = new FloatBufferWrapper(buffer);
1489:                    } else {
1490:                        throw new IllegalArgumentException(
1491:                                "\ninterleaved vertex buffer must be FloatBuffer");
1492:                    }
1493:                }
1494:
1495:                void copyVertex(int pi, int ni, int ci, VertexCopy vc) {
1496:                    int voffset;
1497:                    voffset = pi * vstride;
1498:                    vc.p = new Point3f(fbw.get(voffset + poffset + 0), fbw
1499:                            .get(voffset + poffset + 1), fbw.get(voffset
1500:                            + poffset + 2));
1501:
1502:                    if (vertexNormals) {
1503:                        voffset = ni * vstride;
1504:                        vc.n = new Vector3f(fbw.get(voffset + noffset + 0), fbw
1505:                                .get(voffset + noffset + 1), fbw.get(voffset
1506:                                + noffset + 2));
1507:                    }
1508:                    if (vertexColor3) {
1509:                        voffset = ci * vstride;
1510:                        vc.c3 = new Color3f(fbw.get(voffset + coffset + 0), fbw
1511:                                .get(voffset + coffset + 1), fbw.get(voffset
1512:                                + coffset + 2));
1513:                        vc.c = vc.c3;
1514:                    } else if (vertexColor4) {
1515:                        voffset = ci * vstride;
1516:                        vc.c4 = new Color4f(fbw.get(voffset + coffset + 0), fbw
1517:                                .get(voffset + coffset + 1), fbw.get(voffset
1518:                                + coffset + 2), fbw.get(voffset + coffset + 3));
1519:                        vc.c = vc.c4;
1520:                    }
1521:                }
1522:            }
1523:
1524:            /**
1525:             * This class implements the GeometryAccessor interface for indexed
1526:             * interleaved NIO geometry arrays.
1527:             */
1528:            private class IndexedInterleavedGeometryNIO extends
1529:                    InterleavedGeometryNIO {
1530:                IndexArrays ia = new IndexArrays();
1531:                VertexIndices vi = new VertexIndices();
1532:
1533:                IndexedInterleavedGeometryNIO(GeometryArray ga) {
1534:                    super (ga);
1535:                    getIndexArrays(ga, ia);
1536:                }
1537:
1538:                public void processVertex(int v, int stripFlag) {
1539:                    getVertexIndices(v, ia, vi);
1540:                    copyVertex(vi.pi, vi.ni, vi.ci, vc);
1541:                    processIndexedVertexCopy(vc, vi, stripFlag);
1542:                }
1543:            }
1544:
1545:            /**
1546:             * This class implements the GeometryAccessor interface for
1547:             * non-interleaved geometry arrays accessed with by-reference semantics.
1548:             */
1549:            private class ByRefGeometry implements  GeometryAccessor {
1550:                VertexCopy vc = new VertexCopy();
1551:
1552:                byte[] colorsB = null;
1553:                float[] colorsF = null;
1554:                float[] normals = null;
1555:                float[] positionsF = null;
1556:                double[] positionsD = null;
1557:
1558:                int initialPositionIndex = 0;
1559:                int initialNormalIndex = 0;
1560:                int initialColorIndex = 0;
1561:
1562:                ByRefGeometry(GeometryArray ga) {
1563:                    positionsF = ga.getCoordRefFloat();
1564:                    if (debug && positionsF != null)
1565:                        System.out.println("float positions");
1566:
1567:                    positionsD = ga.getCoordRefDouble();
1568:                    if (debug && positionsD != null)
1569:                        System.out.println("double positions");
1570:
1571:                    if (positionsF == null && positionsD == null)
1572:                        throw new UnsupportedOperationException(
1573:                                "\nby-reference access to Point3{d,f} arrays");
1574:
1575:                    initialPositionIndex = ga.getInitialCoordIndex();
1576:
1577:                    if (vertexColors) {
1578:                        colorsB = ga.getColorRefByte();
1579:                        if (debug && colorsB != null)
1580:                            System.out.println("byte colors");
1581:
1582:                        colorsF = ga.getColorRefFloat();
1583:                        if (debug && colorsF != null)
1584:                            System.out.println("float colors");
1585:
1586:                        if (colorsB == null && colorsF == null)
1587:                            throw new UnsupportedOperationException(
1588:                                    "\nby-reference access to Color{3b,3f,4b,4f} arrays");
1589:
1590:                        initialColorIndex = ga.getInitialColorIndex();
1591:                    }
1592:
1593:                    if (vertexNormals) {
1594:                        normals = ga.getNormalRefFloat();
1595:                        if (debug && normals != null)
1596:                            System.out.println("float normals");
1597:
1598:                        if (normals == null)
1599:                            throw new UnsupportedOperationException(
1600:                                    "\nby-reference access to Normal3f array");
1601:
1602:                        initialNormalIndex = ga.getInitialNormalIndex();
1603:                    }
1604:                }
1605:
1606:                void copyVertex(int pi, int ni, int ci, VertexCopy vc) {
1607:                    pi *= 3;
1608:                    if (positionsF != null) {
1609:                        vc.p = new Point3f(positionsF[pi + 0],
1610:                                positionsF[pi + 1], positionsF[pi + 2]);
1611:                    } else {
1612:                        vc.p = new Point3f((float) positionsD[pi + 0],
1613:                                (float) positionsD[pi + 1],
1614:                                (float) positionsD[pi + 2]);
1615:                    }
1616:
1617:                    ni *= 3;
1618:                    if (vertexNormals) {
1619:                        vc.n = new Vector3f(normals[ni + 0], normals[ni + 1],
1620:                                normals[ni + 2]);
1621:                    }
1622:
1623:                    if (vertexColor3) {
1624:                        ci *= 3;
1625:                        if (colorsB != null) {
1626:                            vc.c3 = new Color3f(
1627:                                    (colorsB[ci + 0] & 0xff) * ByteToFloatScale,
1628:                                    (colorsB[ci + 1] & 0xff) * ByteToFloatScale,
1629:                                    (colorsB[ci + 2] & 0xff) * ByteToFloatScale);
1630:                        } else {
1631:                            vc.c3 = new Color3f(colorsF[ci + 0],
1632:                                    colorsF[ci + 1], colorsF[ci + 2]);
1633:                        }
1634:                        vc.c = vc.c3;
1635:                    } else if (vertexColor4) {
1636:                        ci *= 4;
1637:                        if (colorsB != null) {
1638:                            vc.c4 = new Color4f(
1639:                                    (colorsB[ci + 0] & 0xff) * ByteToFloatScale,
1640:                                    (colorsB[ci + 1] & 0xff) * ByteToFloatScale,
1641:                                    (colorsB[ci + 2] & 0xff) * ByteToFloatScale,
1642:                                    (colorsB[ci + 3] & 0xff) * ByteToFloatScale);
1643:                        } else {
1644:                            vc.c4 = new Color4f(colorsF[ci + 0],
1645:                                    colorsF[ci + 1], colorsF[ci + 2],
1646:                                    colorsF[ci + 3]);
1647:                        }
1648:                        vc.c = vc.c4;
1649:                    }
1650:                }
1651:
1652:                public void processVertex(int v, int stripFlag) {
1653:                    copyVertex(v + initialPositionIndex,
1654:                            v + initialNormalIndex, v + initialColorIndex, vc);
1655:
1656:                    processVertexCopy(vc, stripFlag);
1657:                }
1658:            }
1659:
1660:            /**
1661:             * This class implements the GeometryAccessor interface for indexed
1662:             * non-interleaved geometry arrays accessed with by-reference semantics.
1663:             */
1664:            private class IndexedByRefGeometry extends ByRefGeometry {
1665:                IndexArrays ia = new IndexArrays();
1666:                VertexIndices vi = new VertexIndices();
1667:
1668:                IndexedByRefGeometry(GeometryArray ga) {
1669:                    super (ga);
1670:                    getIndexArrays(ga, ia);
1671:                }
1672:
1673:                public void processVertex(int v, int stripFlag) {
1674:                    getVertexIndices(v, ia, vi);
1675:                    copyVertex(vi.pi, vi.ni, vi.ci, vc);
1676:                    processIndexedVertexCopy(vc, vi, stripFlag);
1677:                }
1678:            }
1679:
1680:            /**
1681:             * This class implements the GeometryAccessor interface for
1682:             * non-interleaved geometry arrays accessed with NIO.
1683:             */
1684:            private class ByRefGeometryNIO implements  GeometryAccessor {
1685:                VertexCopy vc = new VertexCopy();
1686:
1687:                ByteBufferWrapper colorsB = null;
1688:                FloatBufferWrapper colorsF = null;
1689:                FloatBufferWrapper normals = null;
1690:                FloatBufferWrapper positionsF = null;
1691:                DoubleBufferWrapper positionsD = null;
1692:
1693:                int initialPositionIndex = 0;
1694:                int initialNormalIndex = 0;
1695:                int initialColorIndex = 0;
1696:
1697:                ByRefGeometryNIO(GeometryArray ga) {
1698:                    J3DBuffer buffer;
1699:                    buffer = ga.getCoordRefBuffer();
1700:                    initialPositionIndex = ga.getInitialCoordIndex();
1701:
1702:                    switch (BufferWrapper.getBufferType(buffer)) {
1703:                    case BufferWrapper.TYPE_FLOAT:
1704:                        positionsF = new FloatBufferWrapper(buffer);
1705:                        if (debug)
1706:                            System.out.println("float positions buffer");
1707:                        break;
1708:                    case BufferWrapper.TYPE_DOUBLE:
1709:                        positionsD = new DoubleBufferWrapper(buffer);
1710:                        if (debug)
1711:                            System.out.println("double positions buffer");
1712:                        break;
1713:                    default:
1714:                        throw new IllegalArgumentException(
1715:                                "\nposition buffer must be FloatBuffer or DoubleBuffer");
1716:                    }
1717:
1718:                    if (vertexColors) {
1719:                        buffer = ga.getColorRefBuffer();
1720:                        initialColorIndex = ga.getInitialColorIndex();
1721:
1722:                        switch (BufferWrapper.getBufferType(buffer)) {
1723:                        case BufferWrapper.TYPE_BYTE:
1724:                            colorsB = new ByteBufferWrapper(buffer);
1725:                            if (debug)
1726:                                System.out.println("byte colors buffer");
1727:                            break;
1728:                        case BufferWrapper.TYPE_FLOAT:
1729:                            colorsF = new FloatBufferWrapper(buffer);
1730:                            if (debug)
1731:                                System.out.println("float colors buffer");
1732:                            break;
1733:                        default:
1734:                            throw new IllegalArgumentException(
1735:                                    "\ncolor buffer must be ByteBuffer or FloatBuffer");
1736:                        }
1737:                    }
1738:
1739:                    if (vertexNormals) {
1740:                        buffer = ga.getNormalRefBuffer();
1741:                        initialNormalIndex = ga.getInitialNormalIndex();
1742:
1743:                        switch (BufferWrapper.getBufferType(buffer)) {
1744:                        case BufferWrapper.TYPE_FLOAT:
1745:                            normals = new FloatBufferWrapper(buffer);
1746:                            if (debug)
1747:                                System.out.println("float normals buffer");
1748:                            break;
1749:                        default:
1750:                            throw new IllegalArgumentException(
1751:                                    "\nnormal buffer must be FloatBuffer");
1752:                        }
1753:                    }
1754:                }
1755:
1756:                void copyVertex(int pi, int ni, int ci, VertexCopy vc) {
1757:                    pi *= 3;
1758:                    if (positionsF != null) {
1759:                        vc.p = new Point3f(positionsF.get(pi + 0), positionsF
1760:                                .get(pi + 1), positionsF.get(pi + 2));
1761:                    } else {
1762:                        vc.p = new Point3f((float) positionsD.get(pi + 0),
1763:                                (float) positionsD.get(pi + 1),
1764:                                (float) positionsD.get(pi + 2));
1765:                    }
1766:
1767:                    ni *= 3;
1768:                    if (vertexNormals) {
1769:                        vc.n = new Vector3f(normals.get(ni + 0), normals
1770:                                .get(ni + 1), normals.get(ni + 2));
1771:                    }
1772:
1773:                    if (vertexColor3) {
1774:                        ci *= 3;
1775:                        if (colorsB != null) {
1776:                            vc.c3 = new Color3f((colorsB.get(ci + 0) & 0xff)
1777:                                    * ByteToFloatScale,
1778:                                    (colorsB.get(ci + 1) & 0xff)
1779:                                            * ByteToFloatScale, (colorsB
1780:                                            .get(ci + 2) & 0xff)
1781:                                            * ByteToFloatScale);
1782:                        } else {
1783:                            vc.c3 = new Color3f(colorsF.get(ci + 0), colorsF
1784:                                    .get(ci + 1), colorsF.get(ci + 2));
1785:                        }
1786:                        vc.c = vc.c3;
1787:                    } else if (vertexColor4) {
1788:                        ci *= 4;
1789:                        if (colorsB != null) {
1790:                            vc.c4 = new Color4f((colorsB.get(ci + 0) & 0xff)
1791:                                    * ByteToFloatScale,
1792:                                    (colorsB.get(ci + 1) & 0xff)
1793:                                            * ByteToFloatScale, (colorsB
1794:                                            .get(ci + 2) & 0xff)
1795:                                            * ByteToFloatScale, (colorsB
1796:                                            .get(ci + 3) & 0xff)
1797:                                            * ByteToFloatScale);
1798:                        } else {
1799:                            vc.c4 = new Color4f(colorsF.get(ci + 0), colorsF
1800:                                    .get(ci + 1), colorsF.get(ci + 2), colorsF
1801:                                    .get(ci + 3));
1802:                        }
1803:                        vc.c = vc.c4;
1804:                    }
1805:                }
1806:
1807:                public void processVertex(int v, int stripFlag) {
1808:                    copyVertex(v + initialPositionIndex,
1809:                            v + initialNormalIndex, v + initialColorIndex, vc);
1810:
1811:                    processVertexCopy(vc, stripFlag);
1812:                }
1813:            }
1814:
1815:            /**
1816:             * This class implements the GeometryAccessor interface for
1817:             * non-interleaved indexed geometry arrays accessed with NIO.
1818:             */
1819:            private class IndexedByRefGeometryNIO extends ByRefGeometryNIO {
1820:                IndexArrays ia = new IndexArrays();
1821:                VertexIndices vi = new VertexIndices();
1822:
1823:                IndexedByRefGeometryNIO(GeometryArray ga) {
1824:                    super (ga);
1825:                    getIndexArrays(ga, ia);
1826:                }
1827:
1828:                public void processVertex(int v, int stripFlag) {
1829:                    getVertexIndices(v, ia, vi);
1830:                    copyVertex(vi.pi, vi.ni, vi.ci, vc);
1831:                    processIndexedVertexCopy(vc, vi, stripFlag);
1832:                }
1833:            }
1834:
1835:            /**
1836:             * Convert a GeometryArray to compression stream elements and add them to
1837:             * this stream.
1838:             *
1839:             * @param ga GeometryArray to convert
1840:             * @exception IllegalArgumentException if GeometryArray has a
1841:             * dimensionality or vertex format inconsistent with the CompressionStream
1842:             */
1843:            void addGeometryArray(GeometryArray ga) {
1844:                int firstVertex = 0;
1845:                int validVertexCount = 0;
1846:                int vertexFormat = ga.getVertexFormat();
1847:                GeometryAccessor geometryAccessor = null;
1848:
1849:                if (streamType != getStreamType(ga))
1850:                    throw new IllegalArgumentException(
1851:                            "GeometryArray has inconsistent dimensionality");
1852:
1853:                if (vertexComponents != getVertexComponents(vertexFormat))
1854:                    throw new IllegalArgumentException(
1855:                            "GeometryArray has inconsistent vertex components");
1856:
1857:                // Set up for vertex data access semantics.
1858:                boolean NIO = (vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0;
1859:                boolean byRef = (vertexFormat & GeometryArray.BY_REFERENCE) != 0;
1860:                boolean interleaved = (vertexFormat & GeometryArray.INTERLEAVED) != 0;
1861:                boolean indexedGeometry = ga instanceof  IndexedGeometryArray;
1862:
1863:                if (indexedGeometry) {
1864:                    if (debug)
1865:                        System.out.println("indexed");
1866:                    // Index arrays will be copied such that valid indices start at
1867:                    // offset 0 in the copied arrays.
1868:                    firstVertex = 0;
1869:                    validVertexCount = ((IndexedGeometryArray) ga)
1870:                            .getValidIndexCount();
1871:                }
1872:
1873:                if (!byRef) {
1874:                    if (debug)
1875:                        System.out.println("by-copy");
1876:                    if (indexedGeometry) {
1877:                        geometryAccessor = new IndexedByCopyGeometry(ga);
1878:                    } else {
1879:                        firstVertex = 0;
1880:                        validVertexCount = ga.getValidVertexCount();
1881:                        geometryAccessor = new ByCopyGeometry(ga);
1882:                    }
1883:                } else if (interleaved && NIO) {
1884:                    if (debug)
1885:                        System.out.println("interleaved NIO");
1886:                    if (indexedGeometry) {
1887:                        geometryAccessor = new IndexedInterleavedGeometryNIO(ga);
1888:                    } else {
1889:                        firstVertex = ga.getInitialVertexIndex();
1890:                        validVertexCount = ga.getValidVertexCount();
1891:                        geometryAccessor = new InterleavedGeometryNIO(ga);
1892:                    }
1893:                } else if (interleaved && !NIO) {
1894:                    if (debug)
1895:                        System.out.println("interleaved");
1896:                    if (indexedGeometry) {
1897:                        geometryAccessor = new IndexedInterleavedGeometryFloat(
1898:                                ga);
1899:                    } else {
1900:                        firstVertex = ga.getInitialVertexIndex();
1901:                        validVertexCount = ga.getValidVertexCount();
1902:                        geometryAccessor = new InterleavedGeometryFloat(ga);
1903:                    }
1904:                } else if (!interleaved && NIO) {
1905:                    if (debug)
1906:                        System.out.println("non-interleaved NIO");
1907:                    if (indexedGeometry) {
1908:                        geometryAccessor = new IndexedByRefGeometryNIO(ga);
1909:                    } else {
1910:                        firstVertex = 0;
1911:                        validVertexCount = ga.getValidVertexCount();
1912:                        geometryAccessor = new ByRefGeometryNIO(ga);
1913:                    }
1914:                } else if (!interleaved && !NIO) {
1915:                    if (debug)
1916:                        System.out.println("non-interleaved by-ref");
1917:                    if (indexedGeometry) {
1918:                        geometryAccessor = new IndexedByRefGeometry(ga);
1919:                    } else {
1920:                        firstVertex = 0;
1921:                        validVertexCount = ga.getValidVertexCount();
1922:                        geometryAccessor = new ByRefGeometry(ga);
1923:                    }
1924:                }
1925:
1926:                // Set up for topology.
1927:                int stripCount = 0;
1928:                int stripCounts[] = null;
1929:                int constantStripLength = 0;
1930:                int replaceCode = RESTART;
1931:                boolean strips = false;
1932:                boolean implicitStrips = false;
1933:
1934:                if (ga instanceof  TriangleStripArray
1935:                        || ga instanceof  IndexedTriangleStripArray
1936:                        || ga instanceof  LineStripArray
1937:                        || ga instanceof  IndexedLineStripArray) {
1938:
1939:                    strips = true;
1940:                    replaceCode = REPLACE_OLDEST;
1941:                    if (debug)
1942:                        System.out.println("strips");
1943:                } else if (ga instanceof  TriangleFanArray
1944:                        || ga instanceof  IndexedTriangleFanArray) {
1945:
1946:                    strips = true;
1947:                    replaceCode = REPLACE_MIDDLE;
1948:                    if (debug)
1949:                        System.out.println("fans");
1950:                } else if (ga instanceof  QuadArray
1951:                        || ga instanceof  IndexedQuadArray) {
1952:
1953:                    // Handled as fan arrays with 4 vertices per fan.
1954:                    implicitStrips = true;
1955:                    constantStripLength = 4;
1956:                    replaceCode = REPLACE_MIDDLE;
1957:                    if (debug)
1958:                        System.out.println("quads");
1959:                }
1960:
1961:                // Get strip counts.
1962:                if (strips) {
1963:                    if (indexedGeometry) {
1964:                        IndexedGeometryStripArray igsa;
1965:                        igsa = (IndexedGeometryStripArray) ga;
1966:
1967:                        stripCount = igsa.getNumStrips();
1968:                        stripCounts = new int[stripCount];
1969:                        igsa.getStripIndexCounts(stripCounts);
1970:
1971:                    } else {
1972:                        GeometryStripArray gsa;
1973:                        gsa = (GeometryStripArray) ga;
1974:
1975:                        stripCount = gsa.getNumStrips();
1976:                        stripCounts = new int[stripCount];
1977:                        gsa.getStripVertexCounts(stripCounts);
1978:                    }
1979:                }
1980:
1981:                // Build the compression stream for this shape's geometry.
1982:                int v = firstVertex;
1983:                if (strips) {
1984:                    for (int i = 0; i < stripCount; i++) {
1985:                        geometryAccessor.processVertex(v++, RESTART);
1986:                        for (int j = 1; j < stripCounts[i]; j++) {
1987:                            geometryAccessor.processVertex(v++, replaceCode);
1988:                        }
1989:                    }
1990:                } else if (implicitStrips) {
1991:                    while (v < firstVertex + validVertexCount) {
1992:                        geometryAccessor.processVertex(v++, RESTART);
1993:                        for (int j = 1; j < constantStripLength; j++) {
1994:                            geometryAccessor.processVertex(v++, replaceCode);
1995:                        }
1996:                    }
1997:                } else {
1998:                    while (v < firstVertex + validVertexCount) {
1999:                        geometryAccessor.processVertex(v++, RESTART);
2000:                    }
2001:                }
2002:            }
2003:
2004:            /**
2005:             * Print the stream to standard output.
2006:             */
2007:            void print() {
2008:                System.out
2009:                        .println("\nstream has " + stream.size() + " entries");
2010:                System.out.println("uncompressed size " + byteCount + " bytes");
2011:                System.out.println("upper position bound: "
2012:                        + mcBounds[1].toString());
2013:                System.out.println("lower position bound: "
2014:                        + mcBounds[0].toString());
2015:                System.out.println("X, Y, Z centers (" + ((float) center[0])
2016:                        + " " + ((float) center[1]) + " " + ((float) center[2])
2017:                        + ")\n" + "scale " + ((float) scale) + "\n");
2018:
2019:                Iterator i = stream.iterator();
2020:                while (i.hasNext()) {
2021:                    System.out.println(i.next().toString() + "\n");
2022:                }
2023:            }
2024:
2025:            ////////////////////////////////////////////////////////////////////////////
2026:            //									      //
2027:            // The following constructors and methods are currently the only public   //
2028:            // members of this class.  All other members are subject to revision.     //
2029:            //									      //
2030:            ////////////////////////////////////////////////////////////////////////////
2031:
2032:            /**
2033:             * Creates a CompressionStream from an array of Shape3D scene graph
2034:             * objects.  These Shape3D objects may only consist of a GeometryArray
2035:             * component and an optional Appearance component.  The resulting stream
2036:             * may be used as input to the GeometryCompressor methods.<p>
2037:             *
2038:             * Each Shape3D in the array must be of the same dimensionality (point,
2039:             * line, or surface) and have the same vertex format as the others.
2040:             * Texture coordinates are ignored.<p>
2041:             *
2042:             * If a color is specified in the material attributes for a Shape3D then
2043:             * that color is added to the CompressionStream as the current global
2044:             * color.  Subsequent colors as well as any colors bundled with vertices
2045:             * will override it.  Only the material diffuse colors are used; all other
2046:             * appearance attributes are ignored.<p>
2047:             *
2048:             * @param positionQuant
2049:             * number of bits to quantize each position's X, Y,
2050:             * and Z components, ranging from 1 to 16
2051:             *
2052:             * @param colorQuant
2053:             * number of bits to quantize each color's R, G, B, and
2054:             * alpha components, ranging from 2 to 16
2055:             *
2056:             * @param normalQuant
2057:             * number of bits for quantizing each normal's U and V components, ranging
2058:             * from 0 to 6
2059:             *
2060:             * @param shapes
2061:             * an array of Shape3D scene graph objects containing
2062:             * GeometryArray objects, all with the same vertex format and
2063:             * dimensionality
2064:             *
2065:             * @exception IllegalArgumentException if any Shape3D has an inconsistent
2066:             * dimensionality or vertex format, or if any Shape3D contains a geometry
2067:             * component that is not a GeometryArray
2068:             *
2069:             * @see Shape3D
2070:             * @see GeometryArray
2071:             * @see GeometryCompressor
2072:             */
2073:            public CompressionStream(int positionQuant, int colorQuant,
2074:                    int normalQuant, Shape3D shapes[]) {
2075:                this ();
2076:                if (debug)
2077:                    System.out.println("CompressionStream(Shape3D[]):");
2078:
2079:                if (shapes == null)
2080:                    throw new IllegalArgumentException("null Shape3D array");
2081:
2082:                if (shapes.length == 0)
2083:                    throw new IllegalArgumentException(
2084:                            "zero-length Shape3D array");
2085:
2086:                if (shapes[0] == null)
2087:                    throw new IllegalArgumentException(
2088:                            "Shape3D at index 0 is null");
2089:
2090:                long startTime = 0;
2091:                if (benchmark)
2092:                    startTime = System.currentTimeMillis();
2093:
2094:                Geometry g = shapes[0].getGeometry();
2095:                if (!(g instanceof  GeometryArray))
2096:                    throw new IllegalArgumentException(
2097:                            "Shape3D at index 0 is not a GeometryArray");
2098:
2099:                GeometryArray ga = (GeometryArray) g;
2100:                this .streamType = getStreamType(ga);
2101:                this .vertexComponents = getVertexComponents(ga
2102:                        .getVertexFormat());
2103:
2104:                // Add global quantization parameters to the start of the stream.
2105:                addPositionQuantization(positionQuant);
2106:                addColorQuantization(colorQuant);
2107:                addNormalQuantization(normalQuant);
2108:
2109:                // Loop through all shapes.
2110:                for (int s = 0; s < shapes.length; s++) {
2111:                    if (debug)
2112:                        System.out.println("\nShape3D " + s + ":");
2113:
2114:                    g = shapes[s].getGeometry();
2115:                    if (!(g instanceof  GeometryArray))
2116:                        throw new IllegalArgumentException("Shape3D at index "
2117:                                + s + " is not a GeometryArray");
2118:
2119:                    // Check for material color and add it to the stream if it exists.
2120:                    Appearance a = shapes[s].getAppearance();
2121:                    if (a != null) {
2122:                        Material m = a.getMaterial();
2123:                        if (m != null) {
2124:                            m.getDiffuseColor(c3f);
2125:                            if (vertexColor4) {
2126:                                c4f.set(c3f.x, c3f.y, c3f.z, 1.0f);
2127:                                addColor(c4f);
2128:                            } else
2129:                                addColor(c3f);
2130:                        }
2131:                    }
2132:
2133:                    // Add the geometry array to the stream.
2134:                    addGeometryArray((GeometryArray) g);
2135:                }
2136:
2137:                if (benchmark) {
2138:                    long t = System.currentTimeMillis() - startTime;
2139:                    System.out.println("\nCompressionStream:\n" + shapes.length
2140:                            + " shapes in " + (t / 1000f) + " sec");
2141:                }
2142:            }
2143:
2144:            /**
2145:             * Creates a CompressionStream from an array of Shape3D scene graph
2146:             * objects.  These Shape3D objects may only consist of a GeometryArray
2147:             * component and an optional Appearance component.  The resulting stream
2148:             * may be used as input to the GeometryCompressor methods.<p>
2149:             *
2150:             * Each Shape3D in the array must be of the same dimensionality (point,
2151:             * line, or surface) and have the same vertex format as the others.
2152:             * Texture coordinates are ignored.<p>
2153:             *
2154:             * If a color is specified in the material attributes for a Shape3D then
2155:             * that color is added to the CompressionStream as the current global
2156:             * color.  Subsequent colors as well as any colors bundled with vertices
2157:             * will override it.  Only the material diffuse colors are used; all other
2158:             * appearance attributes are ignored.<p>
2159:             *
2160:             * Defaults of 16, 9, and 6 bits are used as the quantization values for
2161:             * positions, colors, and normals respectively.  These are the maximum
2162:             * resolution values defined for positions and normals; the default of 9
2163:             * for color is the equivalent of the 8 bits of RGBA component resolution
2164:             * commonly available in graphics frame buffers.<p>
2165:             *
2166:             * @param shapes
2167:             * an array of Shape3D scene graph objects containing
2168:             * GeometryArray objects, all with the same vertex format and
2169:             * dimensionality.
2170:             *
2171:             * @exception IllegalArgumentException if any Shape3D has an inconsistent
2172:             * dimensionality or vertex format, or if any Shape3D contains a geometry
2173:             * component that is not a GeometryArray
2174:             *
2175:             * @see Shape3D
2176:             * @see GeometryArray
2177:             * @see GeometryCompressor
2178:             */
2179:            public CompressionStream(Shape3D shapes[]) {
2180:                this (16, 9, 6, shapes);
2181:            }
2182:
2183:            /**
2184:             * Creates a CompressionStream from an array of GeometryInfo objects.  The
2185:             * resulting stream may be used as input to the GeometryCompressor
2186:             * methods.<p>
2187:             *
2188:             * Each GeometryInfo in the array must be of the same dimensionality
2189:             * (point, line, or surface) and have the same vertex format as the
2190:             * others.  Texture coordinates are ignored.<p>
2191:             *
2192:             * @param positionQuant
2193:             * number of bits to quantize each position's X, Y,
2194:             * and Z components, ranging from 1 to 16
2195:             *
2196:             * @param colorQuant
2197:             * number of bits to quantize each color's R, G, B, and
2198:             * alpha components, ranging from 2 to 16
2199:             *
2200:             * @param normalQuant
2201:             * number of bits for quantizing each normal's U and V components, ranging
2202:             * from 0 to 6
2203:             *
2204:             * @param geometry
2205:             * an array of GeometryInfo objects, all with the same
2206:             * vertex format and dimensionality
2207:             *
2208:             * @exception IllegalArgumentException if any GeometryInfo object has an
2209:             * inconsistent dimensionality or vertex format
2210:             *
2211:             * @see GeometryInfo
2212:             * @see GeometryCompressor
2213:             */
2214:            public CompressionStream(int positionQuant, int colorQuant,
2215:                    int normalQuant, GeometryInfo geometry[]) {
2216:                this ();
2217:                if (debug)
2218:                    System.out.println("CompressionStream(GeometryInfo[])");
2219:
2220:                if (geometry == null)
2221:                    throw new IllegalArgumentException(
2222:                            "null GeometryInfo array");
2223:
2224:                if (geometry.length == 0)
2225:                    throw new IllegalArgumentException(
2226:                            "zero-length GeometryInfo array");
2227:
2228:                if (geometry[0] == null)
2229:                    throw new IllegalArgumentException(
2230:                            "GeometryInfo at index 0 is null");
2231:
2232:                long startTime = 0;
2233:                if (benchmark)
2234:                    startTime = System.currentTimeMillis();
2235:
2236:                GeometryArray ga = geometry[0].getGeometryArray();
2237:                this .streamType = getStreamType(ga);
2238:                this .vertexComponents = getVertexComponents(ga
2239:                        .getVertexFormat());
2240:
2241:                // Add global quantization parameters to the start of the stream.
2242:                addPositionQuantization(positionQuant);
2243:                addColorQuantization(colorQuant);
2244:                addNormalQuantization(normalQuant);
2245:
2246:                // Loop through all GeometryInfo objects and add them to the stream.
2247:                for (int i = 0; i < geometry.length; i++) {
2248:                    if (debug)
2249:                        System.out.println("\nGeometryInfo " + i + ":");
2250:                    addGeometryArray(geometry[i].getGeometryArray());
2251:                }
2252:
2253:                if (benchmark) {
2254:                    long t = System.currentTimeMillis() - startTime;
2255:                    System.out.println("\nCompressionStream:\n"
2256:                            + geometry.length + " GeometryInfo objects in "
2257:                            + (t / 1000f) + " sec");
2258:                }
2259:            }
2260:
2261:            /**
2262:             * Creates a CompressionStream from an array of GeometryInfo objects.  The
2263:             * resulting stream may be used as input to the GeometryCompressor
2264:             * methods.<p>
2265:             *
2266:             * Each GeometryInfo in the array must be of the same dimensionality
2267:             * (point, line, or surface) and have the same vertex format as the
2268:             * others.  Texture coordinates are ignored.<p>
2269:             *
2270:             * Defaults of 16, 9, and 6 bits are used as the quantization values for
2271:             * positions, colors, and normals respectively.  These are the maximum
2272:             * resolution values defined for positions and normals; the default of 9
2273:             * for color is the equivalent of the 8 bits of RGBA component resolution
2274:             * commonly available in graphics frame buffers.<p>
2275:             *
2276:             * @param geometry
2277:             * an array of GeometryInfo objects, all with the same
2278:             * vertex format and dimensionality
2279:             *
2280:             * @exception IllegalArgumentException if any GeometryInfo object has an
2281:             * inconsistent dimensionality or vertex format
2282:             *
2283:             * @see GeometryInfo
2284:             * @see GeometryCompressor
2285:             */
2286:            public CompressionStream(GeometryInfo geometry[]) {
2287:                this (16, 9, 6, geometry);
2288:            }
2289:
2290:            /**
2291:             * Get the original bounds of the coordinate data, in modeling coordinates.
2292:             * Coordinate data is positioned and scaled to a normalized cube after
2293:             * compression.  
2294:             * 
2295:             * @return Point3d array of length 2, where the 1st Point3d is the lower
2296:             * bounds and the 2nd Point3d is the upper bounds.
2297:             * @since Java 3D 1.3
2298:             */
2299:            public Point3d[] getModelBounds() {
2300:                Point3d[] bounds = new Point3d[2];
2301:                bounds[0] = new Point3d(mcBounds[0]);
2302:                bounds[1] = new Point3d(mcBounds[1]);
2303:                return bounds;
2304:            }
2305:
2306:            /**
2307:             * Get the bounds of the compressed object in normalized coordinates.
2308:             * These have an maximum bounds by [-1.0 .. +1.0] across each axis.
2309:             * 
2310:             * @return Point3d array of length 2, where the 1st Point3d is the lower
2311:             * bounds and the 2nd Point3d is the upper bounds.
2312:             * @since Java 3D 1.3
2313:             */
2314:            public Point3d[] getNormalizedBounds() {
2315:                Point3d[] bounds = new Point3d[2];
2316:                bounds[0] = new Point3d(ncBounds[0]);
2317:                bounds[1] = new Point3d(ncBounds[1]);
2318:                return bounds;
2319:            }
2320:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.