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