0001: /*
0002: * $RCSfile: GeometryDecompressor.java,v $
0003: *
0004: * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * Redistribution and use in source and binary forms, with or without
0007: * modification, are permitted provided that the following conditions
0008: * are met:
0009: *
0010: * - Redistribution of source code must retain the above copyright
0011: * notice, this list of conditions and the following disclaimer.
0012: *
0013: * - Redistribution in binary form must reproduce the above copyright
0014: * notice, this list of conditions and the following disclaimer in
0015: * the documentation and/or other materials provided with the
0016: * distribution.
0017: *
0018: * Neither the name of Sun Microsystems, Inc. or the names of
0019: * contributors may be used to endorse or promote products derived
0020: * from this software without specific prior written permission.
0021: *
0022: * This software is provided "AS IS," without a warranty of any
0023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
0024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
0025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
0026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
0027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
0028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
0029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
0030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
0031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
0032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
0033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
0034: * POSSIBILITY OF SUCH DAMAGES.
0035: *
0036: * You acknowledge that this software is not designed, licensed or
0037: * intended for use in the design, construction, operation or
0038: * maintenance of any nuclear facility.
0039: *
0040: * $Revision: 1.3 $
0041: * $Date: 2007/02/09 17:20:22 $
0042: * $State: Exp $
0043: */
0044:
0045: package com.sun.j3d.utils.geometry.compression;
0046:
0047: import com.sun.j3d.internal.J3dUtilsI18N;
0048: import javax.vecmath.Color4f;
0049: import javax.vecmath.Point3f;
0050: import javax.vecmath.Vector3f;
0051:
0052: /**
0053: * This abstract class provides the base methods needed to create a geometry
0054: * decompressor. Subclasses must implement a backend to handle the output,
0055: * consisting of a generalized triangle strip, line strip, or point array,
0056: * along with possible global color and normal changes.
0057: */
0058: abstract class GeometryDecompressor {
0059: private static final boolean debug = false;
0060: private static final boolean benchmark = false;
0061:
0062: /**
0063: * Compressed geometry format version supported.
0064: */
0065: static final int majorVersionNumber = 1;
0066: static final int minorVersionNumber = 0;
0067: static final int minorMinorVersionNumber = 2;
0068:
0069: /**
0070: * This method is called when a SetState command is encountered in the
0071: * decompression stream.
0072: *
0073: * @param bundlingNorm true indicates normals are bundled with vertices
0074: * @param bundlingColor true indicates colors are bundled with vertices
0075: * @param doingAlpha true indicates alpha values are bundled with vertices
0076: */
0077: abstract void outputVertexFormat(boolean bundlingNorm,
0078: boolean bundlingColor, boolean doingAlpha);
0079:
0080: /**
0081: * This method captures the vertex output of the decompressor. The normal
0082: * or color references may be null if the corresponding data is not
0083: * bundled with the vertices in the compressed geometry buffer. Alpha
0084: * values may be included in the color.
0085: *
0086: * @param position The coordinates of the vertex.
0087: * @param normal The normal bundled with the vertex. May be null.
0088: * @param color The color bundled with the vertex. May be null.
0089: * Alpha may be present.
0090: * @param vertexReplaceCode Specifies the generalized strip flag
0091: * that is bundled with each vertex.
0092: * @see GeneralizedStripFlags
0093: * @see CompressedGeometryHeader
0094: */
0095: abstract void outputVertex(Point3f position, Vector3f normal,
0096: Color4f color, int vertexReplaceCode);
0097:
0098: /**
0099: * This method captures the global color output of the decompressor. It
0100: * is only invoked if colors are not bundled with the vertex data. The
0101: * global color applies to all succeeding vertices until the next time the
0102: * method is invoked.
0103: *
0104: * @param color The current global color.
0105: */
0106: abstract void outputColor(Color4f color);
0107:
0108: /**
0109: * This method captures the global normal output of the decompressor. It
0110: * is only invoked if normals are not bundled with the vertex data. The
0111: * global normal applies to all succeeding vertices until the next time the
0112: * method is invoked.
0113: *
0114: * @param normal The current global normal.
0115: */
0116: abstract void outputNormal(Vector3f normal);
0117:
0118: // Geometry compression opcodes.
0119: private static final int GC_VERTEX = 0x40;
0120: private static final int GC_SET_NORM = 0xC0;
0121: private static final int GC_SET_COLOR = 0x80;
0122: private static final int GC_MESH_B_R = 0x20;
0123: private static final int GC_SET_STATE = 0x18;
0124: private static final int GC_SET_TABLE = 0x10;
0125: private static final int GC_PASS_THROUGH = 0x08;
0126: private static final int GC_EOS = 0x00;
0127: private static final int GC_V_NO_OP = 0x01;
0128: private static final int GC_SKIP_8 = 0x07;
0129:
0130: // Three 64-entry decompression tables are used: gctables[0] for
0131: // positions, gctables[1] for colors, and gctables[2] for normals.
0132: private HuffmanTableEntry gctables[][];
0133:
0134: /**
0135: * Decompression table entry.
0136: */
0137: static class HuffmanTableEntry {
0138: int tagLength, dataLength;
0139: int rightShift, absolute;
0140:
0141: public String toString() {
0142: return " tag length: " + tagLength + " data length: "
0143: + dataLength + " shift: " + rightShift
0144: + " abs/rel: " + absolute;
0145: }
0146: }
0147:
0148: // A 16-entry mesh buffer is used.
0149: private MeshBufferEntry meshBuffer[];
0150: private int meshIndex = 15;
0151: private int meshState;
0152:
0153: // meshState values. These are needed to determine if colors and/or
0154: // normals should come from meshBuffer or from SetColor or SetNormal.
0155: private static final int USE_MESH_NORMAL = 0x1;
0156: private static final int USE_MESH_COLOR = 0x2;
0157:
0158: /**
0159: * Mesh buffer entry containing position, normal, and color.
0160: */
0161: static class MeshBufferEntry {
0162: short x, y, z;
0163: short octant, sextant, u, v;
0164: short r, g, b, a;
0165: }
0166:
0167: // Geometry compression state variables.
0168: private short curX, curY, curZ;
0169: private short curR, curG, curB, curA;
0170: private int curSex, curOct, curU, curV;
0171:
0172: // Current vertex data.
0173: private Point3f curPos;
0174: private Vector3f curNorm;
0175: private Color4f curColor;
0176: private int repCode;
0177:
0178: // Flags indicating what data is bundled with the vertex.
0179: private boolean bundlingNorm;
0180: private boolean bundlingColor;
0181: private boolean doingAlpha;
0182:
0183: // Internal decompression buffering variables.
0184: private int currentHeader = 0;
0185: private int nextHeader = 0;
0186: private int bitBuffer = 0;
0187: private int bitBufferCount = 32;
0188:
0189: // Used for benchmarking if so configured.
0190: private long startTime;
0191: private int vertexCount;
0192:
0193: // Bit-field masks: BMASK[i] = (1<<i)-1
0194: private static final int BMASK[] = { 0x0, 0x1, 0x3, 0x7, 0xF, 0x1F,
0195: 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF,
0196: 0x3FFF, 0x7FFF, 0xFFFF, 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF,
0197: 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, 0x1FFFFFF,
0198: 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF,
0199: 0x7FFFFFFF, 0xFFFFFFFF, };
0200:
0201: // A reference to the compressed data and the current offset.
0202: private byte gcData[];
0203: private int gcIndex;
0204:
0205: // The normals table for decoding 6-bit [u,v] spherical sextant coordinates.
0206: private static final double gcNormals[][][];
0207: private static final double NORMAL_MAX_Y_ANG = 0.615479709;
0208: private static final boolean printNormalTable = false;
0209:
0210: /**
0211: * Initialize the normals table.
0212: */
0213: static {
0214: int i, j, inx, iny, inz;
0215: double th, psi, qnx, qny, qnz;
0216:
0217: gcNormals = new double[65][65][3];
0218:
0219: for (i = 0; i < 65; i++) {
0220: for (j = 0; j < 65; j++) {
0221: if (i + j > 64)
0222: continue;
0223:
0224: psi = NORMAL_MAX_Y_ANG * (i / 64.0);
0225: th = Math.asin(Math.tan(NORMAL_MAX_Y_ANG
0226: * ((64 - j) / 64.0)));
0227:
0228: qnx = Math.cos(th) * Math.cos(psi);
0229: qny = Math.sin(psi);
0230: qnz = Math.sin(th) * Math.cos(psi);
0231:
0232: // Convert the floating point normal to s1.14 bit notation,
0233: // then back again.
0234: qnx = qnx * 16384.0;
0235: inx = (int) qnx;
0236: qnx = (double) inx;
0237: qnx = qnx / 16384.0;
0238:
0239: qny = qny * 16384.0;
0240: iny = (int) qny;
0241: qny = (double) iny;
0242: qny = qny / 16384.0;
0243:
0244: qnz = qnz * 16384.0;
0245: inz = (int) qnz;
0246: qnz = (double) inz;
0247: qnz = qnz / 16384.0;
0248:
0249: gcNormals[i][j][0] = qnx;
0250: gcNormals[i][j][1] = qny;
0251: gcNormals[i][j][2] = qnz;
0252: }
0253: }
0254:
0255: if (printNormalTable) {
0256: System.out.println("struct {");
0257: System.out.println(" double nx, ny, nz ;");
0258: System.out.println("} gcNormals[65][65] = {");
0259: for (i = 0; i <= 64; i++) {
0260: System.out.println("{");
0261: for (j = 0; j <= 64; j++) {
0262: if (j + i > 64)
0263: continue;
0264: System.out.println("{ " + gcNormals[i][j][0] + ", "
0265: + gcNormals[i][j][1] + ", "
0266: + gcNormals[i][j][2] + " }");
0267: }
0268: System.out.println("},");
0269: }
0270: System.out.println("}");
0271: }
0272: }
0273:
0274: //
0275: // The constructor.
0276: //
0277: GeometryDecompressor() {
0278: curPos = new Point3f();
0279: curNorm = new Vector3f();
0280: curColor = new Color4f();
0281: gctables = new HuffmanTableEntry[3][64];
0282:
0283: for (int i = 0; i < 64; i++) {
0284: gctables[0][i] = new HuffmanTableEntry();
0285: gctables[1][i] = new HuffmanTableEntry();
0286: gctables[2][i] = new HuffmanTableEntry();
0287: }
0288:
0289: meshBuffer = new MeshBufferEntry[16];
0290: for (int i = 0; i < 16; i++)
0291: meshBuffer[i] = new MeshBufferEntry();
0292: }
0293:
0294: /**
0295: * Check version numbers and return true if compatible.
0296: */
0297: boolean checkVersion(int majorVersionNumber, int minorVersionNumber) {
0298: return ((majorVersionNumber < this .majorVersionNumber) || ((majorVersionNumber == this .majorVersionNumber) && (minorVersionNumber <= this .minorVersionNumber)));
0299: }
0300:
0301: /**
0302: * Decompress data and invoke abstract output methods.
0303: *
0304: * @param start byte offset to start of compressed geometry in data array
0305: * @param length size of compressed geometry in bytes
0306: * @param data array containing compressed geometry buffer of the
0307: * specified length at the given offset from the start of the array
0308: * @exception ArrayIndexOutOfBoundsException if start+length > data size
0309: */
0310: void decompress(int start, int length, byte data[]) {
0311: if (debug)
0312: System.out.println("GeometryDecompressor.decompress\n"
0313: + " start: " + start + " length: " + length
0314: + " data array size: " + data.length);
0315: if (benchmark)
0316: benchmarkStart(length);
0317:
0318: if (start + length > data.length)
0319: throw new ArrayIndexOutOfBoundsException(J3dUtilsI18N
0320: .getString("GeometryDecompressor0"));
0321:
0322: // Set reference to compressed data and skip to start of data.
0323: gcData = data;
0324: gcIndex = start;
0325:
0326: // Initialize state.
0327: bitBufferCount = 0;
0328: meshState = 0;
0329: bundlingNorm = false;
0330: bundlingColor = false;
0331: doingAlpha = false;
0332: repCode = 0;
0333:
0334: // Headers are interleaved for hardware implementations, so the
0335: // first is always a nullop.
0336: nextHeader = GC_V_NO_OP;
0337:
0338: // Enter decompression loop.
0339: while (gcIndex < start + length)
0340: processDecompression();
0341:
0342: // Finish out any bits left in bitBuffer.
0343: while (bitBufferCount > 0)
0344: processDecompression();
0345:
0346: if (benchmark)
0347: benchmarkPrint(length);
0348: }
0349:
0350: //
0351: // Return the next bitCount bits of compressed data.
0352: //
0353: private int getBits(int bitCount, String d) {
0354: int bits;
0355:
0356: if (debug)
0357: System.out.print(" getBits(" + bitCount + ") " + d + ", "
0358: + bitBufferCount + " available at gcIndex "
0359: + gcIndex);
0360:
0361: if (bitCount == 0) {
0362: if (debug)
0363: System.out.println(": got 0x0");
0364: return 0;
0365: }
0366:
0367: if (bitBufferCount == 0) {
0368: bitBuffer = (((gcData[gcIndex++] & 0xff) << 24)
0369: | ((gcData[gcIndex++] & 0xff) << 16)
0370: | ((gcData[gcIndex++] & 0xff) << 8) | ((gcData[gcIndex++] & 0xff)));
0371:
0372: bitBufferCount = 32;
0373: }
0374:
0375: if (bitBufferCount >= bitCount) {
0376: bits = (bitBuffer >>> (32 - bitCount)) & BMASK[bitCount];
0377: bitBuffer = bitBuffer << bitCount;
0378: bitBufferCount -= bitCount;
0379: } else {
0380: bits = (bitBuffer >>> (32 - bitCount)) & BMASK[bitCount];
0381: bits = bits >>> (bitCount - bitBufferCount);
0382: bits = bits << (bitCount - bitBufferCount);
0383:
0384: bitBuffer = (((gcData[gcIndex++] & 0xff) << 24)
0385: | ((gcData[gcIndex++] & 0xff) << 16)
0386: | ((gcData[gcIndex++] & 0xff) << 8) | ((gcData[gcIndex++] & 0xff)));
0387:
0388: bits = bits
0389: | ((bitBuffer >>> (32 - (bitCount - bitBufferCount))) & BMASK[bitCount
0390: - bitBufferCount]);
0391:
0392: bitBuffer = bitBuffer << (bitCount - bitBufferCount);
0393: bitBufferCount = 32 - (bitCount - bitBufferCount);
0394: }
0395:
0396: if (debug)
0397: System.out.println(": got 0x" + Integer.toHexString(bits));
0398:
0399: return bits;
0400: }
0401:
0402: //
0403: // Shuffle interleaved headers and opcodes.
0404: //
0405: private void processDecompression() {
0406: int mbp;
0407: currentHeader = nextHeader;
0408:
0409: if ((currentHeader & 0xC0) == GC_VERTEX) {
0410: // Process a vertex.
0411: if (!bundlingNorm && !bundlingColor) {
0412: // get next opcode, process current position opcode
0413: nextHeader = getBits(8, "header");
0414: mbp = processDecompressionOpcode(0);
0415:
0416: } else if (bundlingNorm && !bundlingColor) {
0417: // get normal header, process current position opcode
0418: nextHeader = getBits(6, "normal");
0419: mbp = processDecompressionOpcode(0);
0420: currentHeader = nextHeader | GC_SET_NORM;
0421:
0422: // get next opcode, process current normal opcode
0423: nextHeader = getBits(8, "header");
0424: processDecompressionOpcode(mbp);
0425:
0426: } else if (!bundlingNorm && bundlingColor) {
0427: // get color header, process current position opcode
0428: nextHeader = getBits(6, "color");
0429: mbp = processDecompressionOpcode(0);
0430: currentHeader = nextHeader | GC_SET_COLOR;
0431:
0432: // get next opcode, process current color opcode
0433: nextHeader = getBits(8, "header");
0434: processDecompressionOpcode(mbp);
0435:
0436: } else {
0437: // get normal header, process current position opcode
0438: nextHeader = getBits(6, "normal");
0439: mbp = processDecompressionOpcode(0);
0440: currentHeader = nextHeader | GC_SET_NORM;
0441:
0442: // get color header, process current normal opcode
0443: nextHeader = getBits(6, "color");
0444: processDecompressionOpcode(mbp);
0445: currentHeader = nextHeader | GC_SET_COLOR;
0446:
0447: // get next opcode, process current color opcode
0448: nextHeader = getBits(8, "header");
0449: processDecompressionOpcode(mbp);
0450: }
0451:
0452: // Send out the complete vertex.
0453: outputVertex(curPos, curNorm, curColor, repCode);
0454: if (benchmark)
0455: vertexCount++;
0456:
0457: // meshState bits get turned off in the setColor and setNormal
0458: // routines in order to keep track of what data a mesh buffer
0459: // reference should use.
0460: meshState |= USE_MESH_NORMAL;
0461: meshState |= USE_MESH_COLOR;
0462:
0463: } else {
0464: // Non-vertex case: get next opcode, then process current opcode.
0465: nextHeader = getBits(8, "header");
0466: processDecompressionOpcode(0);
0467: }
0468: }
0469:
0470: //
0471: // Decode the opcode in currentHeader, and dispatch to the appropriate
0472: // processing method.
0473: //
0474: private int processDecompressionOpcode(int mbp) {
0475: if ((currentHeader & 0xC0) == GC_SET_NORM)
0476: processSetNormal(mbp);
0477: else if ((currentHeader & 0xC0) == GC_SET_COLOR)
0478: processSetColor(mbp);
0479: else if ((currentHeader & 0xC0) == GC_VERTEX)
0480: // Return the state of the mesh buffer push bit
0481: // when processing a vertex.
0482: return processVertex();
0483: else if ((currentHeader & 0xE0) == GC_MESH_B_R) {
0484: processMeshBR();
0485:
0486: // Send out the complete vertex.
0487: outputVertex(curPos, curNorm, curColor, repCode);
0488: if (benchmark)
0489: vertexCount++;
0490:
0491: // meshState bits get turned off in the setColor and setNormal
0492: // routines in order to keep track of what data a mesh buffer
0493: // reference should use.
0494: meshState |= USE_MESH_NORMAL;
0495: meshState |= USE_MESH_COLOR;
0496: } else if ((currentHeader & 0xF8) == GC_SET_STATE)
0497: processSetState();
0498: else if ((currentHeader & 0xF8) == GC_SET_TABLE)
0499: processSetTable();
0500: else if ((currentHeader & 0xFF) == GC_EOS)
0501: processEos();
0502: else if ((currentHeader & 0xFF) == GC_V_NO_OP)
0503: processVNoop();
0504: else if ((currentHeader & 0xFF) == GC_PASS_THROUGH)
0505: processPassThrough();
0506: else if ((currentHeader & 0xFF) == GC_SKIP_8)
0507: processSkip8();
0508:
0509: return 0;
0510: }
0511:
0512: //
0513: // Process a set state opcode.
0514: //
0515: private void processSetState() {
0516: int ii;
0517: if (debug)
0518: System.out.println("GeometryDecompressor.processSetState");
0519:
0520: ii = getBits(3, "bundling");
0521:
0522: bundlingNorm = ((currentHeader & 0x1) != 0);
0523: bundlingColor = (((ii >>> 2) & 0x1) != 0);
0524: doingAlpha = (((ii >>> 1) & 0x1) != 0);
0525:
0526: if (debug)
0527: System.out.println(" bundling normal: " + bundlingNorm
0528: + " bundling color: " + bundlingColor
0529: + " alpha present: " + doingAlpha);
0530:
0531: // Call the abstract output implementation.
0532: outputVertexFormat(bundlingNorm, bundlingColor, doingAlpha);
0533: }
0534:
0535: //
0536: // Process a set decompression table opcode.
0537: //
0538: // Extract the parameters of the table set command,
0539: // and set the approprate table entries.
0540: //
0541: private void processSetTable() {
0542: HuffmanTableEntry gct[];
0543: int i, adr, tagLength, dataLength, rightShift, absolute;
0544: int ii, index;
0545:
0546: if (debug)
0547: System.out.println("GeometryDecompressor.processSetTable");
0548:
0549: // Get reference to approprate 64 entry table.
0550: index = (currentHeader & 0x6) >>> 1;
0551: gct = gctables[index];
0552:
0553: // Get the remaining bits of the set table command.
0554: ii = getBits(15, "set table");
0555:
0556: // Extract the individual fields from the two bit strings.
0557: adr = ((currentHeader & 0x1) << 6) | ((ii >>> 9) & 0x3F);
0558:
0559: // Get data length. For positions and colors, 0 really means 16, as 0
0560: // lengths are meaningless for them. Normal components are allowed to
0561: // have lengths of 0.
0562: dataLength = (ii >>> 5) & 0x0F;
0563: if (dataLength == 0 && index != 2)
0564: dataLength = 16;
0565:
0566: rightShift = ii & 0x0F;
0567: absolute = (ii >>> 4) & 0x1;
0568:
0569: //
0570: // Decode the tag length from the address field by finding the
0571: // first set 1 from the left in the bitfield.
0572: //
0573: for (tagLength = 6; tagLength > 0; tagLength--) {
0574: if ((adr >> tagLength) != 0)
0575: break;
0576: }
0577:
0578: // Shift the address bits up into place, and off the leading 1.
0579: adr = (adr << (6 - tagLength)) & 0x3F;
0580:
0581: if (debug)
0582: System.out.println(" table "
0583: + ((currentHeader & 0x6) >>> 1) + " address " + adr
0584: + " tag length " + tagLength + " data length "
0585: + dataLength + " shift " + rightShift
0586: + " absolute " + absolute);
0587:
0588: // Fill in the table fields with the specified values.
0589: for (i = 0; i < (1 << (6 - tagLength)); i++) {
0590: gct[adr + i].tagLength = tagLength;
0591: gct[adr + i].dataLength = dataLength;
0592: gct[adr + i].rightShift = rightShift;
0593: gct[adr + i].absolute = absolute;
0594: }
0595: }
0596:
0597: //
0598: // Process a vertex opcode. Any bundled normal and/or color will be
0599: // processed by separate methods. Return the mesh buffer push indicator.
0600: //
0601: private int processVertex() {
0602: HuffmanTableEntry gct;
0603: float fX, fY, fZ;
0604: short dx, dy, dz;
0605: int mbp, x, y, z, dataLen;
0606: int ii;
0607:
0608: // If the next command is a mesh buffer reference
0609: // then use colors and normals from the mesh buffer.
0610: meshState = 0;
0611:
0612: // Get a reference to the approprate tag table entry.
0613: gct = gctables[0][currentHeader & 0x3F];
0614:
0615: if (debug)
0616: System.out.println("GeometryDecompressor.processVertex\n"
0617: + gct.toString());
0618:
0619: // Get the true length of the data.
0620: dataLen = gct.dataLength - gct.rightShift;
0621:
0622: // Read in the replace code and mesh buffer push bits,
0623: // if they're not in the current header.
0624: if (6 - (3 * dataLen) - gct.tagLength > 0) {
0625: int numBits = 6 - (3 * dataLen) - gct.tagLength;
0626: int jj;
0627:
0628: jj = currentHeader & BMASK[numBits];
0629: ii = getBits(3 - numBits, "repcode/mbp");
0630: ii |= (jj << (3 - numBits));
0631: } else
0632: ii = getBits(3, "repcode/mbp");
0633:
0634: repCode = ii >>> 1;
0635: mbp = ii & 0x1;
0636:
0637: // Read in x, y, and z components.
0638: x = currentHeader & BMASK[6 - gct.tagLength];
0639:
0640: if (gct.tagLength + dataLen == 6) {
0641: y = getBits(dataLen, "y");
0642: z = getBits(dataLen, "z");
0643: } else if (gct.tagLength + dataLen < 6) {
0644: x = x >> (6 - gct.tagLength - dataLen);
0645:
0646: y = currentHeader & BMASK[6 - gct.tagLength - dataLen];
0647: if (gct.tagLength + 2 * dataLen == 6) {
0648: z = getBits(dataLen, "z");
0649: } else if (gct.tagLength + 2 * dataLen < 6) {
0650: y = y >> (6 - gct.tagLength - 2 * dataLen);
0651:
0652: z = currentHeader
0653: & BMASK[6 - gct.tagLength - 2 * dataLen];
0654: if (gct.tagLength + 3 * dataLen < 6) {
0655: z = z >> (6 - gct.tagLength - 3 * dataLen);
0656: } else if (gct.tagLength + 3 * dataLen > 6) {
0657: ii = getBits(dataLen
0658: - (6 - gct.tagLength - 2 * dataLen), "z");
0659: z = (z << (dataLen - (6 - gct.tagLength - 2 * dataLen)))
0660: | ii;
0661: }
0662: } else {
0663: ii = getBits(dataLen - (6 - gct.tagLength - dataLen),
0664: "y");
0665: y = (y << (dataLen - (6 - gct.tagLength - dataLen)))
0666: | ii;
0667: z = getBits(dataLen, "z");
0668: }
0669: } else {
0670: ii = getBits(dataLen - (6 - gct.tagLength), "x");
0671: x = (x << (dataLen - (6 - gct.tagLength))) | ii;
0672: y = getBits(dataLen, "y");
0673: z = getBits(dataLen, "z");
0674: }
0675:
0676: // Sign extend delta x y z components.
0677: x = x << (32 - dataLen);
0678: x = x >> (32 - dataLen);
0679: y = y << (32 - dataLen);
0680: y = y >> (32 - dataLen);
0681: z = z << (32 - dataLen);
0682: z = z >> (32 - dataLen);
0683:
0684: // Normalize values.
0685: dx = (short) (x << gct.rightShift);
0686: dy = (short) (y << gct.rightShift);
0687: dz = (short) (z << gct.rightShift);
0688:
0689: // Update current position, first adding deltas if in relative mode.
0690: if (gct.absolute != 0) {
0691: curX = dx;
0692: curY = dy;
0693: curZ = dz;
0694: if (debug)
0695: System.out.println(" absolute position: " + curX + " "
0696: + curY + " " + curZ);
0697: } else {
0698: curX += dx;
0699: curY += dy;
0700: curZ += dz;
0701: if (debug)
0702: System.out.println(" delta position: " + dx + " " + dy
0703: + " " + dz);
0704: }
0705:
0706: // Do optional mesh buffer push.
0707: if (mbp != 0) {
0708: // Increment to next position (meshIndex is initialized to 15).
0709: meshIndex = (meshIndex + 1) & 0xF;
0710: meshBuffer[meshIndex].x = curX;
0711: meshBuffer[meshIndex].y = curY;
0712: meshBuffer[meshIndex].z = curZ;
0713: if (debug)
0714: System.out
0715: .println(" pushed position into mesh buffer at "
0716: + meshIndex);
0717: }
0718:
0719: // Convert point back to [-1..1] floating point.
0720: fX = curX;
0721: fX /= 32768.0;
0722: fY = curY;
0723: fY /= 32768.0;
0724: fZ = curZ;
0725: fZ /= 32768.0;
0726: if (debug)
0727: System.out.println(" result position " + fX + " " + fY
0728: + " " + fZ);
0729:
0730: curPos.set(fX, fY, fZ);
0731: return mbp;
0732: }
0733:
0734: //
0735: // Process a set current normal opcode.
0736: //
0737: private void processSetNormal(int mbp) {
0738: HuffmanTableEntry gct;
0739: int index, du, dv, n, dataLength;
0740: int ii;
0741:
0742: // if next command is a mesh buffer reference, use this normal
0743: meshState &= ~USE_MESH_NORMAL;
0744:
0745: // use table 2 for normals
0746: gct = gctables[2][currentHeader & 0x3F];
0747:
0748: if (debug)
0749: System.out
0750: .println("GeometryDecompressor.processSetNormal\n"
0751: + gct.toString());
0752:
0753: // subtract up-shift amount to get true data (u, v) length
0754: dataLength = gct.dataLength - gct.rightShift;
0755:
0756: if (gct.absolute != 0) {
0757: //
0758: // Absolute normal case. Extract index from 6-bit tag.
0759: //
0760: index = currentHeader & BMASK[6 - gct.tagLength];
0761:
0762: if (gct.tagLength != 0) {
0763: // read in the rest of the 6-bit sex/oct pair (index)
0764: ii = getBits(6 - (6 - gct.tagLength), "sex/oct");
0765: index = (index << (6 - (6 - gct.tagLength))) | ii;
0766: }
0767:
0768: // read in u and v data
0769: curU = getBits(dataLength, "u");
0770: curV = getBits(dataLength, "v");
0771:
0772: // normalize u, v, sextant, and octant
0773: curU = curU << gct.rightShift;
0774: curV = curV << gct.rightShift;
0775:
0776: curSex = (index >> 3) & 0x7;
0777: curOct = index & 0x7;
0778:
0779: if (debug) {
0780: if (curSex < 6)
0781: System.out.println(" absolute normal: sex "
0782: + curSex + " oct " + curOct + " u " + curU
0783: + " v " + curV);
0784: else
0785: System.out.println(" special normal: sex " + curSex
0786: + " oct " + curOct);
0787: }
0788: } else {
0789: //
0790: // Relative normal case. Extract du from 6-bit tag.
0791: //
0792: du = currentHeader & BMASK[6 - gct.tagLength];
0793:
0794: if (gct.tagLength + dataLength < 6) {
0795: // normalize du, get dv
0796: du = du >> (6 - gct.tagLength - dataLength);
0797: dv = currentHeader
0798: & BMASK[6 - gct.tagLength - dataLength];
0799:
0800: if (gct.tagLength + 2 * dataLength < 6) {
0801: // normalize dv
0802: dv = dv >> (6 - gct.tagLength - 2 * dataLength);
0803: } else if (gct.tagLength + 2 * dataLength > 6) {
0804: // read in rest of dv and normalize it
0805: ii = getBits(dataLength
0806: - (6 - gct.tagLength - dataLength), "dv");
0807: dv = (dv << (dataLength - (6 - gct.tagLength - dataLength)))
0808: | ii;
0809: }
0810: } else if (gct.tagLength + dataLength > 6) {
0811: // read in rest of du and normalize it
0812: ii = getBits(dataLength - (6 - gct.tagLength), "du");
0813: du = (du << (dataLength - (6 - gct.tagLength))) | ii;
0814: // read in dv
0815: dv = getBits(dataLength, "dv");
0816: } else {
0817: // read in dv
0818: dv = getBits(dataLength, "dv");
0819: }
0820:
0821: // Sign extend delta uv components.
0822: du = du << (32 - dataLength);
0823: du = du >> (32 - dataLength);
0824: dv = dv << (32 - dataLength);
0825: dv = dv >> (32 - dataLength);
0826:
0827: // normalize values
0828: du = du << gct.rightShift;
0829: dv = dv << gct.rightShift;
0830:
0831: // un-delta
0832: curU += du;
0833: curV += dv;
0834:
0835: if (debug)
0836: System.out.println(" delta normal: du " + du + " dv "
0837: + dv);
0838:
0839: //
0840: // Check for normal wrap.
0841: //
0842: if (!((curU >= 0) && (curV >= 0) && (curU + curV <= 64)))
0843: if ((curU < 0) && (curV >= 0)) {
0844: // wrap on u, same octant, different sextant
0845: curU = -curU;
0846: switch (curSex) {
0847: case 0:
0848: curSex = 4;
0849: break;
0850: case 1:
0851: curSex = 5;
0852: break;
0853: case 2:
0854: curSex = 3;
0855: break;
0856: case 3:
0857: curSex = 2;
0858: break;
0859: case 4:
0860: curSex = 0;
0861: break;
0862: case 5:
0863: curSex = 1;
0864: break;
0865: }
0866: } else if ((curU >= 0) && (curV < 0)) {
0867: // wrap on v, same sextant, different octant
0868: curV = -curV;
0869: switch (curSex) {
0870: case 1:
0871: case 5:
0872: curOct = curOct ^ 4; // invert x axis
0873: break;
0874: case 0:
0875: case 4:
0876: curOct = curOct ^ 2; // invert y axis
0877: break;
0878: case 2:
0879: case 3:
0880: curOct = curOct ^ 1; // invert z axis
0881: break;
0882: }
0883: } else if (curU + curV > 64) {
0884: // wrap on uv, same octant, different sextant
0885: curU = 64 - curU;
0886: curV = 64 - curV;
0887: switch (curSex) {
0888: case 0:
0889: curSex = 2;
0890: break;
0891: case 1:
0892: curSex = 3;
0893: break;
0894: case 2:
0895: curSex = 0;
0896: break;
0897: case 3:
0898: curSex = 1;
0899: break;
0900: case 4:
0901: curSex = 5;
0902: break;
0903: case 5:
0904: curSex = 4;
0905: break;
0906: }
0907: } else {
0908: throw new IllegalArgumentException(J3dUtilsI18N
0909: .getString("GeometryDecompressor1"));
0910: }
0911: }
0912:
0913: // do optional mesh buffer push
0914: if (mbp != 0) {
0915: if (debug)
0916: System.out
0917: .println(" pushing normal into mesh buffer at "
0918: + meshIndex);
0919:
0920: meshBuffer[meshIndex].sextant = (short) curSex;
0921: meshBuffer[meshIndex].octant = (short) curOct;
0922: meshBuffer[meshIndex].u = (short) curU;
0923: meshBuffer[meshIndex].v = (short) curV;
0924: }
0925:
0926: // convert normal back to [-1..1] floating point
0927: indexNormal(curSex, curOct, curU, curV, curNorm);
0928:
0929: // a set normal opcode when normals aren't bundled with the vertices
0930: // is a global normal change.
0931: if (!bundlingNorm)
0932: outputNormal(curNorm);
0933: }
0934:
0935: //
0936: // Get the floating point normal from its sextant, octant, u, and v.
0937: //
0938: private void indexNormal(int sex, int oct, int u, int v, Vector3f n) {
0939: float nx, ny, nz, t;
0940:
0941: if (debug)
0942: System.out.println(" sextant " + sex + " octant " + oct
0943: + " u " + u + " v " + v);
0944: if (sex > 5) {
0945: // special normals
0946: switch (oct & 0x1) {
0947: case 0: // six coordinate axes
0948: switch (((sex & 0x1) << 1) | ((oct & 0x4) >> 2)) {
0949: case 0:
0950: nx = 1.0f;
0951: ny = nz = 0.0f;
0952: break;
0953: case 1:
0954: ny = 1.0f;
0955: nx = nz = 0.0f;
0956: break;
0957: default:
0958: case 2:
0959: nz = 1.0f;
0960: nx = ny = 0.0f;
0961: break;
0962: }
0963: sex = 0;
0964: oct = (oct & 0x2) >> 1;
0965: oct = (oct << 2) | (oct << 1) | oct;
0966: break;
0967: case 1: // eight mid
0968: default:
0969: oct = ((sex & 0x1) << 2) | (oct >> 1);
0970: sex = 0;
0971: nx = ny = nz = (float) (1.0 / Math.sqrt(3.0));
0972: break;
0973: }
0974: if ((oct & 0x1) != 0)
0975: nz = -nz;
0976: if ((oct & 0x2) != 0)
0977: ny = -ny;
0978: if ((oct & 0x4) != 0)
0979: nx = -nx;
0980:
0981: } else {
0982: // regular normals
0983: nx = (float) gcNormals[v][u][0];
0984: ny = (float) gcNormals[v][u][1];
0985: nz = (float) gcNormals[v][u][2];
0986:
0987: // reverse the swap
0988: if ((sex & 0x4) != 0) {
0989: t = nx;
0990: nx = nz;
0991: nz = t;
0992: }
0993: if ((sex & 0x2) != 0) {
0994: t = ny;
0995: ny = nz;
0996: nz = t;
0997: }
0998: if ((sex & 0x1) != 0) {
0999: t = nx;
1000: nx = ny;
1001: ny = t;
1002: }
1003:
1004: // reverse the sign flip
1005: if ((oct & 0x1) != 0)
1006: nz = -nz;
1007: if ((oct & 0x2) != 0)
1008: ny = -ny;
1009: if ((oct & 0x4) != 0)
1010: nx = -nx;
1011: }
1012:
1013: // return resulting normal
1014: n.set(nx, ny, nz);
1015: if (debug)
1016: System.out.println(" result normal: " + nx + " " + ny + " "
1017: + nz);
1018: }
1019:
1020: //
1021: // Process a set current color command.
1022: //
1023: private void processSetColor(int mbp) {
1024: HuffmanTableEntry gct;
1025: short dr, dg, db, da;
1026: float fR, fG, fB, fA;
1027: int r, g, b, a, index, dataLength;
1028: int ii;
1029:
1030: // If the next command is a mesh buffer reference, use this color.
1031: meshState &= ~USE_MESH_COLOR;
1032:
1033: // Get the huffman table entry.
1034: gct = gctables[1][currentHeader & 0x3F];
1035:
1036: if (debug)
1037: System.out.println("GeometryDecompressor.processSetColor\n"
1038: + gct.toString());
1039:
1040: // Get the true length of the data.
1041: dataLength = gct.dataLength - gct.rightShift;
1042:
1043: // Read in red, green, blue, and possibly alpha.
1044: r = currentHeader & BMASK[6 - gct.tagLength];
1045: a = 0;
1046:
1047: if (gct.tagLength + dataLength == 6) {
1048: g = getBits(dataLength, "g");
1049: b = getBits(dataLength, "b");
1050: if (doingAlpha)
1051: a = getBits(dataLength, "a");
1052: } else if (gct.tagLength + dataLength < 6) {
1053: r = r >> (6 - gct.tagLength - dataLength);
1054:
1055: g = currentHeader & BMASK[6 - gct.tagLength - dataLength];
1056: if (gct.tagLength + 2 * dataLength == 6) {
1057: b = getBits(dataLength, "b");
1058: if (doingAlpha)
1059: a = getBits(dataLength, "a");
1060: } else if (gct.tagLength + 2 * dataLength < 6) {
1061: g = g >> (6 - gct.tagLength - 2 * dataLength);
1062:
1063: b = currentHeader
1064: & BMASK[6 - gct.tagLength - 2 * dataLength];
1065: if (gct.tagLength + 3 * dataLength == 6) {
1066: if (doingAlpha)
1067: a = getBits(dataLength, "a");
1068: } else if (gct.tagLength + 3 * dataLength < 6) {
1069: b = b >> (6 - gct.tagLength - 3 * dataLength);
1070:
1071: if (doingAlpha) {
1072: a = currentHeader
1073: & BMASK[6 - gct.tagLength - 4
1074: * dataLength];
1075: if (gct.tagLength + 4 * dataLength < 6) {
1076: a = a >> (6 - gct.tagLength - 3 * dataLength);
1077: } else if (gct.tagLength + 4 * dataLength > 6) {
1078: ii = getBits(
1079: dataLength
1080: - (6 - gct.tagLength - 3 * dataLength),
1081: "a");
1082: a = (a << (dataLength - (6 - gct.tagLength - 3 * dataLength)))
1083: | ii;
1084: }
1085: }
1086: } else {
1087: ii = getBits(dataLength
1088: - (6 - gct.tagLength - 2 * dataLength), "b");
1089: b = (b << (dataLength - (6 - gct.tagLength - 2 * dataLength)))
1090: | ii;
1091: if (doingAlpha)
1092: a = getBits(dataLength, "a");
1093: }
1094: } else {
1095: ii = getBits(dataLength
1096: - (6 - gct.tagLength - dataLength), "g");
1097: g = (g << (dataLength - (6 - gct.tagLength - dataLength)))
1098: | ii;
1099: b = getBits(dataLength, "b");
1100: if (doingAlpha)
1101: a = getBits(dataLength, "a");
1102: }
1103: } else {
1104: ii = getBits(dataLength - (6 - gct.tagLength), "r");
1105: r = (r << (dataLength - (6 - gct.tagLength))) | ii;
1106: g = getBits(dataLength, "g");
1107: b = getBits(dataLength, "b");
1108: if (doingAlpha)
1109: a = getBits(dataLength, "a");
1110: }
1111:
1112: // Sign extend delta x y z components.
1113: r <<= (32 - dataLength);
1114: r >>= (32 - dataLength);
1115: g <<= (32 - dataLength);
1116: g >>= (32 - dataLength);
1117: b <<= (32 - dataLength);
1118: b >>= (32 - dataLength);
1119: a <<= (32 - dataLength);
1120: a >>= (32 - dataLength);
1121:
1122: // Normalize values.
1123: dr = (short) (r << gct.rightShift);
1124: dg = (short) (g << gct.rightShift);
1125: db = (short) (b << gct.rightShift);
1126: da = (short) (a << gct.rightShift);
1127:
1128: // Update current position, first adding deltas if in relative mode.
1129: if (gct.absolute != 0) {
1130: curR = dr;
1131: curG = dg;
1132: curB = db;
1133: if (doingAlpha)
1134: curA = da;
1135: if (debug)
1136: System.out.println(" absolute color: r " + curR + " g "
1137: + curG + " b " + curB + " a " + curA);
1138: } else {
1139: curR += dr;
1140: curG += dg;
1141: curB += db;
1142: if (doingAlpha)
1143: curA += da;
1144: if (debug)
1145: System.out.println(" delta color: dr " + dr + " dg "
1146: + dg + " db " + db + " da " + da);
1147: }
1148:
1149: // Do optional mesh buffer push.
1150: if (mbp != 0) {
1151: if (debug)
1152: System.out
1153: .println(" pushing color into mesh buffer at "
1154: + meshIndex);
1155:
1156: meshBuffer[meshIndex].r = curR;
1157: meshBuffer[meshIndex].g = curG;
1158: meshBuffer[meshIndex].b = curB;
1159: meshBuffer[meshIndex].a = curA;
1160: }
1161:
1162: // Convert point back to [-1..1] floating point.
1163: fR = curR;
1164: fR /= 32768.0;
1165: fG = curG;
1166: fG /= 32768.0;
1167: fB = curB;
1168: fB /= 32768.0;
1169: fA = curA;
1170: fA /= 32768.0;
1171:
1172: curColor.set(fR, fG, fB, fA);
1173: if (debug)
1174: System.out.println(" result color: " + fR + " " + fG + " "
1175: + fB + " " + fA);
1176:
1177: // A set color opcode when colors aren't bundled with the vertices
1178: // is a global color change.
1179: if (!bundlingColor)
1180: outputColor(curColor);
1181: }
1182:
1183: //
1184: // Process a mesh buffer reference command.
1185: //
1186: private void processMeshBR() {
1187: MeshBufferEntry entry;
1188: int index, normal;
1189: int ii;
1190:
1191: if (debug)
1192: System.out.println("GeometryDecompressor.processMeshBR");
1193:
1194: ii = getBits(1, "mbr");
1195:
1196: index = (currentHeader >>> 1) & 0xF;
1197: repCode = ((currentHeader & 0x1) << 1) | ii;
1198:
1199: // Adjust index to proper place in fifo.
1200: index = (meshIndex - index) & 0xf;
1201: if (debug)
1202: System.out.println(" using index " + index);
1203:
1204: // Get reference to mesh buffer entry.
1205: entry = meshBuffer[index];
1206: curX = entry.x;
1207: curY = entry.y;
1208: curZ = entry.z;
1209:
1210: // Convert point back to [-1..1] floating point.
1211: curPos.set(((float) curX) / 32768.0f,
1212: ((float) curY) / 32768.0f, ((float) curZ) / 32768.0f);
1213:
1214: if (debug)
1215: System.out.println(" retrieved position " + curPos.x + " "
1216: + curPos.y + " " + curPos.z + " replace code "
1217: + repCode);
1218:
1219: // Get mesh buffer normal if previous opcode was not a setNormal.
1220: if (bundlingNorm && ((meshState & USE_MESH_NORMAL) != 0)) {
1221: curSex = entry.sextant;
1222: curOct = entry.octant;
1223: curU = entry.u;
1224: curV = entry.v;
1225:
1226: // Convert normal back to -1.0 - 1.0 floating point from index.
1227: normal = (curSex << 15) | (curOct << 12) | (curU << 6)
1228: | curV;
1229:
1230: if (debug)
1231: System.out.println(" retrieving normal");
1232: indexNormal(curSex, curOct, curU, curV, curNorm);
1233: }
1234:
1235: // Get mesh buffer color if previous opcode was not a setColor.
1236: if (bundlingColor && ((meshState & USE_MESH_COLOR) != 0)) {
1237: curR = entry.r;
1238: curG = entry.g;
1239: curB = entry.b;
1240:
1241: // Convert point back to -1.0 - 1.0 floating point.
1242: curColor.x = curR;
1243: curColor.x /= 32768.0;
1244: curColor.y = curG;
1245: curColor.y /= 32768.0;
1246: curColor.z = curB;
1247: curColor.z /= 32768.0;
1248:
1249: if (doingAlpha) {
1250: curA = entry.a;
1251: curColor.w = curA;
1252: curColor.w /= 32768.0;
1253: }
1254: if (debug)
1255: System.out.println(" retrieved color " + curColor.x
1256: + " " + curColor.y + " " + curColor.z + " "
1257: + curColor.w);
1258: }
1259:
1260: // Reset meshState.
1261: meshState = 0;
1262: }
1263:
1264: // Process a end-of-stream opcode.
1265: private void processEos() {
1266: if (debug)
1267: System.out.println("GeometryDecompressor.processEos");
1268: }
1269:
1270: // Process a variable length no-op opcode.
1271: private void processVNoop() {
1272: int ii, ct;
1273: if (debug)
1274: System.out.println("GeometryDecompressor.processVNoop");
1275:
1276: ct = getBits(5, "noop count");
1277: ii = getBits(ct, "noop bits");
1278: }
1279:
1280: // Process a pass-through opcode.
1281: private void processPassThrough() {
1282: int ignore;
1283: if (debug)
1284: System.out
1285: .println("GeometryDecompressor.processPassThrough");
1286:
1287: ignore = getBits(24, "passthrough");
1288: ignore = getBits(32, "passthrough");
1289: }
1290:
1291: // Process a skip-8 opcode.
1292: private void processSkip8() {
1293: int skip;
1294: if (debug)
1295: System.out.println("GeometryDecompressor.processSkip8");
1296:
1297: skip = getBits(8, "skip8");
1298: }
1299:
1300: private void benchmarkStart(int length) {
1301: vertexCount = 0;
1302: System.out.println(" GeometryDecompressor: decompressing "
1303: + length + " bytes...");
1304: startTime = System.currentTimeMillis();
1305: }
1306:
1307: private void benchmarkPrint(int length) {
1308: float t = (System.currentTimeMillis() - startTime) / 1000.0f;
1309: System.out.println(" done in " + t + " sec." + "\n"
1310: + " decompressed " + vertexCount + " vertices at "
1311: + (vertexCount / t) + " vertices/sec\n");
1312:
1313: System.out.print(" vertex data present: coords");
1314: int floatVertexSize = 12;
1315: if (bundlingNorm) {
1316: System.out.print(" normals");
1317: floatVertexSize += 12;
1318: }
1319: if (bundlingColor) {
1320: System.out.println(" colors");
1321: floatVertexSize += 12;
1322: }
1323: if (doingAlpha) {
1324: System.out.println(" alpha");
1325: floatVertexSize += 4;
1326: }
1327: System.out.println();
1328:
1329: System.out
1330: .println(" bytes of data in generalized strip output: "
1331: + (vertexCount * floatVertexSize)
1332: + "\n"
1333: + " compression ratio: "
1334: + (length / (float) (vertexCount * floatVertexSize))
1335: + "\n");
1336: }
1337: }
|