Package javax.media.j3d

Source Code of javax.media.j3d.GeometryDecompressor$HuffmanTableEntry

/*
* Copyright 1998-2008 Sun Microsystems, Inc.  All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.  Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*
*/

package javax.media.j3d;
import javax.vecmath.Color4f;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;

/**
* This abstract class provides the base methods needed to create a geometry
* decompressor.  Subclasses must implement a backend to handle the output,
* consisting of a generalized triangle strip, line strip, or point array,
* along with possible global color and normal changes.
*/
abstract class GeometryDecompressor {
    private static final boolean debug = false ;
    private static final boolean benchmark = false ;

    /**
     * Compressed geometry format version supported.
     */
    static final int majorVersionNumber = 1 ;
    static final int minorVersionNumber = 0 ;
    static final int minorMinorVersionNumber = 2 ;

    /**
     * This method is called when a SetState command is encountered in the
     * decompression stream.
     *
     * @param bundlingNorm true indicates normals are bundled with vertices
     * @param bundlingColor true indicates colors are bundled with vertices
     * @param doingAlpha true indicates alpha values are bundled with vertices
     */
    abstract void outputVertexFormat(boolean bundlingNorm,
             boolean bundlingColor,
             boolean doingAlpha) ;

    /**
     * This method captures the vertex output of the decompressor.  The normal
     * or color references may be null if the corresponding data is not
     * bundled with the vertices in the compressed geometry buffer.  Alpha
     * values may be included in the color.
     *
     * @param position The coordinates of the vertex.
     * @param normal The normal bundled with the vertex.  May be null.
     * @param color The color bundled with the vertex.  May be null.
     * Alpha may be present.
     * @param vertexReplaceCode Specifies the generalized strip flag
     * that is bundled with each vertex.
     * @see GeneralizedStripFlags
     * @see CompressedGeometryHeader
     */
    abstract void outputVertex(Point3f position, Vector3f normal,
             Color4f color, int vertexReplaceCode) ;

    /**
     * This method captures the global color output of the decompressor.  It
     * is only invoked if colors are not bundled with the vertex data.  The
     * global color applies to all succeeding vertices until the next time the
     * method is invoked.
     *
     * @param color The current global color.
     */
    abstract void outputColor(Color4f color) ;

    /**
     * This method captures the global normal output of the decompressor.  It
     * is only invoked if normals are not bundled with the vertex data.  The
     * global normal applies to all succeeding vertices until the next time the
     * method is invoked.
     *
     * @param normal The current global normal.
     */
    abstract void outputNormal(Vector3f normal) ;

    // Geometry compression opcodes.
    private static final int GC_VERTEX       = 0x40 ;
    private static final int GC_SET_NORM     = 0xC0 ;
    private static final int GC_SET_COLOR    = 0x80 ;
    private static final int GC_MESH_B_R     = 0x20 ;
    private static final int GC_SET_STATE    = 0x18 ;
    private static final int GC_SET_TABLE    = 0x10 ;
    private static final int GC_PASS_THROUGH = 0x08 ;
    private static final int GC_EOS          = 0x00 ;
    private static final int GC_V_NO_OP      = 0x01 ;
    private static final int GC_SKIP_8       = 0x07 ;

    // Three 64-entry decompression tables are used: gctables[0] for
    // positions, gctables[1] for colors, and gctables[2] for normals.
    private HuffmanTableEntry gctables[][] ;

    /**
     * Decompression table entry.
     */
    static class HuffmanTableEntry {
  int tagLength, dataLength ;
  int rightShift, absolute ;

  @Override
  public String toString() {
      return
    " tag length: "  + tagLength  +
    " data length: " + dataLength +
    " shift: "       + rightShift +
    " abs/rel: "     + absolute ;
  }
    }

    // A 16-entry mesh buffer is used.
    private MeshBufferEntry meshBuffer[] ;
    private int  meshIndex = 15 ;
    private int meshState ;

    // meshState values.  These are needed to determine if colors and/or
    // normals should come from meshBuffer or from SetColor or SetNormal.
    private static final int USE_MESH_NORMAL = 0x1 ;
    private static final int USE_MESH_COLOR  = 0x2 ;

    /**
     * Mesh buffer entry containing position, normal, and color.
     */
    static class MeshBufferEntry {
  short x, y, z ;
  short octant, sextant, u, v ;
  short r, g, b, a ;
    }

    // Geometry compression state variables.
    private short curX, curY, curZ ;
    private short curR, curG, curB, curA ;
    private int  curSex, curOct, curU, curV ;

    // Current vertex data.
    private Point3f curPos ;
    private Vector3f curNorm ;
    private Color4f curColor ;
    private int repCode ;

    // Flags indicating what data is bundled with the vertex.
    private boolean bundlingNorm ;
    private boolean bundlingColor ;
    private boolean doingAlpha ;

    // Internal decompression buffering variables.
    private int currentHeader = 0 ;
    private int nextHeader = 0 ;
    private int bitBuffer = 0 ;
    private int bitBufferCount = 32 ;

    // Used for benchmarking if so configured.
    private long startTime ;
    private int vertexCount ;

    // Bit-field masks: BMASK[i] = (1<<i)-1
    private static final int BMASK[] = {
  0x0,        0x1,        0x3,        0x7,
  0xF,        0x1F,       0x3F,       0x7F,
  0xFF,       0x1FF,      0x3FF,      0x7FF,
  0xFFF,      0x1FFF,     0x3FFF,     0x7FFF,
  0xFFFF,     0x1FFFF,    0x3FFFF,    0x7FFFF,
  0xFFFFF,    0x1FFFFF,   0x3FFFFF,   0x7FFFFF,
  0xFFFFFF,   0x1FFFFFF0x3FFFFFF0x7FFFFFF,
  0xFFFFFFF0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF,
  0xFFFFFFFF,
    } ;

    // A reference to the compressed data and the current offset.
    private byte gcData[] ;
    private int gcIndex ;

    // The normals table for decoding 6-bit [u,v] spherical sextant coordinates.
    private static final double gcNormals[][][] ;
    private static final double NORMAL_MAX_Y_ANG = 0.615479709 ;
    private static final boolean printNormalTable = false ;

    /**
     * Initialize the normals table.
     */
    static {
  int i, j, inx, iny, inz ;
  double th, psi, qnx, qny, qnz ;

  gcNormals = new double[65][65][3] ;

  for (i = 0 ; i < 65 ; i++) {
      for (j = 0 ; j < 65 ; j++) {
    if (i+j > 64) continue ;

    psi = NORMAL_MAX_Y_ANG * (i / 64.0) ;
    th = Math.asin(Math.tan(NORMAL_MAX_Y_ANG * ((64-j)/64.0))) ;

    qnx = Math.cos(th) * Math.cos(psi) ;
    qny = Math.sin(psi) ;
    qnz = Math.sin(th) * Math.cos(psi) ;

    //  Convert the floating point normal to s1.14 bit notation,
    //  then back again.
    qnx = qnx*16384.0 ; inx = (int)qnx ;
    qnx = (double)inx ; qnx = qnx/16384.0 ;

    qny = qny*16384.0 ; iny = (int)qny ;
    qny = (double)iny ; qny = qny/16384.0 ;

    qnz = qnz*16384.0 ; inz = (int)qnz ;
    qnz = (double)inz ; qnz = qnz/16384.0 ;

    gcNormals[i][j][0] = qnx ;
    gcNormals[i][j][1] = qny ;
    gcNormals[i][j][2] = qnz ;
      }
  }

  if (printNormalTable) {
      System.err.println("struct {") ;
      System.err.println("    double nx, ny, nz ;") ;
      System.err.println("} gcNormals[65][65] = {");
      for (i = 0 ; i <= 64 ; i++) {
    System.err.println("{") ;
    for (j = 0 ; j <= 64 ; j++) {
        if (j+i > 64) continue ;
        System.err.println("{ " + gcNormals[i][j][0] +
               ", " + gcNormals[i][j][1] +
               ", " + gcNormals[i][j][2] + " }") ;
    }
    System.err.println("},") ;
      }
      System.err.println("}") ;
  }
    }

    //
    // The constructor.
    //
    GeometryDecompressor() {
  curPos = new Point3f() ;
  curNorm = new Vector3f() ;
  curColor = new Color4f() ;
  gctables = new HuffmanTableEntry[3][64] ;

  for (int i = 0 ; i < 64 ; i++) {
      gctables[0][i] = new HuffmanTableEntry() ;
      gctables[1][i] = new HuffmanTableEntry() ;
      gctables[2][i] = new HuffmanTableEntry() ;
  }

  meshBuffer = new MeshBufferEntry[16] ;
  for (int i = 0 ; i < 16 ; i++)
      meshBuffer[i] = new MeshBufferEntry() ;
    }

    /**
     * Check version numbers and return true if compatible.
     */
    boolean checkVersion(int majorVersionNumber, int minorVersionNumber) {
  return ((majorVersionNumber < this.majorVersionNumber) ||
    ((majorVersionNumber == this.majorVersionNumber) &&
     (minorVersionNumber <= this.minorVersionNumber))) ;
    }

    /**
     * Decompress data and invoke abstract output methods.
     *
     * @param start byte offset to start of compressed geometry in data array
     * @param length size of compressed geometry in bytes
     * @param data array containing compressed geometry buffer of the
     * specified length at the given offset from the start of the array
     * @exception ArrayIndexOutOfBoundsException if start+length > data size
     */
    void decompress(int start, int length, byte data[]) {
  if (debug)
      System.err.println("GeometryDecompressor.decompress\n" +
             " start: " + start +
             " length: " + length +
             " data array size: " + data.length) ;
  if (benchmark)
      benchmarkStart(length) ;

  if (start+length > data.length)
      throw new ArrayIndexOutOfBoundsException
    (J3dI18N.getString("GeometryDecompressor0")) ;

  // Set reference to compressed data and skip to start of data.
  gcData = data ;
  gcIndex = start ;

  // Initialize state.
  bitBufferCount = 0 ;
  meshState = 0 ;
  bundlingNorm = false ;
  bundlingColor = false ;
  doingAlpha = false ;
  repCode = 0 ;

  // Headers are interleaved for hardware implementations, so the
  // first is always a nullop.
  nextHeader = GC_V_NO_OP ;

  // Enter decompression loop.
  while (gcIndex < start+length)
      processDecompression() ;

  // Finish out any bits left in bitBuffer.
  while (bitBufferCount > 0)
      processDecompression() ;

  if (benchmark)
      benchmarkPrint(length) ;
    }

    //
    // Return the next bitCount bits of compressed data.
    //
    private int getBits(int bitCount, String d) {
  int bits ;

  if (debug)
      System.err.print(" getBits(" + bitCount + ") " + d + ", " +
           bitBufferCount + " available at gcIndex " +
           gcIndex) ;

  if (bitCount == 0) {
      if (debug) System.err.println(": got 0x0") ;
      return 0 ;
  }

  if (bitBufferCount == 0) {
      bitBuffer = (((gcData[gcIndex++] & 0xff) << 24) |
       ((gcData[gcIndex++] & 0xff) << 16) |
       ((gcData[gcIndex++] & 0xff) <<  8) |
       ((gcData[gcIndex++] & 0xff))) ;

      bitBufferCount = 32 ;
  }

  if (bitBufferCount >= bitCount) {
      bits = (bitBuffer >>> (32 - bitCount)) & BMASK[bitCount] ;
      bitBuffer = bitBuffer << bitCount ;
      bitBufferCount -= bitCount ;
  } else {
      bits = (bitBuffer >>> (32 - bitCount)) & BMASK[bitCount] ;
      bits = bits >>> (bitCount - bitBufferCount) ;
      bits = bits  << (bitCount - bitBufferCount) ;

      bitBuffer = (((gcData[gcIndex++] & 0xff) << 24) |
       ((gcData[gcIndex++] & 0xff) << 16) |
       ((gcData[gcIndex++] & 0xff) <<  8) |
       ((gcData[gcIndex++] & 0xff))) ;

      bits = bits |
    ((bitBuffer >>> (32 - (bitCount - bitBufferCount))) &
     BMASK[bitCount - bitBufferCount]) ;

      bitBuffer = bitBuffer << (bitCount - bitBufferCount) ;
      bitBufferCount = 32 - (bitCount - bitBufferCount) ;
  }

  if (debug)
      System.err.println(": got 0x" + Integer.toHexString(bits)) ;

  return bits ;
    }

    //
    // Shuffle interleaved headers and opcodes.
    //
    private void processDecompression() {
  int mbp ;
  currentHeader = nextHeader ;

  if ((currentHeader & 0xC0) == GC_VERTEX) {
      // Process a vertex.
      if (!bundlingNorm && !bundlingColor) {
    // get next opcode, process current position opcode
    nextHeader = getBits(8, "header") ;
    mbp = processDecompressionOpcode(0) ;

      } else if (bundlingNorm && !bundlingColor) {
    // get normal header, process current position opcode
    nextHeader = getBits(6, "normal") ;
    mbp = processDecompressionOpcode(0) ;
    currentHeader = nextHeader | GC_SET_NORM ;

    // get next opcode, process current normal opcode
    nextHeader = getBits(8, "header") ;
    processDecompressionOpcode(mbp) ;

      } else if (!bundlingNorm && bundlingColor) {
    // get color header, process current position opcode
    nextHeader = getBits(6, "color") ;
    mbp = processDecompressionOpcode(0) ;
    currentHeader = nextHeader | GC_SET_COLOR ;

    // get next opcode, process current color opcode
    nextHeader = getBits(8, "header") ;
    processDecompressionOpcode(mbp) ;

      } else {
    // get normal header, process current position opcode
    nextHeader = getBits(6, "normal") ;
    mbp = processDecompressionOpcode(0) ;
    currentHeader = nextHeader | GC_SET_NORM ;

    // get color header, process current normal opcode
    nextHeader = getBits(6, "color") ;
    processDecompressionOpcode(mbp) ;
    currentHeader = nextHeader | GC_SET_COLOR ;

    // get next opcode, process current color opcode
    nextHeader = getBits(8, "header") ;
    processDecompressionOpcode(mbp) ;
      }

      // Send out the complete vertex.
      outputVertex(curPos, curNorm, curColor, repCode) ;
      if (benchmark) vertexCount++ ;

      // meshState bits get turned off in the setColor and setNormal
      // routines in order to keep track of what data a mesh buffer
      // reference should use.
      meshState |= USE_MESH_NORMAL ;
      meshState |= USE_MESH_COLOR ;

  } else {
      // Non-vertex case: get next opcode, then process current opcode.
      nextHeader = getBits(8, "header") ;
      processDecompressionOpcode(0) ;
  }
    }

    //
    // Decode the opcode in currentHeader, and dispatch to the appropriate
    // processing method.
    //
    private int processDecompressionOpcode(int mbp) {
  if ((currentHeader & 0xC0) == GC_SET_NORM)
      processSetNormal(mbp) ;
  else if ((currentHeader & 0xC0) == GC_SET_COLOR)
      processSetColor(mbp) ;
  else if ((currentHeader & 0xC0) == GC_VERTEX)
      // Return the state of the mesh buffer push bit
      // when processing a vertex.
      return processVertex() ;
  else if ((currentHeader & 0xE0) == GC_MESH_B_R) {
      processMeshBR() ;

      // Send out the complete vertex.
      outputVertex(curPos, curNorm, curColor, repCode) ;
      if (benchmark) vertexCount++ ;

      // meshState bits get turned off in the setColor and setNormal
      // routines in order to keep track of what data a mesh buffer
      // reference should use.
      meshState |= USE_MESH_NORMAL ;
      meshState |= USE_MESH_COLOR ;
  }
  else if ((currentHeader & 0xF8) == GC_SET_STATE)
      processSetState() ;
  else if ((currentHeader & 0xF8) == GC_SET_TABLE)
      processSetTable() ;
  else if ((currentHeader & 0xFF) == GC_EOS)
      processEos() ;
  else if ((currentHeader & 0xFF) == GC_V_NO_OP)
      processVNoop() ;
  else if ((currentHeader & 0xFF) == GC_PASS_THROUGH)
      processPassThrough() ;
  else if ((currentHeader & 0xFF) == GC_SKIP_8)
      processSkip8() ;

  return 0 ;
    }

    //
    //  Process a set state opcode.
    //
    private void processSetState() {
  int ii ;
  if (debug)
      System.err.println("GeometryDecompressor.processSetState") ;

  ii = getBits(3, "bundling") ;

  bundlingNorm  = ((currentHeader & 0x1) != 0) ;
  bundlingColor = (((ii >>> 2) & 0x1) != 0) ;
  doingAlpha    = (((ii >>> 1) & 0x1) != 0) ;

  if (debug)
      System.err.println(" bundling normal: " + bundlingNorm  +
             " bundling color: "  + bundlingColor +
             " alpha present: "   + doingAlpha) ;

  // Call the abstract output implementation.
  outputVertexFormat(bundlingNorm, bundlingColor, doingAlpha) ;
    }

    //
    // Process a set decompression table opcode.
    //
    // Extract the parameters of the table set command,
    // and set the approprate table entries.
    //
    private void processSetTable() {
  HuffmanTableEntry gct[] ;
  int i, adr, tagLength, dataLength, rightShift, absolute ;
  int ii, index ;

  if (debug)
      System.err.println("GeometryDecompressor.processSetTable") ;

  // Get reference to approprate 64 entry table.
  index = (currentHeader & 0x6) >>> 1 ;
  gct = gctables[index] ;

  // Get the remaining bits of the set table command.
  ii = getBits(15, "set table") ;

  // Extract the individual fields from the two bit strings.
  adr = ((currentHeader & 0x1) << 6) | ((ii >>> 9) & 0x3F) ;

  // Get data length.  For positions and colors, 0 really means 16, as 0
  // lengths are meaningless for them.  Normal components are allowed to
  // have lengths of 0.
  dataLength = (ii >>> 5) & 0x0F ;
  if (dataLength == 0 && index != 2)
      dataLength = 16 ;

  rightShift = ii & 0x0F ;
  absolute = (ii >>> 4) & 0x1 ;

  //
  // Decode the tag length from the address field by finding the
  // first set 1 from the left in the bitfield.
  //
  for (tagLength = 6 ; tagLength > 0 ; tagLength--) {
      if ((adr >> tagLength) != 0) break ;
  }

  // Shift the address bits up into place, and off the leading 1.
  adr = (adr << (6 - tagLength)) & 0x3F ;

  if (debug)
      System.err.println(" table " + ((currentHeader & 0x6) >>> 1) +
             " address "     + adr +
             " tag length "  + tagLength +
             " data length " + dataLength +
             " shift "       + rightShift +
             " absolute "    + absolute) ;

  // Fill in the table fields with the specified values.
  for (i = 0 ; i < (1 << (6 - tagLength)) ; i++) {
      gct[adr+i].tagLength = tagLength ;
      gct[adr+i].dataLength = dataLength ;
      gct[adr+i].rightShift = rightShift ;
      gct[adr+i].absolute = absolute ;
  }
    }


    //
    // Process a vertex opcode.  Any bundled normal and/or color will be
    // processed by separate methods.  Return the mesh buffer push indicator.
    //
    private int processVertex() {
  HuffmanTableEntry gct ;
  float fX, fY, fZ ;
  short dx, dy, dz ;
  int mbp, x, y, z, dataLen ;
  int ii ;

  // If the next command is a mesh buffer reference
  // then use colors and normals from the mesh buffer.
  meshState = 0 ;

  // Get a reference to the approprate tag table entry.
  gct = gctables[0][currentHeader & 0x3F] ;

  if (debug) System.err.println("GeometryDecompressor.processVertex\n" +
              gct.toString()) ;

  // Get the true length of the data.
  dataLen = gct.dataLength - gct.rightShift ;

  // Read in the replace code and mesh buffer push bits,
  // if they're not in the current header.
  if (6 - (3 * dataLen) - gct.tagLength > 0) {
      int numBits = 6 - (3 * dataLen) - gct.tagLength ;
      int jj ;

      jj = currentHeader & BMASK[numBits] ;
      ii = getBits(3 - numBits, "repcode/mbp") ;
      ii |= (jj << (3 - numBits)) ;
      }
  else
      ii = getBits(3, "repcode/mbp") ;

  repCode = ii >>> 1 ;
  mbp = ii & 0x1 ;

  // Read in x, y, and z components.
  x = currentHeader & BMASK[6-gct.tagLength] ;

  if (gct.tagLength + dataLen == 6) {
      y = getBits(dataLen, "y") ;
      z = getBits(dataLen, "z") ;
  } else if (gct.tagLength + dataLen <  6) {
      x = x >> (6 - gct.tagLength - dataLen) ;

      y = currentHeader & BMASK[6 - gct.tagLength - dataLen] ;
      if (gct.tagLength + 2*dataLen == 6) {
    z = getBits(dataLen, "z") ;
      } else if (gct.tagLength + 2*dataLen <  6) {
    y = y >> (6 - gct.tagLength - 2*dataLen) ;

    z = currentHeader & BMASK[6 - gct.tagLength - 2*dataLen] ;
    if (gct.tagLength + 3*dataLen <  6) {
        z = z >> (6 - gct.tagLength - 3*dataLen) ;
    } else if (gct.tagLength + 3*dataLen >  6) {
        ii = getBits(dataLen - (6 - gct.tagLength - 2*dataLen),
         "z") ;
        z = (z << (dataLen - (6 - gct.tagLength - 2*dataLen)))
      | ii ;
    }
      } else {
    ii = getBits(dataLen - (6 - gct.tagLength - dataLen), "y") ;
    y = (y << (dataLen - (6 - gct.tagLength - dataLen))) | ii ;
    z = getBits(dataLen, "z") ;
      }
  } else {
      ii = getBits(dataLen - (6 - gct.tagLength), "x") ;
      x = (x << (dataLen - (6 - gct.tagLength))) | ii ;
      y = getBits(dataLen, "y") ;
      z = getBits(dataLen, "z") ;
  }

  // Sign extend delta x y z components.
  x = x << (32 - dataLen) ; x = x >> (32 - dataLen) ;
  y = y << (32 - dataLen) ; y = y >> (32 - dataLen) ;
  z = z << (32 - dataLen) ; z = z >> (32 - dataLen) ;

  // Normalize values.
  dx = (short)(x << gct.rightShift) ;
  dy = (short)(y << gct.rightShift) ;
  dz = (short)(z << gct.rightShift) ;

  // Update current position, first adding deltas if in relative mode.
  if (gct.absolute != 0) {
      curX = dx ; curY = dy ; curZ = dz ;
      if (debug) System.err.println(" absolute position: " +
            curX + " " + curY + " " + curZ) ;
  } else {
      curX += dx ; curY += dy ; curZ += dz ;
      if (debug) System.err.println(" delta position: " +
            dx + " " + dy + " " + dz) ;
  }

  // Do optional mesh buffer push.
  if (mbp != 0) {
      // Increment to next position (meshIndex is initialized to 15).
      meshIndex = (meshIndex + 1) & 0xF ;
      meshBuffer[meshIndex].x = curX ;
      meshBuffer[meshIndex].y = curY ;
      meshBuffer[meshIndex].z = curZ ;
      if (debug)
    System.err.println(" pushed position into mesh buffer at " +
           meshIndex) ;
  }

  // Convert point back to [-1..1] floating point.
  fX = curX ; fX /= 32768.0 ;
  fY = curY ; fY /= 32768.0 ;
  fZ = curZ ; fZ /= 32768.0 ;
  if (debug)
      System.err.println(" result position " + fX + " " + fY + " " + fZ) ;

  curPos.set(fX, fY, fZ) ;
  return mbp ;
    }


    //
    // Process a set current normal opcode.
    //
    private void processSetNormal(int mbp) {
  HuffmanTableEntry gct ;
  int index, du, dv, n, dataLength ;
  int ii ;

  // if next command is a mesh buffer reference, use this normal
  meshState &= ~USE_MESH_NORMAL ;

  // use table 2 for normals
  gct = gctables[2][currentHeader & 0x3F] ;

  if (debug)
      System.err.println("GeometryDecompressor.processSetNormal\n" +
             gct.toString()) ;

  // subtract up-shift amount to get true data (u, v) length
  dataLength = gct.dataLength - gct.rightShift ;

  if (gct.absolute != 0) {
      //
      // Absolute normal case.  Extract index from 6-bit tag.
      //
      index = currentHeader & BMASK[6-gct.tagLength] ;

      if (gct.tagLength != 0) {
    // read in the rest of the 6-bit sex/oct pair (index)
    ii = getBits(6 - (6 - gct.tagLength), "sex/oct") ;
    index = (index << (6 - (6 - gct.tagLength))) | ii ;
      }

      // read in u and v data
      curU = getBits(dataLength, "u") ;
      curV = getBits(dataLength, "v") ;

      // normalize u, v, sextant, and octant
      curU = curU << gct.rightShift ;
      curV = curV << gct.rightShift ;

      curSex = (index >> 3) & 0x7 ;
      curOct = index & 0x7 ;

      if (debug) {
    if (curSex < 6)
        System.err.println(" absolute normal: sex " + curSex +
               " oct " + curOct +
               " u "   + curU   + " v " + curV) ;
    else
        System.err.println(" special normal: sex " + curSex +
               " oct " + curOct) ;
      }
  } else {
      //
      // Relative normal case.  Extract du from 6-bit tag.
      //
      du = currentHeader & BMASK[6-gct.tagLength] ;

      if (gct.tagLength + dataLength < 6) {
    // normalize du, get dv
    du = du >> (6 - gct.tagLength - dataLength) ;
    dv = currentHeader & BMASK[6 - gct.tagLength - dataLength] ;

    if (gct.tagLength + 2*dataLength <  6) {
        // normalize dv
        dv = dv >> (6 - gct.tagLength - 2*dataLength) ;
    } else if (gct.tagLength + 2*dataLength >  6) {
        // read in rest of dv and normalize it
        ii = getBits(dataLength -
         (6 - gct.tagLength - dataLength), "dv") ;
        dv = (dv << (dataLength -
         (6 - gct.tagLength - dataLength))) | ii ;
    }
      } else if (gct.tagLength + dataLength > 6) {
    // read in rest of du and normalize it
    ii = getBits(dataLength - (6 - gct.tagLength), "du") ;
    du = (du << (dataLength - (6 - gct.tagLength))) | ii ;
    // read in dv
    dv = getBits(dataLength, "dv") ;
      } else {
    // read in dv
    dv = getBits(dataLength, "dv") ;
      }

      // Sign extend delta uv components.
      du = du << (32 - dataLength) ; du = du >> (32 - dataLength) ;
      dv = dv << (32 - dataLength) ; dv = dv >> (32 - dataLength) ;

      // normalize values
      du = du << gct.rightShift ;
      dv = dv << gct.rightShift ;

      // un-delta
      curU += du ;
      curV += dv ;

      if (debug)
    System.err.println(" delta normal: du " + du + " dv " + dv) ;

      //
      // Check for normal wrap.
      //
      if (! ((curU >=  0) && (curV >= 0) && (curU + curV <= 64)))
    if ((curU < 0) && (curV >= 0)) {
        // wrap on u, same octant, different sextant
        curU = -curU ;
        switch (curSex) {
        case 0: curSex = 4 ; break ;
        case 1: curSex = 5 ; break ;
        case 2: curSex = 3 ; break ;
        case 3: curSex = 2 ; break ;
        case 4: curSex = 0 ; break ;
        case 5: curSex = 1 ; break ;
        }
    } else if ((curU >= 0) && (curV <  0)) {
        // wrap on v, same sextant, different octant
        curV = -curV ;
        switch (curSex) {
        case 1: case 5:
      curOct = curOct ^ 4 // invert x axis
      break ;
        case 0: case 4:
      curOct = curOct ^ 2 // invert y axis
      break ;
        case 2: case 3:
      curOct = curOct ^ 1 // invert z axis
      break ;
        }
    } else if (curU + curV > 64) {
        // wrap on uv, same octant, different sextant
        curU = 64 - curU ;
        curV = 64 - curV ;
        switch (curSex) {
        case 0: curSex = 2 ; break ;
        case 1: curSex = 3 ; break ;
        case 2: curSex = 0 ; break ;
        case 3: curSex = 1 ; break ;
        case 4: curSex = 5 ; break ;
        case 5: curSex = 4 ; break ;
        }
    } else {
        throw new IllegalArgumentException
      (J3dI18N.getString("GeometryDecompressor1")) ;
    }
  }

  // do optional mesh buffer push
  if (mbp != 0) {
      if (debug)
    System.err.println(" pushing normal into mesh buffer at " +
           meshIndex) ;

      meshBuffer[meshIndex].sextant = (short)curSex ;
      meshBuffer[meshIndex].octant = (short)curOct ;
      meshBuffer[meshIndex].u = (short)curU ;
      meshBuffer[meshIndex].v = (short)curV ;
  }

  // convert normal back to [-1..1] floating point
  indexNormal(curSex, curOct, curU, curV, curNorm) ;

  // a set normal opcode when normals aren't bundled with the vertices
  // is a global normal change.
  if (! bundlingNorm) outputNormal(curNorm) ;
    }


    //
    // Get the floating point normal from its sextant, octant, u, and v.
    //
    private void indexNormal(int sex, int oct, int u, int v, Vector3f n) {
  float nx, ny, nz, t ;

  if (debug) System.err.println(" sextant " + sex + " octant " + oct  +
              " u " + u + " v " + v) ;
  if (sex > 5) {
      // special normals
      switch (oct & 0x1) {
        case 0: // six coordinate axes
    switch (((sex & 0x1) << 1) | ((oct & 0x4) >> 2)) {
      case 0: nx = 1.0f ; ny = nz = 0.0f ; break ;
      case 1: ny = 1.0f ; nx = nz = 0.0f ; break ;
      default:
      case 2: nz = 1.0f ; nx = ny = 0.0f ; break ;
    }
    sex = 0 ; oct = (oct & 0x2) >> 1 ;
    oct = (oct << 2) | (oct << 1) | oct ;
    break ;
        case 1: // eight mid
        default:
    oct = ((sex & 0x1) << 2) | (oct >> 1) ;
    sex = 0 ;
    nx = ny = nz = (float)(1.0/Math.sqrt(3.0)) ;
    break ;
      }
      if ((oct & 0x1) != 0) nz = -nz ;
      if ((oct & 0x2) != 0) ny = -ny ;
      if ((oct & 0x4) != 0) nx = -nx ;

  } else {
      // regular normals
      nx = (float)gcNormals[v][u][0] ;
      ny = (float)gcNormals[v][u][1] ;
      nz = (float)gcNormals[v][u][2] ;

      // reverse the swap
      if ((sex & 0x4) != 0) { t = nx ; nx = nz ; nz = t ; }
      if ((sex & 0x2) != 0) { t = ny ; ny = nz ; nz = t ; }
      if ((sex & 0x1) != 0) { t = nx ; nx = ny ; ny = t ; }

      // reverse the sign flip
      if ((oct & 0x1) != 0) nz = -nz ;
      if ((oct & 0x2) != 0) ny = -ny ;
      if ((oct & 0x4) != 0) nx = -nx ;
  }

  // return resulting normal
  n.set(nx, ny, nz) ;
  if (debug)
      System.err.println(" result normal: " + nx + " " + ny + " " + nz) ;
    }


    //
    // Process a set current color command.
    //
    private void processSetColor(int mbp) {
  HuffmanTableEntry gct ;
  short dr, dg, db, da ;
  float fR, fG, fB, fA ;
  int r, g, b, a, index, dataLength ;
  int ii ;

  // If the next command is a mesh buffer reference, use this color.
  meshState &= ~USE_MESH_COLOR ;

  // Get the huffman table entry.
  gct = gctables[1][currentHeader & 0x3F] ;

  if (debug)
      System.err.println("GeometryDecompressor.processSetColor\n" +
             gct.toString()) ;

  // Get the true length of the data.
  dataLength = gct.dataLength - gct.rightShift ;

  // Read in red, green, blue, and possibly alpha.
  r = currentHeader & BMASK[6 - gct.tagLength] ;
  a = 0 ;

  if (gct.tagLength + dataLength == 6) {
      g = getBits(dataLength, "g") ;
      b = getBits(dataLength, "b") ;
      if (doingAlpha)
    a = getBits(dataLength, "a") ;
  }
  else if (gct.tagLength + dataLength <  6) {
      r = r >> (6 - gct.tagLength - dataLength) ;

      g = currentHeader & BMASK[6-gct.tagLength-dataLength] ;
      if (gct.tagLength + 2*dataLength == 6) {
    b = getBits(dataLength, "b") ;
    if (doingAlpha)
        a = getBits(dataLength, "a") ;
      }
      else if (gct.tagLength + 2*dataLength <  6) {
    g = g >> (6 - gct.tagLength - 2*dataLength) ;

    b = currentHeader & BMASK[6-gct.tagLength-2*dataLength] ;
    if (gct.tagLength + 3*dataLength == 6) {
        if (doingAlpha)
      a = getBits(dataLength, "a") ;
    }
    else if (gct.tagLength + 3*dataLength <  6) {
        b = b >> (6 - gct.tagLength - 3*dataLength) ;

        if (doingAlpha) {
      a = currentHeader &
          BMASK[6 - gct.tagLength - 4*dataLength] ;
      if (gct.tagLength + 4 * dataLength < 6) {
          a = a >> (6 - gct.tagLength - 3*dataLength) ;
      }
      else if (gct.tagLength + 4 * dataLength > 6) {
          ii = getBits(dataLength -
               (6-gct.tagLength - 3*dataLength), "a") ;
          a = (a << (dataLength -
               (6-gct.tagLength - 3*dataLength))) | ii ;
      }
        }
    } else {
        ii = getBits(dataLength -
         (6 - gct.tagLength - 2*dataLength), "b") ;
        b = (b << (dataLength -
             (6 - gct.tagLength - 2*dataLength))) | ii ;
        if (doingAlpha)
      a = getBits(dataLength, "a") ;
    }
      } else {
    ii = getBits(dataLength - (6 - gct.tagLength - dataLength),
           "g") ;
    g = (g << (dataLength -
         (6 - gct.tagLength - dataLength))) | ii ;
    b = getBits(dataLength, "b") ;
    if (doingAlpha)
        a = getBits(dataLength, "a") ;
      }
  } else {
      ii = getBits(dataLength - (6 - gct.tagLength), "r") ;
      r = (r << (dataLength - (6 - gct.tagLength))) | ii ;
      g = getBits(dataLength, "g") ;
      b = getBits(dataLength, "b") ;
      if (doingAlpha)
    a = getBits(dataLength, "a") ;
  }

  // Sign extend delta x y z components.
  r <<= (32 - dataLength) ;  r >>= (32 - dataLength) ;
  g <<= (32 - dataLength) ;  g >>= (32 - dataLength) ;
  b <<= (32 - dataLength) ;  b >>= (32 - dataLength) ;
  a <<= (32 - dataLength) ;  a >>= (32 - dataLength) ;

  // Normalize values.
  dr = (short)(r << gct.rightShift) ;
  dg = (short)(g << gct.rightShift) ;
  db = (short)(b << gct.rightShift) ;
  da = (short)(a << gct.rightShift) ;

  // Update current position, first adding deltas if in relative mode.
  if (gct.absolute != 0) {
      curR = dr ; curG = dg ; curB = db ;
      if (doingAlpha) curA = da ;
      if (debug) System.err.println(" absolute color: r " + curR +
            " g " + curG + " b " + curB +
            " a " + curA) ;
  } else {
      curR += dr ; curG += dg ; curB += db ;
      if (doingAlpha) curA += da ;
      if (debug) System.err.println(" delta color: dr "  + dr +
            " dg " + dg + " db " + db +
            " da " + da) ;
  }

  // Do optional mesh buffer push.
  if (mbp != 0) {
      if (debug)
    System.err.println(" pushing color into mesh buffer at " +
           meshIndex) ;

      meshBuffer[meshIndex].r = curR ;
      meshBuffer[meshIndex].g = curG ;
      meshBuffer[meshIndex].b = curB ;
      meshBuffer[meshIndex].a = curA ;
  }

  // Convert point back to [-1..1] floating point.
  fR = curR ; fR /= 32768.0 ;
  fG = curG ; fG /= 32768.0 ;
  fB = curB ; fB /= 32768.0 ;
  fA = curA ; fA /= 32768.0 ;

  curColor.set(fR, fG, fB, fA) ;
  if (debug) System.err.println(" result color: " + fR +
              " " + fG + " " + fB + " " + fA) ;

  // A set color opcode when colors aren't bundled with the vertices
  // is a global color change.
  if (! bundlingColor) outputColor(curColor) ;
    }


    //
    // Process a mesh buffer reference command.
    //
    private void processMeshBR() {
  MeshBufferEntry entry ;
  int index, normal ;
  int ii ;

  if (debug)
      System.err.println("GeometryDecompressor.processMeshBR") ;

  ii = getBits(1, "mbr") ;

  index = (currentHeader >>> 1) & 0xF ;
  repCode = ((currentHeader & 0x1) << 1) | ii ;

  // Adjust index to proper place in fifo.
  index = (meshIndex - index) & 0xf ;
  if (debug)
      System.err.println(" using index " + index) ;

  // Get reference to mesh buffer entry.
  entry = meshBuffer[index] ;
  curX = entry.x ;
  curY = entry.y ;
  curZ = entry.z ;

  // Convert point back to [-1..1] floating point.
  curPos.set(((float)curX)/32768.0f,
       ((float)curY)/32768.0f,
       ((float)curZ)/32768.0f) ;

  if (debug) System.err.println(" retrieved position " + curPos.x +
              " " + curPos.y + " " + curPos.z +
              " replace code " + repCode) ;

  // Get mesh buffer normal if previous opcode was not a setNormal.
  if (bundlingNorm && ((meshState & USE_MESH_NORMAL) != 0)) {
      curSex = entry.sextant ;
      curOct = entry.octant ;
      curU = entry.u ;
      curV = entry.v ;

      // Convert normal back to -1.0 - 1.0 floating point from index.
      normal = (curSex<<15) | (curOct<<12) | (curU<<6) | curV ;

      if (debug) System.err.println(" retrieving normal") ;
      indexNormal(curSex, curOct, curU, curV, curNorm) ;
  }

  // Get mesh buffer color if previous opcode was not a setColor.
  if (bundlingColor && ((meshState & USE_MESH_COLOR) != 0)) {
      curR = entry.r ;
      curG = entry.g ;
      curB = entry.b ;

      // Convert point back to -1.0 - 1.0 floating point.
      curColor.x = curR ; curColor.x /= 32768.0 ;
      curColor.y = curG ; curColor.y /= 32768.0 ;
      curColor.z = curB ; curColor.z /= 32768.0 ;

      if (doingAlpha) {
    curA = entry.a ;
    curColor.w = curA ; curColor.w /= 32768.0 ;
      }
      if (debug)
    System.err.println(" retrieved color "    + curColor.x +
           " " + curColor.y + " " + curColor.z +
           " " + curColor.w) ;
  }

        // Reset meshState.
  meshState = 0 ;
    }


    // Process a end-of-stream opcode.
    private void processEos() {
  if (debug) System.err.println("GeometryDecompressor.processEos") ;
    }

    // Process a variable length no-op opcode.
    private void processVNoop() {
  int ii, ct ;
  if (debug) System.err.println("GeometryDecompressor.processVNoop") ;

  ct = getBits(5, "noop count") ;
  ii = getBits(ct, "noop bits") ;
    }

    // Process a pass-through opcode.
    private void processPassThrough() {
  int ignore ;
  if (debug)
      System.err.println("GeometryDecompressor.processPassThrough") ;

  ignore = getBits(24, "passthrough") ;
  ignore = getBits(32, "passthrough") ;
    }

    // Process a skip-8 opcode.
    private void processSkip8() {
  int skip ;
  if (debug) System.err.println("GeometryDecompressor.processSkip8") ;

  skip = getBits(8, "skip8") ;
    }

    private void benchmarkStart(int length) {
  vertexCount = 0 ;
  System.err.println(" GeometryDecompressor: decompressing " +
         length + " bytes...") ;
  startTime = J3dClock.currentTimeMillis() ;
    }

    private void benchmarkPrint(int length) {
  float t = (J3dClock.currentTimeMillis() - startTime) / 1000.0f ;
  System.err.println
      ("  done in " + t + " sec." + "\n" +
       "  decompressed " + vertexCount + " vertices at " +
       (vertexCount/t) + " vertices/sec\n") ;

  System.err.print("  vertex data present: coords") ;
  int floatVertexSize = 12 ;
  if (bundlingNorm) {
      System.err.print(" normals") ;
      floatVertexSize += 12 ;
  }
  if (bundlingColor) {
      System.err.println(" colors") ;
      floatVertexSize += 12 ;
  }
  if (doingAlpha) {
      System.err.println(" alpha") ;
      floatVertexSize +=  4 ;
  }
  System.err.println() ;

  System.err.println
      ("  bytes of data in generalized strip output: " +
       (vertexCount * floatVertexSize) + "\n" +
       "  compression ratio: " +
       (length / (float)(vertexCount * floatVertexSize)) + "\n") ;
    }
}
TOP

Related Classes of javax.media.j3d.GeometryDecompressor$HuffmanTableEntry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.