1 /*
2  * $RCSfile: CompressionStream.java,v $
3  *
4  * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * - Redistribution of source code must retain the above copyright
11  *   notice, this list of conditions and the following disclaimer.
12  *
13  * - Redistribution in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  *
18  * Neither the name of Sun Microsystems, Inc. or the names of
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * This software is provided "AS IS," without a warranty of any
23  * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
24  * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
25  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
26  * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
27  * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
28  * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
29  * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
30  * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
31  * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
32  * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
33  * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGES.
35  *
36  * You acknowledge that this software is not designed, licensed or
37  * intended for use in the design, construction, operation or
38  * maintenance of any nuclear facility.
39  *
40  * $Revision: 1.5 $
41  * $Date: 2007/02/09 17:20:16 $
42  * $State: Exp $
43  */
44 
45 package com.sun.j3d.utils.compression;
46 
47 import com.sun.j3d.internal.BufferWrapper;
48 import com.sun.j3d.internal.ByteBufferWrapper;
49 import com.sun.j3d.internal.DoubleBufferWrapper;
50 import com.sun.j3d.internal.FloatBufferWrapper;
51 import com.sun.j3d.utils.geometry.GeometryInfo;
52 import java.util.Collection;
53 import java.util.Iterator;
54 import java.util.LinkedList;
55 import javax.media.j3d.Appearance;
56 import javax.media.j3d.CompressedGeometryHeader;
57 import javax.media.j3d.Geometry;
58 import javax.media.j3d.GeometryArray;
59 import javax.media.j3d.GeometryStripArray;
60 import javax.media.j3d.IndexedGeometryArray;
61 import javax.media.j3d.IndexedGeometryStripArray;
62 import javax.media.j3d.IndexedLineArray;
63 import javax.media.j3d.IndexedLineStripArray;
64 import javax.media.j3d.IndexedQuadArray;
65 import javax.media.j3d.IndexedTriangleArray;
66 import javax.media.j3d.IndexedTriangleFanArray;
67 import javax.media.j3d.IndexedTriangleStripArray;
68 import javax.media.j3d.J3DBuffer;
69 import javax.media.j3d.LineArray;
70 import javax.media.j3d.LineStripArray;
71 import javax.media.j3d.Material;
72 import javax.media.j3d.QuadArray;
73 import javax.media.j3d.Shape3D;
74 import javax.media.j3d.TriangleArray;
75 import javax.media.j3d.TriangleFanArray;
76 import javax.media.j3d.TriangleStripArray;
77 import javax.vecmath.Color3f;
78 import javax.vecmath.Color4f;
79 import javax.vecmath.Point3d;
80 import javax.vecmath.Point3f;
81 import javax.vecmath.Point3i;
82 import javax.vecmath.Vector3f;
83 
84 /**
85  * This class is used as input to a geometry compressor.  It collects elements
86  * such as vertices, normals, colors, mesh references, and quantization
87  * parameters in an ordered stream.  This stream is then traversed during
88  * the compression process and used to build the compressed output buffer.
89  *
90  * @see GeometryCompressor
91  *
92  * @deprecated As of Java 3D 1.5, replaced by
93  * com.sun.j3d.utils.geometry.compression.{@link com.sun.j3d.utils.geometry.compression.CompressionStream}.
94  */
95 public class CompressionStream {
96     //
97     // NOTE: For now, copies are made of all GeometryArray vertex components
98     // even when by-reference access is available.
99     //
100     // TODO: Retrofit all CompressionStreamElements and MeshBuffer to handle
101     // offsets to vertex data array references so that vertex components don't
102     // have to be copied.  New CompressionStreamElements could be defined to
103     // set the current array reference during the quantization pass, or the
104     // reference could be included in every CompressionStreamElement along
105     // with the data offsets.
106     //
107     // TODO: Quantize on-the-fly when adding GeometryArray vertex data so that
108     // CompressionStreamElements don't need references to the original float,
109     // double, or byte data.  Quantization is currently a separate pass since
110     // the 1st pass adds vertex data and gets the total object bounds, but
111     // this can be computed by merging the bounds of each GeometryArray
112     // compressed into a single object.  The 2nd pass quantization is still
113     // needed for vertex data which isn't retrieved from a GeometryArray; for
114     // example, apps that might use the addVertex() methods directly instead
115     // of addGeometryArray().
116     //
117     // TODO: To further optimize memory, create new subclasses of
118     // CompressionStream{Color, Normal} for bundled attributes and add them as
119     // explicit stream elements.  Then CompressionStreamVertex won't need to
120     // carry references to them.  This memory savings might be negated by the
121     // extra overhead of adding more elements to the stream, however.
122     //
123     // TODO: Keep the absolute quantized values in the mesh buffer mirror so
124     // that unmeshed CompressionStreamElements don't need to carry them.
125     //
126     // TODO: Support texture coordinate compression even though Level II is
127     // not supported by any hardware decompressor on any graphics card.
128     // Software decompression is still useful for applications interested in
129     // minimizing file space, transmission time, and object loading time.
130     //
131     private static final boolean debug = false ;
132     private static final boolean benchmark = false ;
133 
134     // Mesh buffer normal substitution is unavailable in Level I.
135     private static final boolean noMeshNormalSubstitution = true ;
136 
137     /**
138      * This flag indicates that a vertex starts a new triangle or line strip.
139      */
140     static final int RESTART = 1 ;
141 
142     /**
143      * This flag indicates that the next triangle in the strip is defined by
144      * replacing the middle vertex of the previous triangle in the strip.
145      * Equivalent to REPLACE_OLDEST for line strips.
146      */
147     static final int REPLACE_MIDDLE = 2 ;
148 
149     /**
150      * This flag indicates that the next triangle in the strip is defined by
151      * replacing the oldest vertex of the previous triangle in the strip.
152      * Equivalent to REPLACE_MIDDLE for line strips.
153      */
154     static final int REPLACE_OLDEST = 3 ;
155 
156     /**
157      * This flag indicates that a vertex is to be pushed into the mesh buffer.
158      */
159     static final int MESH_PUSH = 1 ;
160 
161     /**
162      * This flag indicates that a vertex does not use the mesh buffer.
163      */
164     static final int NO_MESH_PUSH = 0 ;
165 
166     /**
167      * Byte to float scale factor for scaling byte color components.
168      */
169     static final float ByteToFloatScale = 1.0f/255.0f;
170 
171     /**
172      * Type of this stream, either CompressedGeometryHeader.POINT_BUFFER,
173      * CompressedGeometryHeader.LINE_BUFFER, or
174      * CompressedGeometryHeader.TRIANGLE_BUFFER
175      */
176     int streamType ;
177 
178     /**
179      * A mask indicating which components are present in each vertex, as
180      * defined by GeometryArray.
181      */
182     int vertexComponents ;
183 
184     /**
185      * Boolean indicating colors are bundled with the vertices.
186      */
187     boolean vertexColors ;
188 
189     /**
190      * Boolean indicating RGB colors are bundled with the vertices.
191      */
192     boolean vertexColor3 ;
193 
194     /**
195      * Boolean indicating RGBA colors are bundled with the vertices.
196      */
197     boolean vertexColor4 ;
198 
199     /**
200      * Boolean indicating normals are bundled with the vertices.
201      */
202     boolean vertexNormals ;
203 
204     /**
205      * Boolean indicating texture coordinates are present.
206      */
207     boolean vertexTextures ;
208 
209     /**
210      * Boolean indicating that 2D texture coordinates are used.
211      * Currently only used to skip over textures in interleaved data.
212      */
213     boolean vertexTexture2 ;
214 
215     /**
216      * Boolean indicating that 3D texture coordinates are used.
217      * Currently only used to skip over textures in interleaved data.
218      */
219     boolean vertexTexture3 ;
220 
221     /**
222      * Boolean indicating that 4D texture coordinates are used.
223      * Currently only used to skip over textures in interleaved data.
224      */
225     boolean vertexTexture4 ;
226 
227     /**
228      * Axes-aligned box enclosing all vertices in model coordinates.
229      */
230     Point3d mcBounds[] = new Point3d[2] ;
231 
232     /**
233      * Axes-aligned box enclosing all vertices in normalized coordinates.
234      */
235     Point3d ncBounds[] = new Point3d[2] ;
236 
237     /**
238      * Axes-aligned box enclosing all vertices in quantized coordinates.
239      */
240     Point3i qcBounds[] = new Point3i[2] ;
241 
242     /**
243      * Center for normalizing positions to the unit cube.
244      */
245     double center[] = new double[3] ;
246 
247     /**
248      * Maximum position range along the 3 axes.
249      */
250     double positionRangeMaximum ;
251 
252     /**
253      * Scale for normalizing positions to the unit cube.
254      */
255     double scale ;
256 
257     /**
258      * Current position component (X, Y, and Z) quantization value.  This can
259      * range from 1 to 16 bits and has a default of 16.<p>
260      *
261      * At 1 bit of quantization it is not possible to express positive
262      * absolute or delta positions.
263      */
264     int positionQuant ;
265 
266     /**
267      * Current color component (R, G, B, A) quantization value.  This can
268      * range from 2 to 16 bits and has a default of 9.<p>
269      *
270      * A color component is represented with a signed fixed-point value in
271      * order to be able express negative deltas; the default of 9 bits
272      * corresponds to the 8-bit color component range of the graphics hardware
273      * commonly available.  Colors must be non-negative, so the lower limit of
274      * quantization is 2 bits.
275      */
276     int colorQuant ;
277 
278     /**
279      * Current normal component (U and V) quantization value.  This can range
280      * from 0 to 6 bits and has a default of 6.<p>
281      *
282      * At 0 bits of quantization normals are represented only as 6 bit
283      * sextant/octant pairs and 14 specially encoded normals (the 6 axis
284      * normals and the 8 octant midpoint normals); since U and V can only be 0
285      * at the minimum quantization, the totally number of unique normals is
286      * 12 + 14 = 26.
287      */
288     int normalQuant ;
289 
290     /**
291      * Flag indicating position quantization change.
292      */
293     boolean positionQuantChanged ;
294 
295     /**
296      * Flag indicating color quantization change.
297      */
298     boolean colorQuantChanged ;
299 
300     /**
301      * Flag indicating normal quantization change.
302      */
303     boolean normalQuantChanged ;
304 
305     /**
306      * Last quantized position.
307      */
308     int lastPosition[] = new int[3] ;
309 
310     /**
311      * Last quantized color.
312      */
313     int lastColor[] = new int[4] ;
314 
315     /**
316      * Last quantized normal's sextant.
317      */
318     int lastSextant ;
319 
320     /**
321      * Last quantized normal's octant.
322      */
323     int lastOctant ;
324 
325     /**
326      * Last quantized normal's U encoding parameter.
327      */
328     int lastU ;
329 
330     /**
331      * Last quantized normal's V encoding parameter.
332      */
333     int lastV ;
334 
335     /**
336      * Flag indicating last normal used a special encoding.
337      */
338     boolean lastSpecialNormal ;
339 
340     /**
341      * Flag indicating the first position in this stream.
342      */
343     boolean firstPosition ;
344 
345     /**
346      * Flag indicating the first color in this stream.
347      */
348     boolean firstColor ;
349 
350     /**
351      * Flag indicating the first normal in this stream.
352      */
353     boolean firstNormal ;
354 
355     /**
356      * The total number of bytes used to create the uncompressed geometric
357      * elements in this stream, useful for performance analysis.  This
358      * excludes mesh buffer references.
359      */
360     int byteCount ;
361 
362     /**
363      * The number of vertices created for this stream, excluding mesh buffer
364      * references.
365      */
366     int vertexCount ;
367 
368     /**
369      * The number of mesh buffer references created for this stream.
370      */
371     int meshReferenceCount ;
372 
373     /**
374      * Mesh buffer mirror used for computing deltas during quantization pass
375      * and a limited meshing algorithm for unstripped data.
376      */
377     MeshBuffer meshBuffer = new MeshBuffer() ;
378 
379 
380     // Collection which holds the elements of this stream.
381     private Collection stream ;
382 
383     // True if preceding stream elements were colors or normals.  Used to flag
384     // color and normal mesh buffer substitution when computing deltas during
385     // quantization pass.
386     private boolean lastElementColor = false ;
387     private boolean lastLastElementColor = false ;
388     private boolean lastElementNormal = false ;
389     private boolean lastLastElementNormal = false ;
390 
391     // Some convenient temporary holding variables.
392     private Point3f p3f = new Point3f() ;
393     private Color3f c3f = new Color3f() ;
394     private Color4f c4f = new Color4f() ;
395     private Vector3f n3f = new Vector3f() ;
396 
397 
398     // Private constructor for common initializations.
CompressionStream()399     private CompressionStream() {
400 	this.stream = new LinkedList() ;
401 
402 	byteCount = 0 ;
403 	vertexCount = 0 ;
404 	meshReferenceCount = 0 ;
405 
406 	mcBounds[0] = new Point3d(Double.POSITIVE_INFINITY,
407 				  Double.POSITIVE_INFINITY,
408 				  Double.POSITIVE_INFINITY) ;
409 	mcBounds[1] = new Point3d(Double.NEGATIVE_INFINITY,
410 				  Double.NEGATIVE_INFINITY,
411 				  Double.NEGATIVE_INFINITY) ;
412 
413 	qcBounds[0] = new Point3i(Integer.MAX_VALUE,
414 				  Integer.MAX_VALUE,
415 				  Integer.MAX_VALUE) ;
416 	qcBounds[1] = new Point3i(Integer.MIN_VALUE,
417 				  Integer.MIN_VALUE,
418 				  Integer.MIN_VALUE) ;
419 
420 	/* normalized bounds computed from quantized bounds */
421 	ncBounds[0] = new Point3d() ;
422 	ncBounds[1] = new Point3d() ;
423     }
424 
425     /**
426      * Creates a new CompressionStream for the specified geometry type and
427      * vertex format.<p>
428      *
429      * @param streamType type of data in this stream, either
430      * CompressedGeometryHeader.POINT_BUFFER,
431      * CompressedGeometryHeader.LINE_BUFFER, or
432      * CompressedGeometryHeader.TRIANGLE_BUFFER
433      *
434      * @param vertexComponents a mask indicating which components are present
435      * in each vertex, as defined by GeometryArray: COORDINATES, NORMALS, and
436      * COLOR_3 or COLOR_4.
437      *
438      * @see GeometryCompressor
439      * @see GeometryArray
440      */
CompressionStream(int streamType, int vertexComponents)441     CompressionStream(int streamType, int vertexComponents) {
442 	this() ;
443 	this.streamType = streamType ;
444 	this.vertexComponents = getVertexComponents(vertexComponents) ;
445     }
446 
447     // See what vertex geometry components are present.  The byReference,
448     // interleaved, useNIOBuffer, and useCoordIndexOnly flags are not
449     // examined.
getVertexComponents(int vertexFormat)450     private int getVertexComponents(int vertexFormat) {
451 	int components = 0 ;
452 
453 	vertexColors = vertexColor3 = vertexColor4 = vertexNormals =
454 	    vertexTextures = vertexTexture2 = vertexTexture3 = vertexTexture4 =
455 	    false ;
456 
457 	if ((vertexFormat & GeometryArray.NORMALS) != 0) {
458 	    vertexNormals = true ;
459 	    components &= GeometryArray.NORMALS ;
460 	    if (debug) System.out.println("vertexNormals") ;
461 	}
462 
463 	if ((vertexFormat & GeometryArray.COLOR_3) != 0) {
464 	    vertexColors = true ;
465 
466 	    if ((vertexFormat & GeometryArray.COLOR_4) != 0) {
467 		vertexColor4 = true ;
468 		components &= GeometryArray.COLOR_4 ;
469 		if (debug) System.out.println("vertexColor4") ;
470 	    }
471 	    else {
472 		vertexColor3 = true ;
473 		components &= GeometryArray.COLOR_3 ;
474 		if (debug) System.out.println("vertexColor3") ;
475 	    }
476 	}
477 
478 	if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
479 	    vertexTextures = true ;
480 	    vertexTexture2 = true ;
481 	    components &= GeometryArray.TEXTURE_COORDINATE_2 ;
482 	    if (debug) System.out.println("vertexTexture2") ;
483 	}
484 	else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
485 	    vertexTextures = true ;
486 	    vertexTexture3 = true ;
487 	    components &= GeometryArray.TEXTURE_COORDINATE_3 ;
488 	    if (debug) System.out.println("vertexTexture3") ;
489 	}
490 	else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
491 	    vertexTextures = true ;
492 	    vertexTexture4 = true ;
493 	    components &= GeometryArray.TEXTURE_COORDINATE_4 ;
494 	    if (debug) System.out.println("vertexTexture4") ;
495 	}
496 
497 	if (vertexTextures)
498 	    // Throw exception for now until texture is supported.
499 	    throw new UnsupportedOperationException
500 		("\ncompression of texture coordinates is not supported") ;
501 
502 	return components ;
503     }
504 
505     // Get the streamType associated with a GeometryArray instance.
getStreamType(GeometryArray ga)506     private int getStreamType(GeometryArray ga) {
507 	if (ga instanceof TriangleStripArray ||
508 	    ga instanceof IndexedTriangleStripArray ||
509 	    ga instanceof TriangleFanArray ||
510 	    ga instanceof IndexedTriangleFanArray ||
511 	    ga instanceof TriangleArray ||
512 	    ga instanceof IndexedTriangleArray ||
513 	    ga instanceof QuadArray ||
514 	    ga instanceof IndexedQuadArray)
515 
516 	    return CompressedGeometryHeader.TRIANGLE_BUFFER ;
517 
518 	else if (ga instanceof LineArray ||
519 		 ga instanceof IndexedLineArray ||
520 		 ga instanceof LineStripArray ||
521 		 ga instanceof IndexedLineStripArray)
522 
523 	    return CompressedGeometryHeader.LINE_BUFFER ;
524 
525 	else
526 	    return CompressedGeometryHeader.POINT_BUFFER ;
527     }
528 
529     /**
530      * Iterates across all compression stream elements and applies
531      * quantization parameters, encoding consecutive vertices as delta values
532      * whenever possible.  Each geometric element is mapped to a HuffmanNode
533      * object containing its resulting bit length, right shift (trailing 0
534      * count), and absolute or relative status.<p>
535      *
536      * Positions are normalized to span a unit cube via an offset and a
537      * uniform scale factor that maps the midpoint of the object extents along
538      * each dimension to the origin, and the longest dimension of the object to
539      * the open interval (-1.0 .. +1.0).  The geometric endpoints along that
540      * dimension are both one quantum away from unity; for example, at a
541      * position quantization of 6 bits, an object would be normalized so that
542      * its most negative dimension is at (-1 + 1/64) and the most positive is
543      * at (1 - 1/64).<p>
544      *
545      * Normals are assumed to be of unit length.  Color components are clamped
546      * to the [0..1) range, where the right endpoint is one quantum less
547      * than 1.0.<p>
548      *
549      * @param huffmanTable Table which will map geometric compression stream
550      * elements to HuffmanNode objects describing each element's data
551      * representation.  This table can then be processed with Huffman's
552      * algorithm to optimize the bit length of descriptor tags according to
553      * the number of geometric elements mapped to each tag.
554      */
quantize(HuffmanTable huffmanTable)555     void quantize(HuffmanTable huffmanTable) {
556 	// Set up default initial quantization parameters.  The position and
557 	// color parameters specify the number of bits for each X, Y, Z, R, G,
558 	// B, or A component.  The normal quantization parameter specifies the
559 	// number of bits for each U and V component.
560 	positionQuant = 16 ;
561 	colorQuant = 9 ;
562 	normalQuant = 6 ;
563 
564 	// Compute position center and scaling for normalization to the unit
565 	// cube.  This is a volume bounded by the open intervals (-1..1) on
566 	// each axis.
567 	center[0] = (mcBounds[1].x + mcBounds[0].x) / 2.0 ;
568 	center[1] = (mcBounds[1].y + mcBounds[0].y) / 2.0 ;
569 	center[2] = (mcBounds[1].z + mcBounds[0].z) / 2.0 ;
570 
571 	double xRange = mcBounds[1].x - mcBounds[0].x ;
572 	double yRange = mcBounds[1].y - mcBounds[0].y ;
573 	double zRange = mcBounds[1].z - mcBounds[0].z ;
574 
575 	if (xRange > yRange)
576 	    positionRangeMaximum = xRange ;
577 	else
578 	    positionRangeMaximum = yRange ;
579 
580 	if (zRange > positionRangeMaximum)
581 	    positionRangeMaximum = zRange ;
582 
583 	// Adjust the range of the unit cube to match the default
584 	// quantization.
585 	//
586 	// This scale factor along with the center values computed above will
587 	// produce 16-bit integer representations of the floating point
588 	// position coordinates ranging symmetrically about 0 from -32767 to
589 	// +32767.  -32768 is not used and the normalized floating point
590 	// position coordinates of -1.0 as well as +1.0 will not be
591 	// represented.
592 	//
593 	// Applications which wish to seamlessly stitch together compressed
594 	// objects will need to be aware that the range of normalized
595 	// positions will be one quantum away from the [-1..1] endpoints of
596 	// the unit cube and should adjust scale factors accordingly.
597 	scale = (2.0 / positionRangeMaximum) * (32767.0 / 32768.0) ;
598 
599 	// Flag quantization change.
600 	positionQuantChanged = colorQuantChanged = normalQuantChanged = true ;
601 
602 	// Flag first position, color, and normal.
603 	firstPosition = firstColor = firstNormal = true ;
604 
605 	// Apply quantization.
606 	Iterator i = stream.iterator() ;
607 	while (i.hasNext()) {
608 	    Object o = i.next() ;
609 
610 	    if (o instanceof CompressionStreamElement) {
611 		((CompressionStreamElement)o).quantize(this, huffmanTable) ;
612 
613 		// Keep track of whether last two elements were colors or
614 		// normals for mesh buffer component substitution semantics.
615 		lastLastElementColor = lastElementColor ;
616 		lastLastElementNormal = lastElementNormal ;
617 		lastElementColor = lastElementNormal = false ;
618 
619 		if (o instanceof CompressionStreamColor)
620 		    lastElementColor = true ;
621 		else if (o instanceof CompressionStreamNormal)
622 		    lastElementNormal = true ;
623 	    }
624 	}
625 
626 	// Compute the bounds in normalized coordinates.
627 	ncBounds[0].x = (double)qcBounds[0].x / 32768.0 ;
628 	ncBounds[0].y = (double)qcBounds[0].y / 32768.0 ;
629 	ncBounds[0].z = (double)qcBounds[0].z / 32768.0 ;
630 
631 	ncBounds[1].x = (double)qcBounds[1].x / 32768.0 ;
632 	ncBounds[1].y = (double)qcBounds[1].y / 32768.0 ;
633 	ncBounds[1].z = (double)qcBounds[1].z / 32768.0 ;
634     }
635 
636     /**
637      * Iterates across all compression stream elements and builds the
638      * compressed geometry command stream output.<p>
639      *
640      * @param huffmanTable Table which maps geometric elements in this stream
641      * to tags describing the encoding parameters (length, shift, and
642      * absolute/relative status) to be used for their representations in the
643      * compressed output.  All tags must be 6 bits or less in length, and the
644      * sum of the number of bits in the tag plus the number of bits in the
645      * data it describes must be at least 6 bits in length.
646      *
647      * @param outputBuffer CommandStream to use for collecting the compressed
648      * bits.
649      */
outputCommands(HuffmanTable huffmanTable, CommandStream outputBuffer)650     void outputCommands(HuffmanTable huffmanTable, CommandStream outputBuffer) {
651 	//
652 	// The first command output is setState to indicate what data is
653 	// bundled with each vertex.  Although the semantics of geometry
654 	// decompression allow setState to appear anywhere in the stream, this
655 	// cannot be handled by the current Java 3D software decompressor,
656 	// which internally decompresses an entire compressed buffer into a
657 	// single retained object sharing a single consistent vertex format.
658 	// This limitation may be removed in subsequent releases of Java 3D.
659 	//
660 	int bnv = (vertexNormals? 1 : 0) ;
661 	int bcv = ((vertexColor3 || vertexColor4)? 1 : 0) ;
662 	int cap = (vertexColor4? 1 : 0) ;
663 
664 	int command = CommandStream.SET_STATE | bnv ;
665 	long data = (bcv << 2) | (cap << 1) ;
666 
667 	// Output the setState command.
668 	outputBuffer.addCommand(command, 8, data, 3) ;
669 
670 	// Output the Huffman table commands.
671 	huffmanTable.outputCommands(outputBuffer) ;
672 
673 	// Output each compression stream element's data.
674 	Iterator i = stream.iterator() ;
675 	while (i.hasNext()) {
676 	    Object o = i.next() ;
677 	    if (o instanceof CompressionStreamElement)
678 		((CompressionStreamElement)o).outputCommand(huffmanTable,
679 							    outputBuffer) ;
680 	}
681 
682 	// Finish the header-forwarding interleave and long-word align.
683 	outputBuffer.end() ;
684     }
685 
686     /**
687      * Retrieve the total size of the uncompressed geometric data in bytes,
688      * excluding mesh buffer references.
689      * @return uncompressed byte count
690      */
getByteCount()691     int getByteCount() {
692 	return byteCount ;
693     }
694 
695     /**
696      * Retrieve the the number of vertices created for this stream, excluding
697      * mesh buffer references.
698      * @return vertex count
699      */
getVertexCount()700     int getVertexCount() {
701 	return vertexCount ;
702     }
703 
704     /**
705      * Retrieve the number of mesh buffer references created for this stream.
706      * @return mesh buffer reference count
707      */
getMeshReferenceCount()708     int getMeshReferenceCount() {
709 	return meshReferenceCount ;
710     }
711 
712     /**
713      * Stream element that sets position quantization during quantize pass.
714      */
715     private class PositionQuant extends CompressionStreamElement {
716 	int value ;
717 
PositionQuant(int value)718 	PositionQuant(int value) {
719 	    this.value = value ;
720 	}
721 
quantize(CompressionStream s, HuffmanTable t)722 	void quantize(CompressionStream s, HuffmanTable t) {
723 	    positionQuant = value ;
724 	    positionQuantChanged = true ;
725 
726 	    // Adjust range of unit cube scaling to match quantization.
727 	    scale = (2.0 / positionRangeMaximum) *
728 		(((double)((1 << (value-1)) - 1))/((double)(1 << (value-1)))) ;
729 	}
730 
toString()731 	public String toString() {
732 	    return "positionQuant: " + value ;
733 	}
734     }
735 
736     /**
737      * Stream element that sets normal quantization during quantize pass.
738      */
739     private class NormalQuant extends CompressionStreamElement {
740 	int value ;
741 
NormalQuant(int value)742 	NormalQuant(int value) {
743 	    this.value = value ;
744 	}
745 
quantize(CompressionStream s, HuffmanTable t)746 	void quantize(CompressionStream s, HuffmanTable t) {
747 	    normalQuant = value ;
748 	    normalQuantChanged = true ;
749 	}
750 
toString()751 	public String toString() {
752 	    return "normalQuant: " + value ;
753 	}
754     }
755 
756     /**
757      * Stream element that sets color quantization during quantize pass.
758      */
759     private class ColorQuant extends CompressionStreamElement {
760 	int value ;
761 
ColorQuant(int value)762 	ColorQuant(int value) {
763 	    this.value = value ;
764 	}
765 
quantize(CompressionStream s, HuffmanTable t)766 	void quantize(CompressionStream s, HuffmanTable t) {
767 	    colorQuant = value ;
768 	    colorQuantChanged = true ;
769 	}
770 
toString()771 	public String toString() {
772 	    return "colorQuant: " + value ;
773 	}
774     }
775 
776     /**
777      * Stream element that references the mesh buffer.
778      */
779     private class MeshReference extends CompressionStreamElement {
780 	int stripFlag, meshIndex ;
781 
MeshReference(int stripFlag, int meshIndex)782 	MeshReference(int stripFlag, int meshIndex) {
783 	    this.stripFlag = stripFlag ;
784 	    this.meshIndex = meshIndex ;
785 	    meshReferenceCount++ ;
786 	}
787 
quantize(CompressionStream s, HuffmanTable t)788 	void quantize(CompressionStream s, HuffmanTable t) {
789 	    // Retrieve the vertex from the mesh buffer mirror and set up the
790 	    // data needed for the next stream element to compute its deltas.
791 	    CompressionStreamVertex v = meshBuffer.getVertex(meshIndex) ;
792 	    lastPosition[0] = v.xAbsolute ;
793 	    lastPosition[1] = v.yAbsolute ;
794 	    lastPosition[2] = v.zAbsolute ;
795 
796 	    // Set up last color data if it exists and previous elements
797 	    // don't override it.
798 	    if (v.color != null && !lastElementColor &&
799 		!(lastElementNormal && lastLastElementColor)) {
800 		lastColor[0] = v.color.rAbsolute ;
801 		lastColor[1] = v.color.gAbsolute ;
802 		lastColor[2] = v.color.bAbsolute ;
803 		lastColor[3] = v.color.aAbsolute ;
804 	    }
805 
806 	    // Set up last normal data if it exists and previous element
807 	    // doesn't override it.
808 	    if (v.normal != null && !lastElementNormal &&
809 		!(lastElementColor && lastLastElementNormal)) {
810 		lastSextant = v.normal.sextant ;
811 		lastOctant = v.normal.octant ;
812 		lastU = v.normal.uAbsolute ;
813 		lastV = v.normal.vAbsolute ;
814 		lastSpecialNormal = v.normal.specialNormal ;
815 	    }
816 	}
817 
outputCommand(HuffmanTable t, CommandStream outputBuffer)818 	void outputCommand(HuffmanTable t, CommandStream outputBuffer) {
819 	    int command = CommandStream.MESH_B_R ;
820 	    long data = stripFlag & 0x1 ;
821 
822 	    command |= (((meshIndex & 0xf) << 1) | (stripFlag >> 1)) ;
823 	    outputBuffer.addCommand(command, 8, data, 1) ;
824 	}
825 
toString()826 	public String toString() {
827 	    return
828 		"meshReference: stripFlag " + stripFlag +
829 		" meshIndex " + meshIndex ;
830 	}
831     }
832 
833 
834     /**
835      * Copy vertex data and add it to the end of this stream.
836      * @param pos position data
837      * @param stripFlag vertex replacement flag, either RESTART,
838      * REPLACE_OLDEST, or REPLACE_MIDDLE
839      */
addVertex(Point3f pos, int stripFlag)840     void addVertex(Point3f pos, int stripFlag) {
841 	stream.add(new CompressionStreamVertex(this, pos,
842 					       (Vector3f)null, (Color3f)null,
843 					       stripFlag, NO_MESH_PUSH)) ;
844     }
845 
846     /**
847      * Copy vertex data and add it to the end of this stream.
848      * @param pos position data
849      * @param norm normal data
850      * @param stripFlag vertex replacement flag, either RESTART,
851      * REPLACE_OLDEST, or REPLACE_MIDDLE
852      */
addVertex(Point3f pos, Vector3f norm, int stripFlag)853     void addVertex(Point3f pos, Vector3f norm, int stripFlag) {
854 	stream.add(new CompressionStreamVertex
855 	    (this, pos, norm, (Color3f)null, stripFlag, NO_MESH_PUSH)) ;
856     }
857 
858     /**
859      * Copy vertex data and add it to the end of this stream.
860      * @param pos position data
861      * @param color color data
862      * @param stripFlag vertex replacement flag, either RESTART,
863      * REPLACE_OLDEST, or REPLACE_MIDDLE
864      */
addVertex(Point3f pos, Color3f color, int stripFlag)865     void addVertex(Point3f pos, Color3f color, int stripFlag) {
866 	stream.add(new CompressionStreamVertex
867 	    (this, pos, (Vector3f)null, color, stripFlag, NO_MESH_PUSH)) ;
868     }
869 
870     /**
871      * Copy vertex data and add it to the end of this stream.
872      * @param pos position data
873      * @param color color data
874      * @param stripFlag vertex replacement flag, either RESTART,
875      * REPLACE_OLDEST, or REPLACE_MIDDLE
876      */
addVertex(Point3f pos, Color4f color, int stripFlag)877     void addVertex(Point3f pos, Color4f color, int stripFlag) {
878 	stream.add(new CompressionStreamVertex
879 	    (this, pos, (Vector3f)null, color, stripFlag, NO_MESH_PUSH)) ;
880     }
881 
882     /**
883      * Copy vertex data and add it to the end of this stream.
884      * @param pos position data
885      * @param norm normal data
886      * @param color color data
887      * @param stripFlag vertex replacement flag, either RESTART,
888      * REPLACE_OLDEST, or REPLACE_MIDDLE
889      */
addVertex(Point3f pos, Vector3f norm, Color3f color, int stripFlag)890     void addVertex(Point3f pos, Vector3f norm, Color3f color,
891 			  int stripFlag) {
892 	stream.add(new CompressionStreamVertex
893 	    (this, pos, norm, color, stripFlag, NO_MESH_PUSH)) ;
894     }
895 
896     /**
897      * Copy vertex data and add it to the end of this stream.
898      * @param pos position data
899      * @param norm normal data
900      * @param color color data
901      * @param stripFlag vertex replacement flag, either RESTART,
902      * REPLACE_OLDEST, or REPLACE_MIDDLE
903      */
addVertex(Point3f pos, Vector3f norm, Color4f color, int stripFlag)904     void addVertex(Point3f pos, Vector3f norm, Color4f color,
905 			  int stripFlag) {
906 	stream.add(new CompressionStreamVertex
907 	    (this, pos, norm, color, stripFlag, NO_MESH_PUSH)) ;
908     }
909 
910     /**
911      * Copy vertex data and add it to the end of this stream.
912      * @param pos position data
913      * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
914      * or REPLACE_MIDDLE
915      * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
916      */
addVertex(Point3f pos, int stripFlag, int meshFlag)917     void addVertex(Point3f pos, int stripFlag, int meshFlag) {
918 	stream.add(new CompressionStreamVertex
919 	    (this, pos, (Vector3f)null, (Color3f)null, stripFlag, meshFlag)) ;
920     }
921 
922     /**
923      * Copy vertex data and add it to the end of this stream.
924      * @param pos position data
925      * @param norm normal data
926      * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
927      * or REPLACE_MIDDLE
928      * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
929      */
addVertex(Point3f pos, Vector3f norm, int stripFlag, int meshFlag)930     void addVertex(Point3f pos, Vector3f norm,
931 			  int stripFlag, int meshFlag) {
932 	stream.add(new CompressionStreamVertex
933 	    (this, pos, norm, (Color3f)null, stripFlag, meshFlag)) ;
934     }
935 
936     /**
937      * Copy vertex data and add it to the end of this stream.
938      * @param pos position data
939      * @param color color data
940      * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
941      * or REPLACE_MIDDLE
942      * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
943      */
addVertex(Point3f pos, Color3f color, int stripFlag, int meshFlag)944     void addVertex(Point3f pos, Color3f color,
945 			  int stripFlag, int meshFlag) {
946 	stream.add(new CompressionStreamVertex
947 	    (this, pos, (Vector3f)null, color, stripFlag, meshFlag)) ;
948     }
949 
950     /**
951      * Copy vertex data and add it to the end of this stream.
952      * @param pos position data
953      * @param color color data
954      * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
955      * or REPLACE_MIDDLE
956      * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
957      */
addVertex(Point3f pos, Color4f color, int stripFlag, int meshFlag)958     void addVertex(Point3f pos, Color4f color,
959 			  int stripFlag, int meshFlag) {
960 	stream.add(new CompressionStreamVertex
961 	    (this, pos, (Vector3f)null, color, stripFlag, meshFlag)) ;
962     }
963 
964     /**
965      * Copy vertex data and add it to the end of this stream.
966      * @param pos position data
967      * @param norm normal data
968      * @param color color data
969      * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
970      * or REPLACE_MIDDLE
971      * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
972      */
addVertex(Point3f pos, Vector3f norm, Color3f color, int stripFlag, int meshFlag)973     void addVertex(Point3f pos, Vector3f norm, Color3f color,
974 			  int stripFlag, int meshFlag) {
975 	stream.add(new CompressionStreamVertex
976 	    (this, pos, norm, color, stripFlag, meshFlag)) ;
977     }
978 
979     /**
980      * Copy vertex data and add it to the end of this stream.
981      * @param pos position data
982      * @param norm normal data
983      * @param color color data
984      * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
985      * or REPLACE_MIDDLE
986      * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
987      */
addVertex(Point3f pos, Vector3f norm, Color4f color, int stripFlag, int meshFlag)988     void addVertex(Point3f pos, Vector3f norm, Color4f color,
989 			  int stripFlag, int meshFlag) {
990 	stream.add(new CompressionStreamVertex
991 	    (this, pos, norm, color, stripFlag, meshFlag)) ;
992     }
993 
994     /**
995      * Copy vertex data and add it to the end of this stream.
996      * @param pos position data
997      * @param norm normal data
998      * @param color color data, either Color3f or Color4f, determined by
999      * current vertex format
1000      * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
1001      * or REPLACE_MIDDLE
1002      * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
1003      */
addVertex(Point3f pos, Vector3f norm, Object color, int stripFlag, int meshFlag)1004     void addVertex(Point3f pos, Vector3f norm,
1005 		   Object color, int stripFlag, int meshFlag) {
1006 
1007 	if (vertexColor3)
1008 	    stream.add(new CompressionStreamVertex
1009 		       (this, pos, norm, (Color3f)color, stripFlag, meshFlag)) ;
1010 	else
1011 	    stream.add(new CompressionStreamVertex
1012 		       (this, pos, norm, (Color4f)color, stripFlag, meshFlag)) ;
1013     }
1014 
1015     /**
1016      * Add a mesh buffer reference to this stream.
1017      * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
1018      * or REPLACE_MIDDLE
1019      * @param meshIndex index of vertex to retrieve from the mesh buffer
1020      */
addMeshReference(int stripFlag, int meshIndex)1021     void addMeshReference(int stripFlag, int meshIndex) {
1022 	stream.add(new MeshReference(stripFlag, meshIndex)) ;
1023     }
1024 
1025     /**
1026      * Copy the given color to the end of this stream and use it as a global
1027      * state change that applies to all subsequent vertices.
1028      */
addColor(Color3f c3f)1029     void addColor(Color3f c3f) {
1030 	stream.add(new CompressionStreamColor(this, c3f)) ;
1031     }
1032 
1033     /**
1034      * Copy the given color to the end of this stream and use it as a global
1035      * state change that applies to all subsequent vertices.
1036      */
addColor(Color4f c4f)1037     void addColor(Color4f c4f) {
1038 	stream.add(new CompressionStreamColor(this, c4f)) ;
1039     }
1040 
1041     /**
1042      * Copy the given normal to the end of this stream and use it as a global
1043      * state change that applies to all subsequent vertices.
1044      */
addNormal(Vector3f n)1045     void addNormal(Vector3f n) {
1046 	stream.add(new CompressionStreamNormal(this, n)) ;
1047     }
1048 
1049     /**
1050      * Add a new position quantization value to the end of this stream that
1051      * will apply to all subsequent vertex positions.
1052      *
1053      * @param value number of bits to quantize each position's X, Y,
1054      * and Z components, ranging from 1 to 16 with a default of 16
1055      */
addPositionQuantization(int value)1056     void addPositionQuantization(int value) {
1057 	stream.add(new PositionQuant(value)) ;
1058     }
1059 
1060     /**
1061      * Add a new color quantization value to the end of this stream that will
1062      * apply to all subsequent colors.
1063      *
1064      * @param value number of bits to quantize each color's R, G, B, and
1065      * alpha components, ranging from 2 to 16 with a default of 9
1066      */
addColorQuantization(int value)1067     void addColorQuantization(int value) {
1068 	stream.add(new ColorQuant(value)) ;
1069     }
1070 
1071     /**
1072      * Add a new normal quantization value to the end of this stream that will
1073      * apply to all subsequent normals.  This value specifies the number of
1074      * bits for each normal's U and V components.
1075      *
1076      * @param value number of bits for quantizing U and V, ranging from 0 to
1077      * 6 with a default of 6
1078      */
addNormalQuantization(int value)1079     void addNormalQuantization(int value) {
1080 	stream.add(new NormalQuant(value)) ;
1081     }
1082 
1083     /**
1084      * Interface to access GeometryArray vertex components and add them to the
1085      * compression stream.
1086      *
1087      * A processVertex() implementation retrieves vertex components using the
1088      * appropriate access semantics of a particular GeometryArray, and adds
1089      * them to the compression stream.
1090      *
1091      * The implementation always pushes vertices into the mesh buffer unless
1092      * they match ones already there; if they do, it generates mesh buffer
1093      * references instead.  This reduces the number of vertices when
1094      * non-stripped abutting facets are added to the stream.
1095      *
1096      * Note: Level II geometry compression semantics allow the mesh buffer
1097      * normals to be substituted with the value of an immediately
1098      * preceding SetNormal command, but this is unavailable in Level I.
1099      *
1100      * @param index vertex offset from the beginning of its data array
1101      * @param stripFlag RESTART, REPLACE_MIDDLE, or REPLACE_OLDEST
1102      */
1103     private interface GeometryAccessor {
processVertex(int index, int stripFlag)1104 	void processVertex(int index, int stripFlag) ;
1105     }
1106 
1107     /**
1108      * This class implements the GeometryAccessor interface for geometry
1109      * arrays accessed with by-copy semantics.
1110      */
1111     private class ByCopyGeometry implements GeometryAccessor {
1112 	Point3f[] positions = null ;
1113 	Vector3f[] normals = null ;
1114 	Color3f[] colors3 = null ;
1115 	Color4f[] colors4 = null ;
1116 
ByCopyGeometry(GeometryArray ga)1117 	ByCopyGeometry(GeometryArray ga) {
1118 	    this(ga, ga.getInitialVertexIndex(), ga.getValidVertexCount()) ;
1119 	}
1120 
ByCopyGeometry(GeometryArray ga, int firstVertex, int validVertexCount)1121 	ByCopyGeometry(GeometryArray ga,
1122 		       int firstVertex, int validVertexCount) {
1123 	    int i ;
1124 	    positions = new Point3f[validVertexCount] ;
1125 	    for (i = 0 ; i < validVertexCount ; i++)
1126 		positions[i] = new Point3f() ;
1127 
1128 	    ga.getCoordinates(firstVertex, positions) ;
1129 
1130 	    if (vertexNormals) {
1131 		normals = new Vector3f[validVertexCount] ;
1132 		for (i = 0 ; i < validVertexCount ; i++)
1133 		    normals[i] = new Vector3f() ;
1134 
1135 		ga.getNormals(firstVertex, normals) ;
1136 	    }
1137 
1138 	    if (vertexColor3) {
1139 		colors3 = new Color3f[validVertexCount] ;
1140 		for (i = 0 ; i < validVertexCount ; i++)
1141 		    colors3[i] = new Color3f() ;
1142 
1143 		ga.getColors(firstVertex, colors3) ;
1144 	    }
1145 	    else if (vertexColor4) {
1146 		colors4 = new Color4f[validVertexCount] ;
1147 		for (i = 0 ; i < validVertexCount ; i++)
1148 		    colors4[i] = new Color4f() ;
1149 
1150 		ga.getColors(firstVertex, colors4) ;
1151 	    }
1152 	}
1153 
processVertex(int v, int stripFlag)1154 	public void processVertex(int v, int stripFlag) {
1155 	    Point3f p = positions[v] ;
1156 	    int r = meshBuffer.getMeshReference(p) ;
1157 
1158 	    if ((r == meshBuffer.NOT_FOUND) ||
1159 		(vertexNormals && noMeshNormalSubstitution &&
1160 		 (! normals[v].equals(meshBuffer.getNormal(r))))) {
1161 
1162 		Vector3f n = vertexNormals? normals[v] : null ;
1163 		Object c = vertexColor3? (Object)colors3[v] :
1164 		    vertexColor4? (Object)colors4[v] : null ;
1165 
1166 		addVertex(p, n, c, stripFlag, MESH_PUSH) ;
1167 		meshBuffer.push(p, c, n) ;
1168 	    }
1169 	    else {
1170 		if (vertexNormals && !noMeshNormalSubstitution &&
1171 		    (! normals[v].equals(meshBuffer.getNormal(r))))
1172 		    addNormal(normals[v]) ;
1173 
1174 		if (vertexColor3 &&
1175 		    (! colors3[v].equals(meshBuffer.getColor3(r))))
1176 		    addColor(colors3[v]) ;
1177 
1178 		else if (vertexColor4 &&
1179 			 (! colors4[v].equals(meshBuffer.getColor4(r))))
1180 		    addColor(colors4[v]) ;
1181 
1182 		addMeshReference(stripFlag, r) ;
1183 	    }
1184 	}
1185     }
1186 
1187     /**
1188      * Class which holds index array references for a geometry array.
1189      */
1190     private static class IndexArrays {
1191 	int colorIndices[] = null ;
1192 	int normalIndices[] = null ;
1193 	int positionIndices[] = null ;
1194     }
1195 
1196     /**
1197      * Retrieves index array references for the specified IndexedGeometryArray.
1198      * Index arrays are copied starting from initialIndexIndex.
1199      */
getIndexArrays(GeometryArray ga, IndexArrays ia)1200     private void getIndexArrays(GeometryArray ga, IndexArrays ia) {
1201 	IndexedGeometryArray iga = (IndexedGeometryArray)ga ;
1202 
1203 	int initialIndexIndex = iga.getInitialIndexIndex() ;
1204 	int indexCount = iga.getValidIndexCount() ;
1205 	int vertexFormat = iga.getVertexFormat() ;
1206 
1207 	boolean useCoordIndexOnly = false ;
1208 	if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) {
1209 	    if (debug) System.out.println("useCoordIndexOnly") ;
1210 	    useCoordIndexOnly = true ;
1211 	}
1212 
1213 	ia.positionIndices = new int[indexCount] ;
1214 	iga.getCoordinateIndices(initialIndexIndex, ia.positionIndices) ;
1215 
1216 	if (vertexNormals) {
1217 	    if (useCoordIndexOnly) {
1218 		ia.normalIndices = ia.positionIndices ;
1219 	    }
1220 	    else {
1221 		ia.normalIndices = new int[indexCount] ;
1222 		iga.getNormalIndices(initialIndexIndex, ia.normalIndices) ;
1223 	    }
1224 	}
1225 	if (vertexColor3 || vertexColor4) {
1226 	    if (useCoordIndexOnly) {
1227 		ia.colorIndices = ia.positionIndices ;
1228 	    }
1229 	    else {
1230 		ia.colorIndices = new int[indexCount] ;
1231 		iga.getColorIndices(initialIndexIndex, ia.colorIndices) ;
1232 	    }
1233 	}
1234     }
1235 
1236     /**
1237      * Class which holds indices for a specific vertex of an
1238      * IndexedGeometryArray.
1239      */
1240     private static class VertexIndices {
1241 	int pi, ni, ci ;
1242     }
1243 
1244     /**
1245      * Retrieves vertex indices for a specific vertex in an
1246      * IndexedGeometryArray.
1247      */
getVertexIndices(int v, IndexArrays ia, VertexIndices vi)1248     private void getVertexIndices(int v, IndexArrays ia, VertexIndices vi) {
1249 	vi.pi = ia.positionIndices[v] ;
1250 	if (vertexNormals)
1251 	    vi.ni = ia.normalIndices[v] ;
1252 	if (vertexColors)
1253 	    vi.ci = ia.colorIndices[v] ;
1254     }
1255 
1256     /**
1257      * This class implements the GeometryAccessor interface for indexed
1258      * geometry arrays accessed with by-copy semantics.
1259      */
1260     private class IndexedByCopyGeometry extends ByCopyGeometry {
1261 	IndexArrays ia = new IndexArrays() ;
1262 	VertexIndices vi = new VertexIndices() ;
1263 
IndexedByCopyGeometry(GeometryArray ga)1264 	IndexedByCopyGeometry(GeometryArray ga) {
1265 	    super(ga, 0, ga.getVertexCount()) ;
1266 	    getIndexArrays(ga, ia) ;
1267 	}
1268 
processVertex(int v, int stripFlag)1269 	public void processVertex(int v, int stripFlag) {
1270 	    getVertexIndices(v, ia, vi) ;
1271 	    int r = meshBuffer.getMeshReference(vi.pi) ;
1272 
1273 	    if ((r == meshBuffer.NOT_FOUND) ||
1274 		(vertexNormals && noMeshNormalSubstitution &&
1275 		 (vi.ni != meshBuffer.getNormalIndex(r)))) {
1276 
1277 		Point3f p = positions[vi.pi] ;
1278 		Vector3f n = vertexNormals? normals[vi.ni] : null ;
1279 		Object c = vertexColor3? (Object)colors3[vi.ci] :
1280 		    vertexColor4? (Object)colors4[vi.ci] : null ;
1281 
1282 		addVertex(p, n, c, stripFlag, MESH_PUSH) ;
1283 		meshBuffer.push(vi.pi, vi.ci, vi.ni) ;
1284 	    }
1285 	    else {
1286 		if (vertexNormals && !noMeshNormalSubstitution &&
1287 		    vi.ni != meshBuffer.getNormalIndex(r))
1288 		    addNormal(normals[vi.ni]) ;
1289 
1290 		if (vertexColor3 && vi.ci != meshBuffer.getColorIndex(r))
1291 		    addColor(colors3[vi.ci]) ;
1292 
1293 		else if (vertexColor4 && vi.ci != meshBuffer.getColorIndex(r))
1294 		    addColor(colors4[vi.ci]) ;
1295 
1296 		addMeshReference(stripFlag, r) ;
1297 	    }
1298 	}
1299     }
1300 
1301     //
1302     // NOTE: For now, copies are made of all GeometryArray vertex components
1303     // even when by-reference access is available.
1304     //
1305     private static class VertexCopy {
1306 	Object c = null ;
1307 	Point3f p = null ;
1308 	Vector3f n = null ;
1309 	Color3f c3 = null ;
1310 	Color4f c4 = null ;
1311     }
1312 
processVertexCopy(VertexCopy vc, int stripFlag)1313     private void processVertexCopy(VertexCopy vc, int stripFlag) {
1314 	int r = meshBuffer.getMeshReference(vc.p) ;
1315 
1316 	if ((r == meshBuffer.NOT_FOUND) ||
1317 	    (vertexNormals && noMeshNormalSubstitution &&
1318 	     (! vc.n.equals(meshBuffer.getNormal(r))))) {
1319 
1320 	    addVertex(vc.p, vc.n, vc.c, stripFlag, MESH_PUSH) ;
1321 	    meshBuffer.push(vc.p, vc.c, vc.n) ;
1322 	}
1323 	else {
1324 	    if (vertexNormals && !noMeshNormalSubstitution &&
1325 		(! vc.n.equals(meshBuffer.getNormal(r))))
1326 		addNormal(vc.n) ;
1327 
1328 	    if (vertexColor3 && (! vc.c3.equals(meshBuffer.getColor3(r))))
1329 		addColor(vc.c3) ;
1330 
1331 	    else if (vertexColor4 && (! vc.c4.equals(meshBuffer.getColor4(r))))
1332 		addColor(vc.c4) ;
1333 
1334 	    addMeshReference(stripFlag, r) ;
1335 	}
1336     }
1337 
processIndexedVertexCopy(VertexCopy vc, VertexIndices vi, int stripFlag)1338     private void processIndexedVertexCopy(VertexCopy vc,
1339 					  VertexIndices vi,
1340 					  int stripFlag) {
1341 
1342 	int r = meshBuffer.getMeshReference(vi.pi) ;
1343 
1344 	if ((r == meshBuffer.NOT_FOUND) ||
1345 	    (vertexNormals && noMeshNormalSubstitution &&
1346 	     (vi.ni != meshBuffer.getNormalIndex(r)))) {
1347 
1348 	    addVertex(vc.p, vc.n, vc.c, stripFlag, MESH_PUSH) ;
1349 	    meshBuffer.push(vi.pi, vi.ci, vi.ni) ;
1350 	}
1351 	else {
1352 	    if (vertexNormals && !noMeshNormalSubstitution &&
1353 		vi.ni != meshBuffer.getNormalIndex(r))
1354 		addNormal(vc.n) ;
1355 
1356 	    if (vertexColor3 && vi.ci != meshBuffer.getColorIndex(r))
1357 		addColor(vc.c3) ;
1358 
1359 	    else if (vertexColor4 && vi.ci != meshBuffer.getColorIndex(r))
1360 		addColor(vc.c4) ;
1361 
1362 	    addMeshReference(stripFlag, r) ;
1363 	}
1364     }
1365 
1366     /**
1367      * This abstract class implements the GeometryAccessor interface for
1368      * concrete subclasses which handle float and NIO interleaved geometry
1369      * arrays.
1370      */
1371     private abstract class InterleavedGeometry implements GeometryAccessor {
1372 	VertexCopy vc = new VertexCopy() ;
1373 
1374 	int vstride = 0 ;
1375 	int coffset = 0 ;
1376 	int noffset = 0 ;
1377 	int poffset = 0 ;
1378 	int tstride = 0 ;
1379 	int tcount = 0 ;
1380 
InterleavedGeometry(GeometryArray ga)1381 	InterleavedGeometry(GeometryArray ga) {
1382 	    if (vertexTextures) {
1383 		if (vertexTexture2) tstride = 2 ;
1384 		else if (vertexTexture3) tstride = 3 ;
1385 		else if (vertexTexture4) tstride = 4 ;
1386 
1387 		tcount = ga.getTexCoordSetCount() ;
1388 		vstride += tcount * tstride ;
1389 	    }
1390 
1391 	    if (vertexColors) {
1392 		coffset = vstride ;
1393 		if (vertexColor3) vstride += 3 ;
1394 		else vstride += 4 ;
1395 	    }
1396 
1397 	    if (vertexNormals) {
1398 		noffset = vstride ;
1399 		vstride += 3 ;
1400 	    }
1401 
1402 	    poffset = vstride ;
1403 	    vstride += 3 ;
1404 	}
1405 
copyVertex(int pi, int ni, int ci, VertexCopy vc)1406 	abstract void copyVertex(int pi, int ni, int ci, VertexCopy vc) ;
1407 
processVertex(int v, int stripFlag)1408 	public void processVertex(int v, int stripFlag) {
1409 	    copyVertex(v, v, v, vc) ;
1410 	    processVertexCopy(vc, stripFlag) ;
1411 	}
1412     }
1413 
1414     /**
1415      * This class implements the GeometryAccessor interface for float
1416      * interleaved geometry arrays.
1417      */
1418     private class InterleavedGeometryFloat extends InterleavedGeometry {
1419 	float[] vdata = null ;
1420 
InterleavedGeometryFloat(GeometryArray ga)1421 	InterleavedGeometryFloat(GeometryArray ga) {
1422 	    super(ga) ;
1423 	    vdata = ga.getInterleavedVertices() ;
1424 	}
1425 
copyVertex(int pi, int ni, int ci, VertexCopy vc)1426 	void copyVertex(int pi, int ni, int ci, VertexCopy vc) {
1427 	    int voffset ;
1428 	    voffset = pi * vstride ;
1429 	    vc.p = new Point3f(vdata[voffset + poffset + 0],
1430 			       vdata[voffset + poffset + 1],
1431 			       vdata[voffset + poffset + 2]) ;
1432 
1433 	    if (vertexNormals) {
1434 		voffset = ni * vstride ;
1435 		vc.n = new Vector3f(vdata[voffset + noffset + 0],
1436 				    vdata[voffset + noffset + 1],
1437 				    vdata[voffset + noffset + 2]) ;
1438 	    }
1439 	    if (vertexColor3) {
1440 		voffset = ci * vstride ;
1441 		vc.c3 = new Color3f(vdata[voffset + coffset + 0],
1442 				    vdata[voffset + coffset + 1],
1443 				    vdata[voffset + coffset + 2]) ;
1444 		vc.c = vc.c3 ;
1445 	    }
1446 	    else if (vertexColor4) {
1447 		voffset = ci * vstride ;
1448 		vc.c4 = new Color4f(vdata[voffset + coffset + 0],
1449 				    vdata[voffset + coffset + 1],
1450 				    vdata[voffset + coffset + 2],
1451 				    vdata[voffset + coffset + 3]) ;
1452 		vc.c = vc.c4 ;
1453 	    }
1454 	}
1455     }
1456 
1457     /**
1458      * This class implements the GeometryAccessor interface for indexed
1459      * interleaved geometry arrays.
1460      */
1461     private class IndexedInterleavedGeometryFloat
1462 	extends InterleavedGeometryFloat {
1463 
1464 	IndexArrays ia = new IndexArrays() ;
1465 	VertexIndices vi = new VertexIndices() ;
1466 
IndexedInterleavedGeometryFloat(GeometryArray ga)1467 	IndexedInterleavedGeometryFloat(GeometryArray ga) {
1468 	    super(ga) ;
1469 	    getIndexArrays(ga, ia) ;
1470 	}
1471 
processVertex(int v, int stripFlag)1472 	public void processVertex(int v, int stripFlag) {
1473 	    getVertexIndices(v, ia, vi) ;
1474 	    copyVertex(vi.pi, vi.ni, vi.ci, vc) ;
1475 	    processIndexedVertexCopy(vc, vi, stripFlag) ;
1476 	}
1477     }
1478 
1479     /**
1480      * This class implements the GeometryAccessor interface for
1481      * interleaved NIO geometry arrays.
1482      */
1483     private class InterleavedGeometryNIO extends InterleavedGeometry {
1484 	FloatBufferWrapper fbw = null ;
1485 
InterleavedGeometryNIO(GeometryArray ga)1486 	InterleavedGeometryNIO(GeometryArray ga) {
1487 	    super(ga) ;
1488 	    J3DBuffer buffer = ga.getInterleavedVertexBuffer() ;
1489 	    if (BufferWrapper.getBufferType(buffer) ==
1490 		BufferWrapper.TYPE_FLOAT) {
1491 		fbw = new FloatBufferWrapper(buffer) ;
1492 	    }
1493 	    else {
1494 		throw new IllegalArgumentException
1495 		    ("\ninterleaved vertex buffer must be FloatBuffer") ;
1496 	    }
1497 	}
1498 
copyVertex(int pi, int ni, int ci, VertexCopy vc)1499 	void copyVertex(int pi, int ni, int ci, VertexCopy vc) {
1500 	    int voffset ;
1501 	    voffset = pi * vstride ;
1502 	    vc.p = new Point3f(fbw.get(voffset + poffset + 0),
1503 			       fbw.get(voffset + poffset + 1),
1504 			       fbw.get(voffset + poffset + 2)) ;
1505 
1506 	    if (vertexNormals) {
1507 		voffset = ni * vstride ;
1508 		vc.n = new Vector3f(fbw.get(voffset + noffset + 0),
1509 				    fbw.get(voffset + noffset + 1),
1510 				    fbw.get(voffset + noffset + 2)) ;
1511 	    }
1512 	    if (vertexColor3) {
1513 		voffset = ci * vstride ;
1514 		vc.c3 = new Color3f(fbw.get(voffset + coffset + 0),
1515 				    fbw.get(voffset + coffset + 1),
1516 				    fbw.get(voffset + coffset + 2)) ;
1517 		vc.c = vc.c3 ;
1518 	    }
1519 	    else if (vertexColor4) {
1520 		voffset = ci * vstride ;
1521 		vc.c4 = new Color4f(fbw.get(voffset + coffset + 0),
1522 				    fbw.get(voffset + coffset + 1),
1523 				    fbw.get(voffset + coffset + 2),
1524 				    fbw.get(voffset + coffset + 3)) ;
1525 		vc.c = vc.c4 ;
1526 	    }
1527 	}
1528     }
1529 
1530     /**
1531      * This class implements the GeometryAccessor interface for indexed
1532      * interleaved NIO geometry arrays.
1533      */
1534     private class IndexedInterleavedGeometryNIO extends InterleavedGeometryNIO {
1535 	IndexArrays ia = new IndexArrays() ;
1536 	VertexIndices vi = new VertexIndices() ;
1537 
IndexedInterleavedGeometryNIO(GeometryArray ga)1538 	IndexedInterleavedGeometryNIO(GeometryArray ga) {
1539 	    super(ga) ;
1540 	    getIndexArrays(ga, ia) ;
1541 	}
1542 
processVertex(int v, int stripFlag)1543 	public void processVertex(int v, int stripFlag) {
1544 	    getVertexIndices(v, ia, vi) ;
1545 	    copyVertex(vi.pi, vi.ni, vi.ci, vc) ;
1546 	    processIndexedVertexCopy(vc, vi, stripFlag) ;
1547 	}
1548     }
1549 
1550     /**
1551      * This class implements the GeometryAccessor interface for
1552      * non-interleaved geometry arrays accessed with by-reference semantics.
1553      */
1554     private class ByRefGeometry implements GeometryAccessor {
1555 	VertexCopy vc = new VertexCopy() ;
1556 
1557 	byte[]   colorsB    = null ;
1558 	float[]  colorsF    = null ;
1559 	float[]  normals    = null ;
1560 	float[]  positionsF = null ;
1561 	double[] positionsD = null ;
1562 
1563 	int initialPositionIndex = 0 ;
1564 	int initialNormalIndex   = 0 ;
1565 	int initialColorIndex    = 0 ;
1566 
ByRefGeometry(GeometryArray ga)1567 	ByRefGeometry(GeometryArray ga) {
1568 	    positionsF = ga.getCoordRefFloat() ;
1569 	    if (debug && positionsF != null)
1570 		System.out.println("float positions") ;
1571 
1572 	    positionsD = ga.getCoordRefDouble() ;
1573 	    if (debug && positionsD != null)
1574 		System.out.println("double positions") ;
1575 
1576 	    if (positionsF == null && positionsD == null)
1577 		throw new UnsupportedOperationException
1578 		    ("\nby-reference access to Point3{d,f} arrays") ;
1579 
1580 	    initialPositionIndex = ga.getInitialCoordIndex() ;
1581 
1582 	    if (vertexColors) {
1583 		colorsB = ga.getColorRefByte() ;
1584 		if (debug && colorsB != null)
1585 		    System.out.println("byte colors") ;
1586 
1587 		colorsF = ga.getColorRefFloat() ;
1588 		if (debug && colorsF != null)
1589 		    System.out.println("float colors") ;
1590 
1591 		if (colorsB == null && colorsF == null)
1592 		    throw new UnsupportedOperationException
1593 			("\nby-reference access to Color{3b,3f,4b,4f} arrays") ;
1594 
1595 		initialColorIndex = ga.getInitialColorIndex() ;
1596 	    }
1597 
1598 	    if (vertexNormals) {
1599 		normals = ga.getNormalRefFloat() ;
1600 		if (debug && normals != null)
1601 		    System.out.println("float normals") ;
1602 
1603 		if (normals == null)
1604 		    throw new UnsupportedOperationException
1605 			("\nby-reference access to Normal3f array") ;
1606 
1607 		initialNormalIndex = ga.getInitialNormalIndex() ;
1608 	    }
1609 	}
1610 
copyVertex(int pi, int ni, int ci, VertexCopy vc)1611 	void copyVertex(int pi, int ni, int ci, VertexCopy vc) {
1612 	    pi *= 3 ;
1613 	    if (positionsF != null) {
1614 		vc.p = new Point3f(positionsF[pi + 0],
1615 				   positionsF[pi + 1],
1616 				   positionsF[pi + 2]) ;
1617 	    }
1618 	    else {
1619 		vc.p = new Point3f((float)positionsD[pi + 0],
1620 				   (float)positionsD[pi + 1],
1621 				   (float)positionsD[pi + 2]) ;
1622 	    }
1623 
1624 	    ni *= 3 ;
1625 	    if (vertexNormals) {
1626 		vc.n = new Vector3f(normals[ni + 0],
1627 				    normals[ni + 1],
1628 				    normals[ni + 2]) ;
1629 	    }
1630 
1631 	    if (vertexColor3) {
1632 		ci *= 3 ;
1633 		if (colorsB != null) {
1634 		    vc.c3 = new Color3f
1635 			((colorsB[ci + 0] & 0xff) * ByteToFloatScale,
1636 			 (colorsB[ci + 1] & 0xff) * ByteToFloatScale,
1637 			 (colorsB[ci + 2] & 0xff) * ByteToFloatScale) ;
1638 		}
1639 		else {
1640 		    vc.c3 = new Color3f(colorsF[ci + 0],
1641 					colorsF[ci + 1],
1642 					colorsF[ci + 2]) ;
1643 		}
1644 		vc.c = vc.c3 ;
1645 	    }
1646 	    else if (vertexColor4) {
1647 		ci *= 4 ;
1648 		if (colorsB != null) {
1649 		    vc.c4 = new Color4f
1650 			((colorsB[ci + 0] & 0xff) * ByteToFloatScale,
1651 			 (colorsB[ci + 1] & 0xff) * ByteToFloatScale,
1652 			 (colorsB[ci + 2] & 0xff) * ByteToFloatScale,
1653 			 (colorsB[ci + 3] & 0xff) * ByteToFloatScale) ;
1654 		}
1655 		else {
1656 		    vc.c4 = new Color4f(colorsF[ci + 0],
1657 					colorsF[ci + 1],
1658 					colorsF[ci + 2],
1659 					colorsF[ci + 3]) ;
1660 		}
1661 		vc.c = vc.c4 ;
1662 	    }
1663 	}
1664 
processVertex(int v, int stripFlag)1665 	public void processVertex(int v, int stripFlag) {
1666 	    copyVertex(v + initialPositionIndex,
1667 		       v + initialNormalIndex,
1668 		       v + initialColorIndex, vc) ;
1669 
1670 	    processVertexCopy(vc, stripFlag) ;
1671 	}
1672     }
1673 
1674     /**
1675      * This class implements the GeometryAccessor interface for indexed
1676      * non-interleaved geometry arrays accessed with by-reference semantics.
1677      */
1678     private class IndexedByRefGeometry extends ByRefGeometry {
1679 	IndexArrays ia = new IndexArrays() ;
1680 	VertexIndices vi = new VertexIndices() ;
1681 
IndexedByRefGeometry(GeometryArray ga)1682 	IndexedByRefGeometry(GeometryArray ga) {
1683 	    super(ga) ;
1684 	    getIndexArrays(ga, ia) ;
1685 	}
1686 
processVertex(int v, int stripFlag)1687 	public void processVertex(int v, int stripFlag) {
1688 	    getVertexIndices(v, ia, vi) ;
1689 	    copyVertex(vi.pi, vi.ni, vi.ci, vc) ;
1690 	    processIndexedVertexCopy(vc, vi, stripFlag) ;
1691 	}
1692     }
1693 
1694     /**
1695      * This class implements the GeometryAccessor interface for
1696      * non-interleaved geometry arrays accessed with NIO.
1697      */
1698     private class ByRefGeometryNIO implements GeometryAccessor {
1699 	VertexCopy vc = new VertexCopy() ;
1700 
1701 	ByteBufferWrapper   colorsB    = null ;
1702 	FloatBufferWrapper  colorsF    = null ;
1703 	FloatBufferWrapper  normals    = null ;
1704 	FloatBufferWrapper  positionsF = null ;
1705 	DoubleBufferWrapper positionsD = null ;
1706 
1707 	int initialPositionIndex = 0 ;
1708 	int initialNormalIndex   = 0 ;
1709 	int initialColorIndex    = 0 ;
1710 
ByRefGeometryNIO(GeometryArray ga)1711 	ByRefGeometryNIO(GeometryArray ga) {
1712 	    J3DBuffer buffer ;
1713 	    buffer = ga.getCoordRefBuffer() ;
1714 	    initialPositionIndex = ga.getInitialCoordIndex() ;
1715 
1716 	    switch (BufferWrapper.getBufferType(buffer)) {
1717 	    case BufferWrapper.TYPE_FLOAT:
1718 		positionsF = new FloatBufferWrapper(buffer) ;
1719 		if (debug) System.out.println("float positions buffer") ;
1720 		break ;
1721 	    case BufferWrapper.TYPE_DOUBLE:
1722 		positionsD = new DoubleBufferWrapper(buffer) ;
1723 		if (debug) System.out.println("double positions buffer") ;
1724 		break ;
1725 	    default:
1726 		throw new IllegalArgumentException
1727 		    ("\nposition buffer must be FloatBuffer or DoubleBuffer") ;
1728 	    }
1729 
1730 	    if (vertexColors) {
1731 		buffer = ga.getColorRefBuffer() ;
1732 		initialColorIndex = ga.getInitialColorIndex() ;
1733 
1734 		switch (BufferWrapper.getBufferType(buffer)) {
1735 		case BufferWrapper.TYPE_BYTE:
1736 		    colorsB = new ByteBufferWrapper(buffer) ;
1737 		    if (debug) System.out.println("byte colors buffer") ;
1738 		    break ;
1739 		case BufferWrapper.TYPE_FLOAT:
1740 		    colorsF = new FloatBufferWrapper(buffer) ;
1741 		    if (debug) System.out.println("float colors buffer") ;
1742 		    break ;
1743 		default:
1744 		    throw new IllegalArgumentException
1745 			("\ncolor buffer must be ByteBuffer or FloatBuffer") ;
1746 		}
1747 	    }
1748 
1749 	    if (vertexNormals) {
1750 		buffer = ga.getNormalRefBuffer() ;
1751 		initialNormalIndex = ga.getInitialNormalIndex() ;
1752 
1753 		switch (BufferWrapper.getBufferType(buffer)) {
1754 		case BufferWrapper.TYPE_FLOAT:
1755 		    normals = new FloatBufferWrapper(buffer) ;
1756 		    if (debug) System.out.println("float normals buffer") ;
1757 		    break ;
1758 		default:
1759 		    throw new IllegalArgumentException
1760 			("\nnormal buffer must be FloatBuffer") ;
1761 		}
1762 	    }
1763 	}
1764 
copyVertex(int pi, int ni, int ci, VertexCopy vc)1765 	void copyVertex(int pi, int ni, int ci, VertexCopy vc) {
1766 	    pi *= 3 ;
1767 	    if (positionsF != null) {
1768 		vc.p = new Point3f(positionsF.get(pi + 0),
1769 				   positionsF.get(pi + 1),
1770 				   positionsF.get(pi + 2)) ;
1771 	    }
1772 	    else {
1773 		vc.p = new Point3f((float)positionsD.get(pi + 0),
1774 				   (float)positionsD.get(pi + 1),
1775 				   (float)positionsD.get(pi + 2)) ;
1776 	    }
1777 
1778 	    ni *= 3 ;
1779 	    if (vertexNormals) {
1780 		vc.n = new Vector3f(normals.get(ni + 0),
1781 				    normals.get(ni + 1),
1782 				    normals.get(ni + 2)) ;
1783 	    }
1784 
1785 	    if (vertexColor3) {
1786 		ci *= 3 ;
1787 		if (colorsB != null) {
1788 		    vc.c3 = new Color3f
1789 			((colorsB.get(ci + 0) & 0xff) * ByteToFloatScale,
1790 			 (colorsB.get(ci + 1) & 0xff) * ByteToFloatScale,
1791 			 (colorsB.get(ci + 2) & 0xff) * ByteToFloatScale) ;
1792 		}
1793 		else {
1794 		    vc.c3 = new Color3f(colorsF.get(ci + 0),
1795 					colorsF.get(ci + 1),
1796 					colorsF.get(ci + 2)) ;
1797 		}
1798 		vc.c = vc.c3 ;
1799 	    }
1800 	    else if (vertexColor4) {
1801 		ci *= 4 ;
1802 		if (colorsB != null) {
1803 		    vc.c4 = new Color4f
1804 			((colorsB.get(ci + 0) & 0xff) * ByteToFloatScale,
1805 			 (colorsB.get(ci + 1) & 0xff) * ByteToFloatScale,
1806 			 (colorsB.get(ci + 2) & 0xff) * ByteToFloatScale,
1807 			 (colorsB.get(ci + 3) & 0xff) * ByteToFloatScale) ;
1808 		}
1809 		else {
1810 		    vc.c4 = new Color4f(colorsF.get(ci + 0),
1811 					colorsF.get(ci + 1),
1812 					colorsF.get(ci + 2),
1813 					colorsF.get(ci + 3)) ;
1814 		}
1815 		vc.c = vc.c4 ;
1816 	    }
1817 	}
1818 
processVertex(int v, int stripFlag)1819 	public void processVertex(int v, int stripFlag) {
1820 	    copyVertex(v + initialPositionIndex,
1821 		       v + initialNormalIndex,
1822 		       v + initialColorIndex, vc) ;
1823 
1824 	    processVertexCopy(vc, stripFlag) ;
1825 	}
1826     }
1827 
1828     /**
1829      * This class implements the GeometryAccessor interface for
1830      * non-interleaved indexed geometry arrays accessed with NIO.
1831      */
1832     private class IndexedByRefGeometryNIO extends ByRefGeometryNIO {
1833 	IndexArrays ia = new IndexArrays() ;
1834 	VertexIndices vi = new VertexIndices() ;
1835 
IndexedByRefGeometryNIO(GeometryArray ga)1836 	IndexedByRefGeometryNIO(GeometryArray ga) {
1837 	    super(ga) ;
1838 	    getIndexArrays(ga, ia) ;
1839 	}
1840 
processVertex(int v, int stripFlag)1841 	public void processVertex(int v, int stripFlag) {
1842 	    getVertexIndices(v, ia, vi) ;
1843 	    copyVertex(vi.pi, vi.ni, vi.ci, vc) ;
1844 	    processIndexedVertexCopy(vc, vi, stripFlag) ;
1845 	}
1846     }
1847 
1848     /**
1849      * Convert a GeometryArray to compression stream elements and add them to
1850      * this stream.
1851      *
1852      * @param ga GeometryArray to convert
1853      * @exception IllegalArgumentException if GeometryArray has a
1854      * dimensionality or vertex format inconsistent with the CompressionStream
1855      */
addGeometryArray(GeometryArray ga)1856     void addGeometryArray(GeometryArray ga) {
1857 	int firstVertex = 0 ;
1858 	int validVertexCount = 0 ;
1859 	int vertexFormat = ga.getVertexFormat() ;
1860 	GeometryAccessor geometryAccessor = null ;
1861 
1862 	if (streamType != getStreamType(ga))
1863 	    throw new IllegalArgumentException
1864 		("GeometryArray has inconsistent dimensionality") ;
1865 
1866 	if (vertexComponents != getVertexComponents(vertexFormat))
1867 	    throw new IllegalArgumentException
1868 		("GeometryArray has inconsistent vertex components") ;
1869 
1870 	// Set up for vertex data access semantics.
1871 	boolean NIO = (vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0 ;
1872 	boolean byRef = (vertexFormat & GeometryArray.BY_REFERENCE) != 0 ;
1873 	boolean interleaved = (vertexFormat & GeometryArray.INTERLEAVED) != 0 ;
1874 	boolean indexedGeometry = ga instanceof IndexedGeometryArray ;
1875 
1876 	if (indexedGeometry) {
1877 	    if (debug) System.out.println("indexed") ;
1878 	    // Index arrays will be copied such that valid indices start at
1879 	    // offset 0 in the copied arrays.
1880 	    firstVertex = 0 ;
1881 	    validVertexCount = ((IndexedGeometryArray)ga).getValidIndexCount() ;
1882 	}
1883 
1884 	if (!byRef) {
1885 	    if (debug) System.out.println("by-copy") ;
1886 	    if (indexedGeometry) {
1887 		geometryAccessor = new IndexedByCopyGeometry(ga) ;
1888 	    }
1889 	    else {
1890 		firstVertex = 0 ;
1891 		validVertexCount = ga.getValidVertexCount() ;
1892 		geometryAccessor = new ByCopyGeometry(ga) ;
1893 	    }
1894 	}
1895 	else if (interleaved && NIO) {
1896 	    if (debug) System.out.println("interleaved NIO") ;
1897 	    if (indexedGeometry) {
1898 		geometryAccessor = new IndexedInterleavedGeometryNIO(ga) ;
1899 	    }
1900 	    else {
1901 		firstVertex = ga.getInitialVertexIndex() ;
1902 		validVertexCount = ga.getValidVertexCount() ;
1903 		geometryAccessor = new InterleavedGeometryNIO(ga) ;
1904 	    }
1905 	}
1906 	else if (interleaved && !NIO) {
1907 	    if (debug) System.out.println("interleaved") ;
1908 	    if (indexedGeometry) {
1909 		geometryAccessor = new IndexedInterleavedGeometryFloat(ga) ;
1910 	    }
1911 	    else {
1912 		firstVertex = ga.getInitialVertexIndex() ;
1913 		validVertexCount = ga.getValidVertexCount() ;
1914 		geometryAccessor = new InterleavedGeometryFloat(ga) ;
1915 	    }
1916 	}
1917 	else if (!interleaved && NIO) {
1918 	    if (debug) System.out.println("non-interleaved NIO") ;
1919 	    if (indexedGeometry) {
1920 		geometryAccessor = new IndexedByRefGeometryNIO(ga) ;
1921 	    }
1922 	    else {
1923 		firstVertex = 0 ;
1924 		validVertexCount = ga.getValidVertexCount() ;
1925 		geometryAccessor = new ByRefGeometryNIO(ga) ;
1926 	    }
1927 	}
1928 	else if (!interleaved && !NIO) {
1929 	    if (debug) System.out.println("non-interleaved by-ref") ;
1930 	    if (indexedGeometry) {
1931 		geometryAccessor = new IndexedByRefGeometry(ga) ;
1932 	    }
1933 	    else {
1934 		firstVertex = 0 ;
1935 		validVertexCount = ga.getValidVertexCount() ;
1936 		geometryAccessor = new ByRefGeometry(ga) ;
1937 	    }
1938 	}
1939 
1940 	// Set up for topology.
1941 	int stripCount = 0 ;
1942 	int stripCounts[] = null ;
1943 	int constantStripLength = 0 ;
1944 	int replaceCode = RESTART ;
1945 	boolean strips = false ;
1946 	boolean implicitStrips = false ;
1947 
1948 	if (ga instanceof TriangleStripArray ||
1949 	    ga instanceof IndexedTriangleStripArray ||
1950 	    ga instanceof LineStripArray ||
1951 	    ga instanceof IndexedLineStripArray) {
1952 
1953 	    strips = true ;
1954 	    replaceCode = REPLACE_OLDEST ;
1955 	    if (debug) System.out.println("strips") ;
1956 	}
1957 	else if (ga instanceof TriangleFanArray ||
1958 		 ga instanceof IndexedTriangleFanArray) {
1959 
1960 	    strips = true ;
1961 	    replaceCode = REPLACE_MIDDLE ;
1962 	    if (debug) System.out.println("fans") ;
1963 	}
1964 	else if (ga instanceof QuadArray ||
1965 		 ga instanceof IndexedQuadArray) {
1966 
1967 	    // Handled as fan arrays with 4 vertices per fan.
1968 	    implicitStrips = true ;
1969 	    constantStripLength = 4 ;
1970 	    replaceCode = REPLACE_MIDDLE ;
1971 	    if (debug) System.out.println("quads") ;
1972 	}
1973 
1974 	// Get strip counts.
1975 	if (strips) {
1976 	    if (indexedGeometry) {
1977 		IndexedGeometryStripArray igsa ;
1978 		igsa = (IndexedGeometryStripArray)ga ;
1979 
1980 		stripCount = igsa.getNumStrips() ;
1981 		stripCounts = new int[stripCount] ;
1982 		igsa.getStripIndexCounts(stripCounts) ;
1983 
1984 	    } else {
1985 		GeometryStripArray gsa ;
1986 		gsa = (GeometryStripArray)ga ;
1987 
1988 		stripCount = gsa.getNumStrips() ;
1989 		stripCounts = new int[stripCount] ;
1990 		gsa.getStripVertexCounts(stripCounts) ;
1991 	    }
1992 	}
1993 
1994 	// Build the compression stream for this shape's geometry.
1995 	int v = firstVertex ;
1996 	if (strips) {
1997 	    for (int i = 0 ; i < stripCount ; i++) {
1998 		geometryAccessor.processVertex(v++, RESTART) ;
1999 		for (int j = 1 ; j < stripCounts[i] ; j++) {
2000 		    geometryAccessor.processVertex(v++, replaceCode) ;
2001 		}
2002 	    }
2003 	}
2004 	else if (implicitStrips) {
2005 	    while (v < firstVertex + validVertexCount) {
2006 		geometryAccessor.processVertex(v++, RESTART) ;
2007 		for (int j = 1 ; j < constantStripLength ; j++) {
2008 		    geometryAccessor.processVertex(v++, replaceCode) ;
2009 		}
2010 	    }
2011 	}
2012 	else {
2013 	    while (v < firstVertex + validVertexCount) {
2014 		geometryAccessor.processVertex(v++, RESTART) ;
2015 	    }
2016 	}
2017     }
2018 
2019     /**
2020      * Print the stream to standard output.
2021      */
print()2022     void print() {
2023 	System.out.println("\nstream has " + stream.size() + " entries") ;
2024 	System.out.println("uncompressed size " + byteCount + " bytes") ;
2025 	System.out.println("upper position bound: " + mcBounds[1].toString()) ;
2026 	System.out.println("lower position bound: " + mcBounds[0].toString()) ;
2027 	System.out.println("X, Y, Z centers (" +
2028 			   ((float)center[0]) + " " +
2029 			   ((float)center[1]) + " " +
2030 			   ((float)center[2]) + ")\n" +
2031 			   "scale " + ((float)scale) + "\n") ;
2032 
2033 	Iterator i = stream.iterator() ;
2034 	while (i.hasNext()) {
2035 	    System.out.println(i.next().toString() + "\n") ;
2036 	}
2037     }
2038 
2039 
2040     ////////////////////////////////////////////////////////////////////////////
2041     //									      //
2042     // The following constructors and methods are currently the only public   //
2043     // members of this class.  All other members are subject to revision.     //
2044     //									      //
2045     ////////////////////////////////////////////////////////////////////////////
2046 
2047     /**
2048      * Creates a CompressionStream from an array of Shape3D scene graph
2049      * objects.  These Shape3D objects may only consist of a GeometryArray
2050      * component and an optional Appearance component.  The resulting stream
2051      * may be used as input to the GeometryCompressor methods.<p>
2052      *
2053      * Each Shape3D in the array must be of the same dimensionality (point,
2054      * line, or surface) and have the same vertex format as the others.
2055      * Texture coordinates are ignored.<p>
2056      *
2057      * If a color is specified in the material attributes for a Shape3D then
2058      * that color is added to the CompressionStream as the current global
2059      * color.  Subsequent colors as well as any colors bundled with vertices
2060      * will override it.  Only the material diffuse colors are used; all other
2061      * appearance attributes are ignored.<p>
2062      *
2063      * @param positionQuant
2064      * number of bits to quantize each position's X, Y,
2065      * and Z components, ranging from 1 to 16
2066      *
2067      * @param colorQuant
2068      * number of bits to quantize each color's R, G, B, and
2069      * alpha components, ranging from 2 to 16
2070      *
2071      * @param normalQuant
2072      * number of bits for quantizing each normal's U and V components, ranging
2073      * from 0 to 6
2074      *
2075      * @param shapes
2076      * an array of Shape3D scene graph objects containing
2077      * GeometryArray objects, all with the same vertex format and
2078      * dimensionality
2079      *
2080      * @exception IllegalArgumentException if any Shape3D has an inconsistent
2081      * dimensionality or vertex format, or if any Shape3D contains a geometry
2082      * component that is not a GeometryArray
2083      *
2084      * @see Shape3D
2085      * @see GeometryArray
2086      * @see GeometryCompressor
2087      */
CompressionStream(int positionQuant, int colorQuant, int normalQuant, Shape3D shapes[])2088     public CompressionStream(int positionQuant, int colorQuant,
2089 			     int normalQuant, Shape3D shapes[]) {
2090 	this() ;
2091 	if (debug) System.out.println("CompressionStream(Shape3D[]):") ;
2092 
2093 	if (shapes == null)
2094 	    throw new IllegalArgumentException("null Shape3D array") ;
2095 
2096 	if (shapes.length == 0)
2097 	    throw new IllegalArgumentException("zero-length Shape3D array") ;
2098 
2099 	if (shapes[0] == null)
2100 	    throw new IllegalArgumentException("Shape3D at index 0 is null") ;
2101 
2102 	long startTime = 0 ;
2103 	if (benchmark) startTime = System.currentTimeMillis() ;
2104 
2105 	Geometry g = shapes[0].getGeometry() ;
2106 	if (! (g instanceof GeometryArray))
2107 	    throw new IllegalArgumentException
2108 		("Shape3D at index 0 is not a GeometryArray") ;
2109 
2110 	GeometryArray ga = (GeometryArray)g ;
2111 	this.streamType = getStreamType(ga) ;
2112 	this.vertexComponents = getVertexComponents(ga.getVertexFormat()) ;
2113 
2114 	// Add global quantization parameters to the start of the stream.
2115 	addPositionQuantization(positionQuant) ;
2116 	addColorQuantization(colorQuant) ;
2117 	addNormalQuantization(normalQuant) ;
2118 
2119 	// Loop through all shapes.
2120 	for (int s = 0 ; s < shapes.length ; s++) {
2121 	    if (debug) System.out.println("\nShape3D " + s + ":") ;
2122 
2123 	    g = shapes[s].getGeometry() ;
2124 	    if (! (g instanceof GeometryArray))
2125 		throw new IllegalArgumentException
2126 		    ("Shape3D at index " + s + " is not a GeometryArray") ;
2127 
2128 	    // Check for material color and add it to the stream if it exists.
2129 	    Appearance a = shapes[s].getAppearance() ;
2130 	    if (a != null) {
2131 		Material m = a.getMaterial() ;
2132 		if (m != null) {
2133 		    m.getDiffuseColor(c3f) ;
2134 		    if (vertexColor4) {
2135 			c4f.set(c3f.x, c3f.y, c3f.z, 1.0f) ;
2136 			addColor(c4f) ;
2137 		    } else
2138 			addColor(c3f) ;
2139 		}
2140 	    }
2141 
2142 	    // Add the geometry array to the stream.
2143 	    addGeometryArray((GeometryArray)g) ;
2144 	}
2145 
2146 	if (benchmark) {
2147 	    long t = System.currentTimeMillis() - startTime ;
2148 	    System.out.println
2149 		("\nCompressionStream:\n" + shapes.length + " shapes in " +
2150 		 (t / 1000f) + " sec") ;
2151 	}
2152     }
2153 
2154     /**
2155      * Creates a CompressionStream from an array of Shape3D scene graph
2156      * objects.  These Shape3D objects may only consist of a GeometryArray
2157      * component and an optional Appearance component.  The resulting stream
2158      * may be used as input to the GeometryCompressor methods.<p>
2159      *
2160      * Each Shape3D in the array must be of the same dimensionality (point,
2161      * line, or surface) and have the same vertex format as the others.
2162      * Texture coordinates are ignored.<p>
2163      *
2164      * If a color is specified in the material attributes for a Shape3D then
2165      * that color is added to the CompressionStream as the current global
2166      * color.  Subsequent colors as well as any colors bundled with vertices
2167      * will override it.  Only the material diffuse colors are used; all other
2168      * appearance attributes are ignored.<p>
2169      *
2170      * Defaults of 16, 9, and 6 bits are used as the quantization values for
2171      * positions, colors, and normals respectively.  These are the maximum
2172      * resolution values defined for positions and normals; the default of 9
2173      * for color is the equivalent of the 8 bits of RGBA component resolution
2174      * commonly available in graphics frame buffers.<p>
2175      *
2176      * @param shapes
2177      * an array of Shape3D scene graph objects containing
2178      * GeometryArray objects, all with the same vertex format and
2179      * dimensionality.
2180      *
2181      * @exception IllegalArgumentException if any Shape3D has an inconsistent
2182      * dimensionality or vertex format, or if any Shape3D contains a geometry
2183      * component that is not a GeometryArray
2184      *
2185      * @see Shape3D
2186      * @see GeometryArray
2187      * @see GeometryCompressor
2188      */
CompressionStream(Shape3D shapes[])2189     public CompressionStream(Shape3D shapes[]) {
2190 	this(16, 9, 6, shapes) ;
2191     }
2192 
2193     /**
2194      * Creates a CompressionStream from an array of GeometryInfo objects.  The
2195      * resulting stream may be used as input to the GeometryCompressor
2196      * methods.<p>
2197      *
2198      * Each GeometryInfo in the array must be of the same dimensionality
2199      * (point, line, or surface) and have the same vertex format as the
2200      * others.  Texture coordinates are ignored.<p>
2201      *
2202      * @param positionQuant
2203      * number of bits to quantize each position's X, Y,
2204      * and Z components, ranging from 1 to 16
2205      *
2206      * @param colorQuant
2207      * number of bits to quantize each color's R, G, B, and
2208      * alpha components, ranging from 2 to 16
2209      *
2210      * @param normalQuant
2211      * number of bits for quantizing each normal's U and V components, ranging
2212      * from 0 to 6
2213      *
2214      * @param geometry
2215      * an array of GeometryInfo objects, all with the same
2216      * vertex format and dimensionality
2217      *
2218      * @exception IllegalArgumentException if any GeometryInfo object has an
2219      * inconsistent dimensionality or vertex format
2220      *
2221      * @see GeometryInfo
2222      * @see GeometryCompressor
2223      */
CompressionStream(int positionQuant, int colorQuant, int normalQuant, GeometryInfo geometry[])2224     public CompressionStream(int positionQuant, int colorQuant,
2225 			     int normalQuant, GeometryInfo geometry[]) {
2226 	this() ;
2227 	if (debug) System.out.println("CompressionStream(GeometryInfo[])") ;
2228 
2229 	if (geometry == null)
2230 	    throw new IllegalArgumentException("null GeometryInfo array") ;
2231 
2232 	if (geometry.length == 0)
2233 	    throw new IllegalArgumentException
2234 		("zero-length GeometryInfo array") ;
2235 
2236 	if (geometry[0] == null)
2237 	    throw new IllegalArgumentException
2238 		("GeometryInfo at index 0 is null") ;
2239 
2240 	long startTime = 0 ;
2241 	if (benchmark) startTime = System.currentTimeMillis() ;
2242 
2243 	GeometryArray ga = geometry[0].getGeometryArray() ;
2244 	this.streamType = getStreamType(ga) ;
2245 	this.vertexComponents = getVertexComponents(ga.getVertexFormat()) ;
2246 
2247 	// Add global quantization parameters to the start of the stream.
2248 	addPositionQuantization(positionQuant) ;
2249 	addColorQuantization(colorQuant) ;
2250 	addNormalQuantization(normalQuant) ;
2251 
2252 	// Loop through all GeometryInfo objects and add them to the stream.
2253 	for (int i = 0 ; i < geometry.length ; i++) {
2254 	    if (debug) System.out.println("\nGeometryInfo " + i + ":") ;
2255 	    addGeometryArray(geometry[i].getGeometryArray()) ;
2256 	}
2257 
2258 	if (benchmark) {
2259 	    long t = System.currentTimeMillis() - startTime ;
2260 	    System.out.println
2261 		("\nCompressionStream:\n" + geometry.length +
2262 		 " GeometryInfo objects in " + (t / 1000f) + " sec") ;
2263 	}
2264     }
2265 
2266     /**
2267      * Creates a CompressionStream from an array of GeometryInfo objects.  The
2268      * resulting stream may be used as input to the GeometryCompressor
2269      * methods.<p>
2270      *
2271      * Each GeometryInfo in the array must be of the same dimensionality
2272      * (point, line, or surface) and have the same vertex format as the
2273      * others.  Texture coordinates are ignored.<p>
2274      *
2275      * Defaults of 16, 9, and 6 bits are used as the quantization values for
2276      * positions, colors, and normals respectively.  These are the maximum
2277      * resolution values defined for positions and normals; the default of 9
2278      * for color is the equivalent of the 8 bits of RGBA component resolution
2279      * commonly available in graphics frame buffers.<p>
2280      *
2281      * @param geometry
2282      * an array of GeometryInfo objects, all with the same
2283      * vertex format and dimensionality
2284      *
2285      * @exception IllegalArgumentException if any GeometryInfo object has an
2286      * inconsistent dimensionality or vertex format
2287      *
2288      * @see GeometryInfo
2289      * @see GeometryCompressor
2290      */
CompressionStream(GeometryInfo geometry[])2291     public CompressionStream(GeometryInfo geometry[]) {
2292 	this(16, 9, 6, geometry) ;
2293     }
2294 
2295     /**
2296      * Get the original bounds of the coordinate data, in modeling coordinates.
2297      * Coordinate data is positioned and scaled to a normalized cube after
2298      * compression.
2299      *
2300      * @return Point3d array of length 2, where the 1st Point3d is the lower
2301      * bounds and the 2nd Point3d is the upper bounds.
2302      * @since Java 3D 1.3
2303      */
getModelBounds()2304     public Point3d[] getModelBounds() {
2305 	Point3d[] bounds = new Point3d[2] ;
2306 	bounds[0] = new Point3d(mcBounds[0]) ;
2307 	bounds[1] = new Point3d(mcBounds[1]) ;
2308 	return bounds ;
2309     }
2310 
2311     /**
2312      * Get the bounds of the compressed object in normalized coordinates.
2313      * These have an maximum bounds by [-1.0 .. +1.0] across each axis.
2314      *
2315      * @return Point3d array of length 2, where the 1st Point3d is the lower
2316      * bounds and the 2nd Point3d is the upper bounds.
2317      * @since Java 3D 1.3
2318      */
getNormalizedBounds()2319     public Point3d[] getNormalizedBounds() {
2320 	Point3d[] bounds = new Point3d[2] ;
2321 	bounds[0] = new Point3d(ncBounds[0]) ;
2322 	bounds[1] = new Point3d(ncBounds[1]) ;
2323 	return bounds ;
2324     }
2325 }
2326