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