Package javax.media.j3d

Source Code of javax.media.j3d.GeometryArrayRetained

/*
* Copyright 1996-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 java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;

import javax.vecmath.Color3b;
import javax.vecmath.Color3f;
import javax.vecmath.Color4b;
import javax.vecmath.Color4f;
import javax.vecmath.Point2d;
import javax.vecmath.Point2f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Point4d;
import javax.vecmath.Point4f;
import javax.vecmath.TexCoord2f;
import javax.vecmath.TexCoord3f;
import javax.vecmath.TexCoord4f;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;


/**
* The GeometryArray object contains arrays of positional coordinates,
* colors, normals and/or texture coordinates that describe
* point, line, or surface geometry.  It is extended to create
* the various primitive types (e.g., lines, triangle_strips, etc.)
*/

abstract class GeometryArrayRetained extends GeometryRetained{

    // XXXX: Memory footprint reduction. Should have separate object to
    //       to contain specific data such as a ByRef object for
    //       all ByRef related data. So that incases where no
    //       ByRef is needed, the ByRef object reference is
    //       set to null. Hence saving memory!
    //       Need object such as Texture, D3d and ByRef ...
    //


    // Contains a bitset indicating which components are present
    int vertexFormat;

    // Whether this geometry was ever rendered as transparent
    int c4fAllocated =  0;

    // Total Number of vertices
    int vertexCount;

    // number of vertices used in rendering
    int validVertexCount;

    // The vertex data in packed format
    float vertexData[];

    // vertex data in packed format for each screen in multi-screen situation
    // if alpha values of each vertex are to be updated
    private float mvertexData[][];

    //
    // The following offset/stride values are internally computed
    // from the format
    //

    // Stride (in words) from one vertex to the next
    int stride;

    // Stride (in words) from one texture coordinate to the next
    int texCoordStride;

    // Offset (in words) within each vertex of the coordinate position
    int coordinateOffset;

    // Offset (in words) within each vertex of the normal
    int normalOffset;

    // Offset (in words) within each vertex of the color
    int colorOffset;

    // Offset (in words) within each vertex of the texture coordinate
    int textureOffset;

    // Offset (in words) within each vertex of each vertex attribute
    int[] vertexAttrOffsets;

    // Stride (size) of all vertex attributes
    int vertexAttrStride;

    // alpha value for transparency and texture blending
    private float[] lastAlpha = new float[1];
    float lastScreenAlpha = -1;

    int colorChanged = 0;

    // byte to float scale factor
    static final float ByteToFloatScale = 1.0f/255.0f;

    // float to byte scale factor
    static final float FloatToByteScale = 255.0f;

    // Set flag indicating that we are in the updater.  This flag
    // can be used by the various setRef methods to inhibit any
    // update messages
    boolean inUpdater = false;

// Array List used for messages
ArrayList<GeometryAtom> gaList = new ArrayList<GeometryAtom>(1);

    // Target threads to be notified when morph changes
    static final int targetThreads = (J3dThread.UPDATE_RENDER |
              J3dThread.UPDATE_GEOMETRY);

    // used for byReference geometry
    float[] floatRefCoords = null;
    double[] doubleRefCoords = null;
    Point3d[] p3dRefCoords = null;
    Point3f[] p3fRefCoords = null;

    // Used for NIO buffer geometry
    J3DBuffer coordRefBuffer = null;
    FloatBuffer floatBufferRefCoords = null;
    DoubleBuffer doubleBufferRefCoords = null;

    // Initial index to use for rendering
    int initialCoordIndex = 0;
    int initialColorIndex = 0;
    int initialNormalIndex = 0;
    int[] initialTexCoordIndex = null;
    int[] initialVertexAttrIndex = null;
    int initialVertexIndex = 0;


    // used for byReference colors
    float[] floatRefColors = null;
    byte[] byteRefColors = null;
    Color3f[] c3fRefColors = null;
    Color4f[] c4fRefColors = null;
    Color3b[] c3bRefColors = null;
    Color4b[] c4bRefColors = null;

    // Used for NIO buffer colors
    J3DBuffer colorRefBuffer = null;
    FloatBuffer floatBufferRefColors = null;
    ByteBuffer byteBufferRefColors = null;

    // flag to indicate if the "by reference" component is already set
    int vertexType = 0;
    static  final int PF    = 0x1;
    static  final int PD    = 0x2;
    static  final int P3F   = 0x4;
    static  final int P3D   = 0x8;
    static final int VERTEX_DEFINED = PF | PD | P3F | P3D;

    static final int CF  = 0x10;
    static final int CUB = 0x20;
    static final int C3F = 0x40;
    static final int C4F = 0x80;
    static final int C3UB  = 0x100;
    static final int C4UB = 0x200;
    static final int COLOR_DEFINED = CF | CUB | C3F | C4F| C3UB | C4UB;

    static final int NF = 0x400;
    static final int N3F = 0x800;
    static final int NORMAL_DEFINED = NF | N3F;

    static final int TF = 0x1000;
    static final int T2F = 0x2000;
    static final int T3F = 0x4000;
    static final int TEXCOORD_DEFINED = TF | T2F | T3F;

    static final int AF = 0x8000;
    static final int VATTR_DEFINED = AF;

    // Flag word indicating the type of by-ref texCoord. We will copy this to
    // the vertexType field only when the references for all texture coordinate
    // sets are set to non-null values.
    private int texCoordType = 0;

    // Flag word indicating the type of by-ref vertex attr. We will copy this to
    // the vertexType field only when the references for all vertex attrs
    // are set to non-null values.
    private int vertexAttrType = 0;

    // flag for execute geometry array when by reference
    static final int COORD_FLOAT  = 0x01;
    static final int COORD_DOUBLE = 0x02;
    static final int COLOR_FLOAT  = 0x04;
    static final int COLOR_BYTE   = 0x08;
    static final int NORMAL_FLOAT = 0x10;
    static final int TEXCOORD_FLOAT = 0x20;
    static final int VATTR_FLOAT = 0x40;


    // used by "by reference" normals
    float[] floatRefNormals = null;
    Vector3f[] v3fRefNormals = null;

    // Used for NIO buffer normals
    J3DBuffer normalRefBuffer = null;
    FloatBuffer floatBufferRefNormals = null;

    // used for "by reference" vertex attrs
    float[][] floatRefVertexAttrs = null;

    // Used for NIO buffer vertex attrs
    J3DBuffer[] vertexAttrsRefBuffer = null;
    FloatBuffer[] floatBufferRefVertexAttrs = null;

    // used by "by reference" tex coords
    Object[] refTexCoords = null;
    TexCoord2f[] t2fRefTexCoords = null;
    TexCoord3f[] t3fRefTexCoords = null;

    // Used for NIO buffer tex coords
    J3DBuffer[] refTexCoordsBuffer = null;
    //FloatBufferWrapper[] floatBufferRefTexCoords = null;


    // used by interleaved array
    float[] interLeavedVertexData = null;

    // used by interleaved NIO buffer
    J3DBuffer interleavedVertexBuffer = null;
    FloatBuffer interleavedFloatBufferImpl = null;

    // pointers used, when transparency is turned on
    // or when its an object such as C3F, P3F etc ..
    float[] mirrorFloatRefCoords = null;
    double[] mirrorDoubleRefCoords = null;
    float[] mirrorFloatRefNormals = null;
    float[][] mirrorFloatRefVertexAttrs = null;
    float[] mirrorFloatRefTexCoords = null;
    Object[] mirrorRefTexCoords = null;

    float[][] mirrorFloatRefColors = new float[1][];
    byte[][] mirrorUnsignedByteRefColors= new byte[1][];
    float[][] mirrorInterleavedColorPointer = null;

    // boolean to determine if a mirror was allocated
    int mirrorVertexAllocated = 0;
    int mirrorColorAllocated = 0;
    boolean mirrorNormalAllocated = false;

    // Some dirty bits for GeometryArrays
    static final int COORDINATE_CHANGED   = 0x01;
    static final int NORMAL_CHANGED     = 0x02;
    static final int COLOR_CHANGED     = 0x04;
    static final int TEXTURE_CHANGED     = 0x08;
    static final int BOUNDS_CHANGED     = 0x10;
    static final int INDEX_CHANGED     = 0x20;
    static final int STRIPCOUNT_CHANGED   = 0x40;
    static final int VATTR_CHANGED     = 0x80;
    static final int VERTEX_CHANGED             = COORDINATE_CHANGED |
                                                  NORMAL_CHANGED |
                                                  COLOR_CHANGED |
                                                  TEXTURE_CHANGED |
                                                  VATTR_CHANGED;

    static final int defaultTexCoordSetMap[] = {0};
    int texCoordSetCount = 0;
    int [] texCoordSetMap = null;

    // this array contains offset to the texCoord data for each
    // texture unit.  -1 means no corresponding texCoord data offset
    int [] texCoordSetMapOffset = null;

    // Vertex attribute information
    int vertexAttrCount = 0;
    int[] vertexAttrSizes = null;


    // This point to a list of VertexBuffers in a Vector structure
    // Each element correspond to a D3D context that create this VB.
    // Note that this GeometryArray can be used by multiple ctx.
    int dirtyFlag;

    // each bit corresponds to a unique renderer if shared context
    // or a unique canvas otherwise
    int resourceCreationMask = 0x0;

    // Fix for Issue 5
    //
    // Replace the per-canvas reference count with a per-RenderBin set
    // of users.  The per-RenderBin set of users of this display list
    // is defined as a HashMap where:
    //
    //   key   = the RenderBin
    //   value = a set of RenderAtomListInfo objects using this
    //           geometry array for display list purposes
private HashMap<RenderBin, HashSet<RenderAtomListInfo>> dlistUsers = null;

    // timestamp used to create display list. This is either
    // one per renderer for useSharedCtx, or one per Canvas for non-shared
    // ctx
    private long[] timeStampPerDlist = new long[2];

    // Unique display list Id, if this geometry is shared
    int dlistId = -1;
    Integer dlistObj = null;

    // A list of pre-defined bits to indicate which component
    // in this Texture object changed.
    //    static final int DLIST_CREATE_CHANGED      = 0x01;
    static final int INIT_MIRROR_GEOMETRY      = 0x02;


// A list of Universes that this Geometry is referenced in Morph from
ArrayList<VirtualUniverse> morphUniverseList = null;

// A list of ArrayLists which contain all the MorphRetained objects
// refering to this geometry. Each list corresponds to the universe
// above.
ArrayList<ArrayList<MorphRetained>> morphUserLists = null;

    // The following variables are only used in compile mode

    // Offset of a geometry array into the merged array
    int[] geoOffset;

    // vertexcount of a geometry array in a merge array
    int[] compileVcount;

    boolean isCompiled = false;

    boolean isShared = false;

    IndexedGeometryArrayRetained cloneSourceArray = null;

    static final double EPS = 1.0e-13;

    GeometryArrayRetained() {
  dirtyFlag = INDEX_CHANGED|VERTEX_CHANGED;
        lastAlpha[0] = 1.0f;
    }


    @Override
    void setLive(boolean inBackgroundGroup, int refCount) {
  dirtyFlag = VERTEX_CHANGED|INDEX_CHANGED;
        isEditable = !isWriteStatic();
        super.doSetLive(inBackgroundGroup, refCount);
  super.markAsLive();
  // Send message to RenderingAttribute structure to obtain a dlistId
  //  System.err.println("Geometry - "+this+"refCount = "+this.refCount);
  if (this.refCount > 1) {
      // Send to rendering attribute structure,
      /*
      J3dMessage createMessage = new J3dMessage();
      createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
      createMessage.type = J3dMessage.GEOMETRYARRAY_CHANGED;
      createMessage.universe = null;
      createMessage.args[0] = this;
      createMessage.args[1]= new Integer(DLIST_CREATE_CHANGED);
      VirtualUniverse.mc.processMessage(createMessage);
      */
      isShared = true;
  } // Clone geometry only for the first setLive
  else {
      // If geometry is indexed and use_index_coord is false, unindexify
      // otherwise, set mirrorGeometry to null (from previous clearLive)
      if (this instanceof IndexedGeometryArrayRetained) {
    // Send to rendering attribute structure,
    J3dMessage createMessage = new J3dMessage();
    createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
    createMessage.type = J3dMessage.GEOMETRY_CHANGED;
    createMessage.universe = null;
    createMessage.args[0] = null;
    createMessage.args[1]= this;
    createMessage.args[2]= new Integer(INIT_MIRROR_GEOMETRY);
    VirtualUniverse.mc.processMessage(createMessage);
      }
  }

    }

    @Override
    void clearLive(int refCount) {
  super.clearLive(refCount);

  if (this.refCount <= 0) {
      isShared = false;
  }
    }

    @Override
    void computeBoundingBox() {

  //  System.err.println("computeBoundingBox ....");

        if (boundsDirty && VirtualUniverse.mc.cacheAutoComputedBounds) {
            for(ArrayList<Shape3DRetained> users : userLists) {
                for(Shape3DRetained shape : users)
                    shape.dirtyBoundsCache();
            }
        }

  if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
      // by copy
      computeBoundingBox(initialVertexIndex, vertexData);

  } else if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) { // USE_NIO_BUFFER
      //System.err.println("vertexFormat & GeometryArray.USE_NIO_BUFFER");
      if((vertexFormat & GeometryArray.INTERLEAVED) != 0) {
    computeBoundingBox(initialCoordIndex, interleavedFloatBufferImpl);
      } else if((vertexType & PF) != 0) {
    computeBoundingBox(floatBufferRefCoords);
      } else if((vertexType & PD) != 0) {
    computeBoundingBox(doubleBufferRefCoords);
      }

  } else if ((vertexFormat & GeometryArray.INTERLEAVED) != 0) {
      //System.err.println("vertexFormat & GeometryArray.INTERLEAVED");
      computeBoundingBox(initialCoordIndex, interLeavedVertexData);
  } else if ((vertexType & PF) != 0) {
      //System.err.println("vertexType & PF");
      computeBoundingBox(floatRefCoords);
  } else if ((vertexType & P3F) != 0) {
      //System.err.println("vertexType & P3F");
      computeBoundingBox(p3fRefCoords);
  } else if ((vertexType & P3D) != 0) {
      //System.err.println("vertexType & P3D");
      computeBoundingBox(p3dRefCoords);
  } else if ((vertexType & PD) != 0) {
      //System.err.println("vertexType & PD");
      computeBoundingBox(doubleRefCoords);
  }

    }


    // NullGeometry is true only for byRef case
    void processCoordsChanged(boolean nullGeo) {

  /*
    System.err.println("processCoordsChanged : nullGeo " + nullGeo);
    System.err.println("Before :processCoordsChanged : geoBounds ");
    System.err.println(geoBounds);
  */
  if (nullGeo) {
      synchronized(geoBounds) {
    geoBounds.setLower(-1.0, -1.0, -1.0);
    geoBounds.setUpper(1.0, 1.0, 1.0);
    boundsDirty = false;
      }
      synchronized(centroid) {
    recompCentroid = false;
      geoBounds.getCenter(this.centroid);
      }

  }
  else {
      // re-compute centroid if used
      synchronized(centroid) {
    recompCentroid = true;
      }

      synchronized(geoBounds) {
    boundsDirty = true;
    computeBoundingBox();
      }

      /*
        System.err.println("After :processCoordsChanged : geoBounds ");
        System.err.println(geoBounds);
      */
  }
    }


    void computeBoundingBox(int vIndex, float[] vdata) {
  int i, offset;
  double xmin, xmax, ymin, ymax, zmin, zmax;


  //System.err.println("Before : computeBoundingBox : geoBounds ");
  //  System.err.println(geoBounds);

  synchronized(geoBounds) {

      // If autobounds compute is false  then return
      // It is possible that user call getBounds() before
      // this Geometry add to live scene graph.
      if ((computeGeoBounds == 0) && (refCount > 0)) {
        return;
      }
      if (!boundsDirty)
    return;

      // Initial offset
      offset = vIndex * stride+coordinateOffset;
      // Compute the bounding box
      xmin = xmax = vdata[offset];
      ymin = ymax = vdata[offset+1];
      zmin = zmax = vdata[offset+2];
      offset += stride;
      for (i=1; i<validVertexCount; i++) {
    if (vdata[offset] > xmax)
        xmax = vdata[offset];
    if (vdata[offset] < xmin)
        xmin = vdata[offset];

    if (vdata[offset+1] > ymax)
        ymax = vdata[offset+1];
    if (vdata[offset+1] < ymin)
        ymin = vdata[offset+1];

    if (vdata[offset+2] > zmax)
        zmax = vdata[offset+2];
    if (vdata[offset+2] < zmin)
        zmin = vdata[offset+2];

    offset += stride;
      }

      geoBounds.setUpper(xmax, ymax, zmax);
      geoBounds.setLower(xmin, ymin, zmin);
      boundsDirty = false;
  }
  /*
    System.err.println("After : computeBoundingBox : geoBounds ");
    System.err.println(geoBounds);
  */
    }

    // Compute boundingbox for interleaved nio buffer
    void computeBoundingBox(int vIndex,   FloatBuffer vdata) {
  int i, offset;
  double xmin, xmax, ymin, ymax, zmin, zmax;


  synchronized(geoBounds) {
      // If autobounds compute is false  then return
      if ((computeGeoBounds == 0) && (refCount > 0)) {
    return;
      }

      if (!boundsDirty)
    return;

      // Initial offset
      offset = vIndex * stride+coordinateOffset;
      // Compute the bounding box
      xmin = xmax = vdata.get(offset);
      ymin = ymax = vdata.get(offset+1);
      zmin = zmax = vdata.get(offset+2);
      offset += stride;
      for (i=1; i<validVertexCount; i++) {
    if (vdata.get(offset) > xmax)
        xmax = vdata.get(offset);
    if (vdata.get(offset) < xmin)
        xmin = vdata.get(offset);

    if (vdata.get(offset+1) > ymax)
        ymax = vdata.get(offset+1);
    if (vdata.get(offset+1) < ymin)
        ymin = vdata.get(offset+1);

    if (vdata.get(offset+2) > zmax)
        zmax = vdata.get(offset+2);
    if (vdata.get(offset+2) < zmin)
        zmin = vdata.get(offset+2);

    offset += stride;
      }

      geoBounds.setUpper(xmax, ymax, zmax);
      geoBounds.setLower(xmin, ymin, zmin);
      boundsDirty = false;
  }
    }


    // compute bounding box for coord with nio buffer
    void computeBoundingBox( DoubleBuffer buffer) {
  int i, j, k, sIndex;
  double xmin, xmax, ymin, ymax, zmin, zmax;

  synchronized(geoBounds) {
      // If autobounds compute is false  then return
      if ((computeGeoBounds == 0) && (refCount > 0)) {
    return;
      }

      if (!boundsDirty)
    return;

      sIndex = initialCoordIndex;
      int maxIndex = 3*validVertexCount;

      // Compute the bounding box
      xmin = xmax = buffer.get(sIndex++);
      ymin = ymax = buffer.get(sIndex++);
      zmin = zmax = buffer.get(sIndex++);

      for (i=sIndex; i<maxIndex; i+=3) {
    j = i + 1;
    k = i + 2;

    if (buffer.get(i) > xmax)
        xmax = buffer.get(i);
    if (buffer.get(i) < xmin)
        xmin = buffer.get(i);

    if (buffer.get(j) > ymax)
        ymax = buffer.get(j);
    if (buffer.get(j) < ymin)
        ymin = buffer.get(j);

    if (buffer.get(k) > zmax)
        zmax = buffer.get(k);
    if (buffer.get(k) < zmin)
        zmin = buffer.get(k);

      }
      geoBounds.setUpper(xmax, ymax, zmax);
      geoBounds.setLower(xmin, ymin, zmin);
      boundsDirty = false;
  }
    }

    // compute bounding box for coord with nio buffer
    void computeBoundingBox( FloatBuffer buffer) {
  int i, j, k, sIndex;
  double xmin, xmax, ymin, ymax, zmin, zmax;


  synchronized(geoBounds) {
      // If autobounds compute is false  then return
      if ((computeGeoBounds == 0) && (refCount > 0)) {
    return;
      }

      if (!boundsDirty)
    return;


      sIndex = initialCoordIndex;
      int maxIndex = 3*validVertexCount;

      // Compute the bounding box
      xmin = xmax = buffer.get(sIndex++);
      ymin = ymax = buffer.get(sIndex++);
      zmin = zmax = buffer.get(sIndex++);

      for (i=sIndex; i<maxIndex; i+=3) {
    j = i + 1;
    k = i + 2;

    if (buffer.get(i) > xmax)
        xmax = buffer.get(i);
    if (buffer.get(i) < xmin)
        xmin = buffer.get(i);

    if (buffer.get(j) > ymax)
        ymax = buffer.get(j);
    if (buffer.get(j) < ymin)
        ymin = buffer.get(j);

    if (buffer.get(k) > zmax)
        zmax = buffer.get(k);
    if (buffer.get(k) < zmin)
        zmin = buffer.get(k);

      }
      geoBounds.setUpper(xmax, ymax, zmax);
      geoBounds.setLower(xmin, ymin, zmin);
      boundsDirty = false;
  }
    }

    void computeBoundingBox(float[] coords) {
  // System.err.println("GeometryArrayRetained : computeBoundingBox(float[] coords)");
  int i, j, k, sIndex;
  double xmin, xmax, ymin, ymax, zmin, zmax;

  synchronized(geoBounds) {
      // If autobounds compute is false  then return
      if ((computeGeoBounds == 0) && (refCount > 0)) {
    return;
      }

      if (!boundsDirty)
    return;

      sIndex = initialCoordIndex;
      int maxIndex = 3*validVertexCount;

  // Compute the bounding box
      xmin = xmax = coords[sIndex++];
      ymin = ymax = coords[sIndex++];
      zmin = zmax = coords[sIndex++];

      for (i=sIndex; i<maxIndex; i+=3) {
    j = i + 1;
    k = i + 2;

    if (coords[i] > xmax)
        xmax = coords[i];
    if (coords[i] < xmin)
        xmin = coords[i];

    if (coords[j] > ymax)
        ymax = coords[j];
    if (coords[j] < ymin)
        ymin = coords[j];

    if (coords[k] > zmax)
        zmax = coords[k];
    if (coords[k] < zmin)
        zmin = coords[k];

      }
      geoBounds.setUpper(xmax, ymax, zmax);
      // System.err.println("max(" + xmax + ", " + ymax + ", " + zmax + ")");
      geoBounds.setLower(xmin, ymin, zmin);
      // System.err.println("min(" + xmin + ", " + ymin + ", " + zmin + ")");

      boundsDirty = false;
  }

    }

    void computeBoundingBox(double[] coords) {
  int i, j, k, sIndex;
  double xmin, xmax, ymin, ymax, zmin, zmax;

  synchronized(geoBounds) {
      // If autobounds compute is false  then return
      if ((computeGeoBounds == 0) && (refCount > 0)) {
    return;
      }

      if (!boundsDirty)
    return;


      sIndex = initialCoordIndex;
      int maxIndex = 3*validVertexCount;

      // Compute the bounding box
      xmin = xmax = coords[sIndex++];
      ymin = ymax = coords[sIndex++];
      zmin = zmax = coords[sIndex++];

      for (i=sIndex; i<maxIndex; i+=3) {
    j = i + 1;
    k = i + 2;

    if (coords[i] > xmax)
        xmax = coords[i];
    if (coords[i] < xmin)
        xmin = coords[i];

    if (coords[j] > ymax)
        ymax = coords[j];
    if (coords[j] < ymin)
        ymin = coords[j];

    if (coords[k] > zmax)
        zmax = coords[k];
    if (coords[k] < zmin)
        zmin = coords[k];

      }
      geoBounds.setUpper(xmax, ymax, zmax);
      geoBounds.setLower(xmin, ymin, zmin);
      boundsDirty = false;
  }

    }

    void computeBoundingBox(Point3f[] coords) {

  double xmin, xmax, ymin, ymax, zmin, zmax;
  Point3f p;

  synchronized(geoBounds) {
      // If autobounds compute is false  then return
      if ((computeGeoBounds == 0) && (refCount > 0)) {
    return;
      }

      if (!boundsDirty)
    return;



  // Compute the bounding box
      xmin = xmax = coords[initialCoordIndex].x;
      ymin = ymax = coords[initialCoordIndex].y;
      zmin = zmax = coords[initialCoordIndex].z;

      for (int i=initialCoordIndex+1; i<validVertexCount; i++) {
    p = coords[i];
    if (p.x > xmax) xmax = p.x;
    if (p.x < xmin) xmin = p.x;

    if (p.y > ymax) ymax = p.y;
    if (p.y < ymin) ymin = p.y;

    if (p.z > zmax) zmax = p.z;
    if (p.z < zmin) zmin = p.z;

      }
      geoBounds.setUpper(xmax, ymax, zmax);
      geoBounds.setLower(xmin, ymin, zmin);
      boundsDirty = false;
  }

    }

    void computeBoundingBox(Point3d[] coords) {

  double xmin, xmax, ymin, ymax, zmin, zmax;
  Point3d p;

  synchronized(geoBounds) {
      // If autobounds compute is false  then return
      if ((computeGeoBounds == 0) && (refCount > 0)) {
    return;
      }

      if (!boundsDirty)
    return;


  // Compute the bounding box
      xmin = xmax = coords[initialCoordIndex].x;
      ymin = ymax = coords[initialCoordIndex].y;
      zmin = zmax = coords[initialCoordIndex].z;

      for (int i=initialCoordIndex+1; i<validVertexCount; i++) {
    p = coords[i];
    if (p.x > xmax) xmax = p.x;
    if (p.x < xmin) xmin = p.x;

    if (p.y > ymax) ymax = p.y;
    if (p.y < ymin) ymin = p.y;

    if (p.z > zmax) zmax = p.z;
    if (p.z < zmin) zmin = p.z;

      }
      geoBounds.setUpper(xmax, ymax, zmax);
      geoBounds.setLower(xmin, ymin, zmin);
      boundsDirty = false;
  }

    }


    @Override
    synchronized void update() {
    }

    void setupMirrorVertexPointer(int vType) {
  int i, index;

  switch (vType) {
  case PF:
      if (floatRefCoords == null) {
    if ((vertexType & VERTEX_DEFINED) == PF) {
        vertexType &= ~PF;
        mirrorFloatRefCoords = null;
        mirrorVertexAllocated &= ~PF;
    }
      }
      else {
    vertexType |= PF;
    mirrorFloatRefCoords = floatRefCoords;
    mirrorVertexAllocated &= ~PF;
      }

      break;
  case PD:
      if (doubleRefCoords == null) {
    if ((vertexType & VERTEX_DEFINED) == PD) {
        mirrorDoubleRefCoords = null;
        mirrorVertexAllocated &= ~PD;
        vertexType &= ~PD;
    }
    vertexType &= ~PD;
      }
      else {
    vertexType |= PD;
    mirrorDoubleRefCoords = doubleRefCoords;
    mirrorVertexAllocated &= ~PD;
      }

      break;
  case P3F:
      if (p3fRefCoords == null) {
    vertexType &= ~P3F;
    // Don't set the mirrorFloatRefCoords to null,
    // may be able to re-use
    //      mirrorFloatRefCoords = null;
      }
      else {
    vertexType |= P3F;

    if ((mirrorVertexAllocated & PF) == 0) {
        mirrorFloatRefCoords = new float[vertexCount * 3];
        mirrorVertexAllocated |= PF;
    }

    index = initialCoordIndex * 3;
    for ( i=initialCoordIndex; i<validVertexCount; i++) {
        mirrorFloatRefCoords[index++] = p3fRefCoords[i].x;
        mirrorFloatRefCoords[index++] = p3fRefCoords[i].y;
        mirrorFloatRefCoords[index++] = p3fRefCoords[i].z;
    }
      }
      break;
  case P3D:
      if (p3dRefCoords == null) {
    vertexType &= ~P3D;
    // Don't set the mirrorDoubleRefCoords to null,
    // may be able to re-use
    //      mirrorDoubleRefCoords = null;
      }
      else {
    vertexType |= P3D;

    if ((mirrorVertexAllocated & PD) == 0) {
        mirrorDoubleRefCoords = new double[vertexCount * 3];
        mirrorVertexAllocated |= PD;
    }

    index = initialCoordIndex * 3;
    for ( i=initialCoordIndex; i<validVertexCount; i++) {
        mirrorDoubleRefCoords[index++] = p3dRefCoords[i].x;
        mirrorDoubleRefCoords[index++] = p3dRefCoords[i].y;
        mirrorDoubleRefCoords[index++] = p3dRefCoords[i].z;
    }
      }
      break;
  default:
      break;

  }

    }

    // If turned transparent the first time, then force it to allocate
    void setupMirrorInterleavedColorPointer(boolean force) {
  int index, length, offset;
  int i;

  if (force || (c4fAllocated != 0)) { // Color is present

      length = 4 * vertexCount;

      if (mirrorInterleavedColorPointer == null) {
    mirrorInterleavedColorPointer = new float[1][length];
      }

      index = 4 * initialVertexIndex;
      offset = stride * initialVertexIndex + colorOffset;

      if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0 &&
    interLeavedVertexData != null ) { // java array
    if ((vertexFormat  & GeometryArray.WITH_ALPHA) != 0) {

        for (i = initialVertexIndex; i < validVertexCount; i++) {
      mirrorInterleavedColorPointer[0][index++] =
          interLeavedVertexData[offset];
      mirrorInterleavedColorPointer[0][index++] =
          interLeavedVertexData[offset+1];
      mirrorInterleavedColorPointer[0][index++] =
          interLeavedVertexData[offset+2];
      mirrorInterleavedColorPointer[0][index++] =
          interLeavedVertexData[offset+3];
      offset += stride;
        }
    }
    else {
        for (i = initialVertexIndex; i < validVertexCount; i++) {
      mirrorInterleavedColorPointer[0][index++] =
          interLeavedVertexData[offset];
      mirrorInterleavedColorPointer[0][index++] =
          interLeavedVertexData[offset+1];
      mirrorInterleavedColorPointer[0][index++] =
          interLeavedVertexData[offset+2];
      mirrorInterleavedColorPointer[0][index++] = 1.0f;
      offset += stride;
        }
    }

      } else { // NIO BUFFER
    if ((vertexFormat  & GeometryArray.WITH_ALPHA) != 0 &&
        interleavedFloatBufferImpl != null) {
        for (i = initialVertexIndex; i < validVertexCount; i++) {
      interleavedFloatBufferImpl.position(offset);
      interleavedFloatBufferImpl.get(mirrorInterleavedColorPointer[0],
                   index , 4);
      index += 4;
      offset += stride;
        }
    }
    else {
        for (i = initialVertexIndex; i < validVertexCount; i++) {
      interleavedFloatBufferImpl.position(offset);
      interleavedFloatBufferImpl.get(mirrorInterleavedColorPointer[0],
                   index, 3);
      mirrorInterleavedColorPointer[0][index+3] = 1.0f;
      index += 4;
      offset += stride;

        }
    }
      }
      c4fAllocated = GeometryArray.WITH_ALPHA;
  }
    }

    // If turned transparent the first time, then force it to allocate
    void setupMirrorColorPointer(int ctype, boolean force) {
  int i, srcIndex = 0, dstIndex = 0;
  int multiplier;

   if (c4fAllocated == 0 && !force) {
      multiplier = 3;
  } else {

      // If the first time, we are forced to allocate 4f, then
      // we need to force the allocation of the colors again
      // for the case when allocation has previously occurred
      // only for RGB
      if (force && (c4fAllocated == 0) &&
    (vertexFormat & GeometryArray.WITH_ALPHA) == 0)  {
    mirrorColorAllocated = 0;
      }
      c4fAllocated = GeometryArray.WITH_ALPHA;
      multiplier = 4;
  }

  if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) { // java array
      switch (ctype) {
      case CF:
    if (floatRefColors == null) {
        if ((c4fAllocated == 0) && !force &&
      (vertexType & COLOR_DEFINED) == CF) {
      mirrorFloatRefColors[0] = null;
      mirrorColorAllocated &= ~CF;
        }
        vertexType &= ~CF;
        return;
    }

    vertexType |= CF;
    if (c4fAllocated == 0 && !force) {
        mirrorFloatRefColors[0] = floatRefColors;
        mirrorColorAllocated &= ~CF;
    }
    else {
         if ((mirrorColorAllocated & CF) == 0) {
      mirrorFloatRefColors[0] = new float[4 * vertexCount];
      mirrorColorAllocated |= CF;
        }

        if ((vertexFormat & GeometryArray.WITH_ALPHA) == 0) {

      srcIndex = initialColorIndex * 3;
      dstIndex = initialColorIndex * 4;

      for (i = initialColorIndex; i < validVertexCount; i++) {
          mirrorFloatRefColors[0][dstIndex++] =
        floatRefColors[srcIndex++];
          mirrorFloatRefColors[0][dstIndex++] =
        floatRefColors[srcIndex++];
          mirrorFloatRefColors[0][dstIndex++] =
        floatRefColors[srcIndex++];
          mirrorFloatRefColors[0][dstIndex++] = 1.0f;
      }

        }
        else {
      srcIndex = initialColorIndex * 4;
      System.arraycopy(floatRefColors, srcIndex,
           mirrorFloatRefColors[0], srcIndex,
           (4*validVertexCount));
        }
    }
    break;
      case CUB:
    if (byteRefColors == null) {
        if (c4fAllocated == 0 && !force &&
      ((vertexType & COLOR_DEFINED) == CUB) ) {
      mirrorUnsignedByteRefColors[0] = null;
      mirrorColorAllocated &= ~CUB;
        }
        vertexType &= ~CUB;
        return;
    }
    vertexType |= CUB;
    if (c4fAllocated == 0 && !force) {
        mirrorUnsignedByteRefColors[0] = byteRefColors;
        mirrorColorAllocated &= ~CUB;;
    }
    else {
        if ((mirrorColorAllocated & CUB) == 0) {
      mirrorUnsignedByteRefColors[0] = new byte[4 * vertexCount];
      mirrorColorAllocated |= CUB;
        }
        if ((vertexFormat & GeometryArray.WITH_ALPHA) == 0) {

      srcIndex = initialColorIndex * 3;
      dstIndex = initialColorIndex * 4;

      for (i = initialColorIndex; i < validVertexCount; i++) {
          mirrorUnsignedByteRefColors[0][dstIndex++] =
        byteRefColors[srcIndex++];
          mirrorUnsignedByteRefColors[0][dstIndex++] =
        byteRefColors[srcIndex++];
          mirrorUnsignedByteRefColors[0][dstIndex++] =
        byteRefColors[srcIndex++];
          mirrorUnsignedByteRefColors[0][dstIndex++] =
        (byte)(255.0);
      }
        }
        else {
      srcIndex = initialColorIndex * 4;
      System.arraycopy(byteRefColors, srcIndex,
           mirrorUnsignedByteRefColors[0], srcIndex,
           (4*validVertexCount));
        }
    }

    break;
      case C3F:
    if (c3fRefColors == null) {
        vertexType &= ~C3F;
        return;
    }
    vertexType |=C3F ;

    if ((mirrorColorAllocated & CF) == 0) {
        mirrorFloatRefColors[0] = new float[vertexCount * multiplier];
        mirrorColorAllocated |= CF;
    }
    if ((c4fAllocated & GeometryArray.WITH_ALPHA) == 0) {

        dstIndex = initialColorIndex * 3;
        for (i = initialColorIndex; i < validVertexCount; i++) {
      mirrorFloatRefColors[0][dstIndex++] = c3fRefColors[i].x;
      mirrorFloatRefColors[0][dstIndex++] = c3fRefColors[i].y;
      mirrorFloatRefColors[0][dstIndex++] = c3fRefColors[i].z;
        }
    } else {

        dstIndex = initialColorIndex * 4;
        for (i = initialColorIndex; i < validVertexCount; i++) {
      mirrorFloatRefColors[0][dstIndex++] = c3fRefColors[i].x;
      mirrorFloatRefColors[0][dstIndex++] = c3fRefColors[i].y;
      mirrorFloatRefColors[0][dstIndex++] = c3fRefColors[i].z;
      mirrorFloatRefColors[0][dstIndex++] = 1.0f;
        }
    }

    break;
      case C4F:
    if (c4fRefColors == null) {
        vertexType &= ~C4F;
        return;
    }
    vertexType |=C4F ;

    if ((mirrorColorAllocated & CF) == 0) {
        mirrorFloatRefColors[0] = new float[vertexCount << 2];
        mirrorColorAllocated |= CF;
    }

    dstIndex = initialColorIndex * 4;
    for (i = initialColorIndex; i < validVertexCount; i++) {
        mirrorFloatRefColors[0][dstIndex++] = c4fRefColors[i].x;
        mirrorFloatRefColors[0][dstIndex++] = c4fRefColors[i].y;
        mirrorFloatRefColors[0][dstIndex++] = c4fRefColors[i].z;
        mirrorFloatRefColors[0][dstIndex++] = c4fRefColors[i].w;
    }
    break;
      case C3UB:
    if (c3bRefColors == null) {
        vertexType &= ~C3UB;
        return;
    }
    vertexType |=C3UB ;

    if ((mirrorColorAllocated & CUB) == 0) {
        mirrorUnsignedByteRefColors[0] =
      new byte[vertexCount * multiplier];
        mirrorColorAllocated |= CUB;
    }
    if ((c4fAllocated & GeometryArray.WITH_ALPHA) == 0) {
        dstIndex = initialColorIndex * 3;
        for (i = initialColorIndex; i < validVertexCount; i++) {
      mirrorUnsignedByteRefColors[0][dstIndex++] = c3bRefColors[i].x;
      mirrorUnsignedByteRefColors[0][dstIndex++] = c3bRefColors[i].y;
      mirrorUnsignedByteRefColors[0][dstIndex++] = c3bRefColors[i].z;
        }
    } else {
        dstIndex = initialColorIndex * 4;
        for (i = initialColorIndex; i < validVertexCount; i++) {
      mirrorUnsignedByteRefColors[0][dstIndex++] = c3bRefColors[i].x;
      mirrorUnsignedByteRefColors[0][dstIndex++] = c3bRefColors[i].y;
      mirrorUnsignedByteRefColors[0][dstIndex++] = c3bRefColors[i].z;
      mirrorUnsignedByteRefColors[0][dstIndex++] = (byte)255;
        }
    }
    break;
      case C4UB:
    if (c4bRefColors == null) {
        vertexType &= ~C4UB;
        return;
    }
    vertexType |=C4UB ;
    if ((mirrorColorAllocated & CUB) == 0) {
        mirrorUnsignedByteRefColors[0] = new byte[vertexCount << 2];
        mirrorColorAllocated |= CUB;
    }

    dstIndex = initialColorIndex * 4;
    for (i = initialColorIndex; i < validVertexCount; i++) {
        mirrorUnsignedByteRefColors[0][dstIndex++] = c4bRefColors[i].x;
        mirrorUnsignedByteRefColors[0][dstIndex++] = c4bRefColors[i].y;
        mirrorUnsignedByteRefColors[0][dstIndex++] = c4bRefColors[i].z;
        mirrorUnsignedByteRefColors[0][dstIndex++] = c4bRefColors[i].w;
    }
    break;
      default:
    break;
      }
  }
  else //USE_NIO_BUFFER is set
      ifcolorRefBuffer == null) {
    if (c4fAllocated == 0 && !force &&
        (vertexType & COLOR_DEFINED) == CF) {
        mirrorFloatRefColors[0] = null;
        mirrorColorAllocated &= ~CF;
    }
    vertexType &= ~CF;

    if (c4fAllocated == 0 && !force &&
        ((vertexType & COLOR_DEFINED) == CUB) ) {
        mirrorUnsignedByteRefColors[0] = null;
        mirrorColorAllocated &= ~CUB;
    }
    vertexType &= ~CUB;
    return;

      } else if( floatBufferRefColors != null) {
    vertexType |= CF;
    vertexType &= ~CUB;
    if (c4fAllocated == 0 && !force) {
        // NOTE: make suren mirrorFloatRefColors[0] is set right
        mirrorFloatRefColors[0] = null;
        mirrorColorAllocated &= ~CF;
    }
    else {
        if ((mirrorColorAllocated & CF) == 0) {
      mirrorFloatRefColors[0] = new float[4 * vertexCount];
      mirrorColorAllocated |= CF;
        }
        floatBufferRefColors.rewind();
        if ((vertexFormat & GeometryArray.WITH_ALPHA) == 0) {
      srcIndex = initialColorIndex * 3;
      dstIndex = initialColorIndex * 4;
      floatBufferRefColors.position(srcIndex);

      for (i = initialColorIndex; i < validVertexCount; i++) {
          floatBufferRefColors.get(mirrorFloatRefColors[0], dstIndex, 3);
          mirrorFloatRefColors[0][dstIndex+3] = 1.0f;
          dstIndex += 4;
      }
        }
        else {

      srcIndex = initialColorIndex * 4;
      dstIndex = initialColorIndex * 4;
      floatBufferRefColors.position(srcIndex);
      for (i = initialColorIndex; i < validVertexCount; i++) {
          floatBufferRefColors.get(mirrorFloatRefColors[0], dstIndex, 4);
          dstIndex+= 4;
      }
        }
    }
      } else if ( byteBufferRefColors != null) {
    vertexType |= CUB;
    vertexType &= ~CF;
    if (c4fAllocated == 0 && !force) {
        // NOTE: make sure mirrorUnsignedByteRefColors[0] is set right
        mirrorUnsignedByteRefColors[0] = null;
        mirrorColorAllocated &= ~CUB;;
    }
    else {
        if ((mirrorColorAllocated & CUB) == 0) {
      mirrorUnsignedByteRefColors[0] = new byte[4 * vertexCount];
      mirrorColorAllocated |= CUB;
        }

        byteBufferRefColors.rewind();
        if ((vertexFormat & GeometryArray.WITH_ALPHA) == 0) {
      srcIndex = initialColorIndex * 3;
      dstIndex = initialColorIndex * 4;
      byteBufferRefColors.position(srcIndex);
      for (i = initialColorIndex; i < validVertexCount; i++) {
          byteBufferRefColors.get(mirrorUnsignedByteRefColors[0],
                dstIndex, 3);
          mirrorUnsignedByteRefColors[0][dstIndex+3] = (byte)(255.0);
          dstIndex += 4;
      }
        }
        else {
      srcIndex = initialColorIndex * 4;
      dstIndex = initialColorIndex * 4;
      byteBufferRefColors.position(srcIndex);
      for (i = initialColorIndex; i < validVertexCount; i++) {
          byteBufferRefColors.get(mirrorUnsignedByteRefColors[0], dstIndex, 4);
          dstIndex+= 4;
      }
        }
    } // end of else
      }//end of else if ( byteBufferRefColors != null)
  }//end of NIO BUFFER case

  colorChanged = 0xffff;
    }


    void setupMirrorNormalPointer(int ntype) {
  int i, index;

  switch (ntype) {
  case NF:
      if (floatRefNormals == null) {
    if ((vertexType & NORMAL_DEFINED) == NF) {
        vertexType &= ~NF;
        mirrorFloatRefNormals = null;
        mirrorNormalAllocated = false;
    }
      }
      else {
    vertexType |= NF;
    mirrorFloatRefNormals = floatRefNormals;
    mirrorNormalAllocated = false;
      }
      break;
  case N3F:
      if (v3fRefNormals == null) {
    if ((vertexType & NORMAL_DEFINED) == N3F) {
        vertexType &= ~N3F;
    }
    return;
      }
      else {
    vertexType |= N3F;
      }
      if (!mirrorNormalAllocated) {
    mirrorFloatRefNormals = new float[vertexCount * 3];
    mirrorNormalAllocated = true;
      }

      index = initialNormalIndex * 3;
      for (i = initialNormalIndex; i < validVertexCount; i++) {
    mirrorFloatRefNormals[index++] = v3fRefNormals[i].x;
    mirrorFloatRefNormals[index++] = v3fRefNormals[i].y;
    mirrorFloatRefNormals[index++] = v3fRefNormals[i].z;
      }
      break;
  default:
      break}
    }

    void setupMirrorTexCoordPointer(int type) {
  for (int i = 0; i < texCoordSetCount; i++) {
       doSetupMirrorTexCoordPointer(i, type);
  }

        validateTexCoordPointerType();
    }

    void setupMirrorTexCoordPointer(int texCoordSet, int type) {
        doSetupMirrorTexCoordPointer(texCoordSet, type);
        validateTexCoordPointerType();
    }

    // If all texCoord pointers are set to a non-null value, then set the
    // texcoord type in the vertexType flag word, else clear the texcoord type
    private void validateTexCoordPointerType() {
        boolean allNonNull = true;
        boolean allNull = true;
        for (int i = 0; i < texCoordSetCount; i++) {
            if (refTexCoords[i] == null) {
                allNonNull = false;
            } else {
                allNull = false;
            }
        }

        // Reset texCoordType if all references are null
        if (allNull) {
            texCoordType = 0;
        }

        // Copy texCoordType to vertexType if all references are non-null
        vertexType &= ~TEXCOORD_DEFINED;
        if (allNonNull) {
            vertexType |= texCoordType;
        }
    }

    private void doSetupMirrorTexCoordPointer(int texCoordSet, int type) {
  int i, index;

        switch (type) {
  case TF:
            texCoordType = TF;
            mirrorRefTexCoords[texCoordSet] = refTexCoords[texCoordSet];
      break;

        case T2F:
            texCoordType = T2F;
      t2fRefTexCoords = (TexCoord2f[])refTexCoords[texCoordSet];

      if (t2fRefTexCoords == null) {
                mirrorRefTexCoords[texCoordSet] = null;
    break;
      }

            mirrorFloatRefTexCoords = (float[])mirrorRefTexCoords[texCoordSet];
            if (mirrorFloatRefTexCoords != null) {
                if (mirrorFloatRefTexCoords.length < (vertexCount * 2))
                    mirrorRefTexCoords[texCoordSet] =
                        mirrorFloatRefTexCoords = new float[vertexCount * 2];
            }
            else {
                mirrorRefTexCoords[texCoordSet] =
                        mirrorFloatRefTexCoords = new float[vertexCount * 2];
            }

      index = initialTexCoordIndex[texCoordSet] * 2;
      for (i = initialTexCoordIndex[texCoordSet]; i < validVertexCount; i++) {
    mirrorFloatRefTexCoords[index++] = t2fRefTexCoords[i].x;
    mirrorFloatRefTexCoords[index++] = t2fRefTexCoords[i].y;
      }
      break;

  case T3F:
            texCoordType = T3F;
      t3fRefTexCoords = (TexCoord3f[])refTexCoords[texCoordSet];

            if (t3fRefTexCoords == null) {
                mirrorRefTexCoords[texCoordSet] = null;
    break;
      }

            mirrorFloatRefTexCoords = (float[])mirrorRefTexCoords[texCoordSet];
            if (mirrorFloatRefTexCoords != null) {
                if (mirrorFloatRefTexCoords.length < (vertexCount * 3))
                    mirrorRefTexCoords[texCoordSet] =
                        mirrorFloatRefTexCoords = new float[vertexCount * 3];
            }
            else {
                mirrorRefTexCoords[texCoordSet] =
                    mirrorFloatRefTexCoords = new float[vertexCount * 3];
            }

      index =  initialTexCoordIndex[texCoordSet] * 3;
      for (i = initialTexCoordIndex[texCoordSet]; i < validVertexCount; i++) {
    mirrorFloatRefTexCoords[index++] = t3fRefTexCoords[i].x;
    mirrorFloatRefTexCoords[index++] = t3fRefTexCoords[i].y;
    mirrorFloatRefTexCoords[index++] = t3fRefTexCoords[i].z;
      }
      break;

  default:
      break;
  }
    }

    void setupMirrorVertexAttrPointer(int type) {
        for (int i = 0; i < vertexAttrCount; i++) {
            doSetupMirrorVertexAttrPointer(i, type);
        }

        validateVertexAttrPointerType();
    }

    void setupMirrorVertexAttrPointer(int vertexAttrNum, int type) {
        doSetupMirrorVertexAttrPointer(vertexAttrNum, type);
        validateVertexAttrPointerType();
    }

    // If all vertex attr pointers are set to a non-null value, then set the
    // vertex attr type in the vertexType flag word, else clear the
    // vertex attr type
    private void validateVertexAttrPointerType() {
        boolean allNonNull = true;
        boolean allNull = true;

        if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) {
            for (int i = 0; i < vertexAttrCount; i++) {
                if (floatRefVertexAttrs[i] == null) {
                    allNonNull = false;
                } else {
                    allNull = false;
                }
            }
        } else {
            for (int i = 0; i < vertexAttrCount; i++) {
                if (floatBufferRefVertexAttrs[i] == null) {
                    allNonNull = false;
                } else {
                    allNull = false;
                }
            }
        }

        // Reset vertexAttrType if all references are null
        if (allNull) {
            vertexAttrType = 0;
        }

        // Copy vertexAttrType to vertexType if all references are non-null
        vertexType &= ~VATTR_DEFINED;
        if (allNonNull) {
            vertexType |= vertexAttrType;
        }
    }

    private void doSetupMirrorVertexAttrPointer(int vertexAttrNum, int type) {
        switch (type) {
        case AF:
            vertexAttrType = AF;
            mirrorFloatRefVertexAttrs[vertexAttrNum] =
                floatRefVertexAttrs[vertexAttrNum];
            break;
        default:
            break;
        }
    }


    void createGeometryArrayData(int vertexCount, int vertexFormat) {
  if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
      createGeometryArrayData(vertexCount, vertexFormat, 1,
            defaultTexCoordSetMap);
  } else {
      createGeometryArrayData(vertexCount, vertexFormat, 0, null);
  }
    }

    void createGeometryArrayData(int vertexCount, int vertexFormat,
         int texCoordSetCount, int[] texCoordSetMap) {

  createGeometryArrayData(vertexCount, vertexFormat,
        texCoordSetCount, texCoordSetMap,
        0, null);
    }

    void createGeometryArrayData(int vertexCount, int vertexFormat,
         int texCoordSetCount, int[] texCoordSetMap,
         int vertexAttrCount, int[] vertexAttrSizes) {
  this.vertexFormat = vertexFormat;
  this.vertexCount = vertexCount;
  this.validVertexCount = vertexCount;

  this.texCoordSetCount = texCoordSetCount;
  if (texCoordSetMap == null) {
      this.texCoordSetMap = null;
  }
  else {
      this.texCoordSetMap = Arrays.copyOf(texCoordSetMap, texCoordSetMap.length);
  }

        this.vertexAttrCount = vertexAttrCount;
  if (vertexAttrSizes == null) {
      this.vertexAttrSizes = null;
  }
  else {
      this.vertexAttrSizes = Arrays.copyOf(vertexAttrSizes, vertexAttrSizes.length);
  }

        this.vertexAttrStride = this.vertexAttrStride();
  this.stride = this.stride();

  this.vertexAttrOffsets = this.vertexAttrOffsets();
  this.texCoordSetMapOffset = this.texCoordSetMapOffset();
  this.textureOffset = this.textureOffset();
  this.colorOffset = this.colorOffset();
  this.normalOffset = this.normalOffset();
  this.coordinateOffset = this.coordinateOffset();

  if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
      this.vertexData = new float[this.vertexCount * this.stride];
  }
  else { // By reference geometry
      this.vertexData = null;
            if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
                this.mirrorRefTexCoords = new Object[texCoordSetCount];
                this.refTexCoords = new Object[texCoordSetCount]; // keep J3DBufferImp object in nio buffer case
    if((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0 )
        this.refTexCoordsBuffer = new J3DBuffer[texCoordSetCount]; // keep J3DBuffer object
      }
            if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
                this.floatRefVertexAttrs = new float[vertexAttrCount][];
                this.mirrorFloatRefVertexAttrs = new float[vertexAttrCount][];
    if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) {
        this.vertexAttrsRefBuffer = new J3DBuffer[vertexAttrCount];
                    this.floatBufferRefVertexAttrs = new FloatBuffer[vertexAttrCount];
                }
      }
  }
        if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
            this.initialTexCoordIndex = new int[texCoordSetCount];
        }
        if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
            this.initialVertexAttrIndex = new int[vertexAttrCount];
        }
  noAlpha = ((vertexFormat & GeometryArray.WITH_ALPHA) == 0);
  lastAlpha[0] = 1.0f;

    }

    void setVertexFormat(boolean useAlpha, boolean ignoreVC, Context ctx) {
  Pipeline.getPipeline().setVertexFormat(ctx,
                this, vertexFormat, useAlpha, ignoreVC);
    }

    float[] updateAlphaInFloatRefColors(Canvas3D cv, int screen, float alpha) {

  //System.err.println("updateAlphaInFloatRefColors  screen = " + screen +
  //       " alpha " + alpha );

      // update alpha only if vertex format includes alpha
  if (((vertexFormat | c4fAllocated) & GeometryArray.WITH_ALPHA) == 0)
      return mirrorFloatRefColors[0];

  // if alpha is smaller than EPSILON, set it to EPSILON, so that
  // even if alpha is equal to 0, we will not completely lose
  // the original alpha value
  if (alpha <= EPSILON) {
      alpha = (float)EPSILON;
  }

        assert lastAlpha != null;
        assert mirrorFloatRefColors != null;
        assert mirrorFloatRefColors.length == lastAlpha.length;

  // Issue 113 - reallocate lastAlpha array if needed, but no need to
        // update the values here
  if (lastAlpha.length <= screen) {
      float[] la = new float[screen + 1];
      for (int i = 0; i < lastAlpha.length; i++) {
    la[i] = lastAlpha[i];
      }
      lastAlpha = la;
  }

  //System.err.println("updateAlphaInFloatRefColors screen is " + screen
  //         + " mirrorFloatRefColors.length " +
  //         mirrorFloatRefColors.length);

  // allocate a copy of the color data for the screen if needed.
  // this piece of code is only for multi-screens case
  if (mirrorFloatRefColors.length <= screen) {
      float[][] cfData = new float[screen + 1][];

      for (int i = 0; i < mirrorFloatRefColors.length; i++) {
    cfData[i] = mirrorFloatRefColors[i];
      }

            // Issue 113 - allocate entries for [oldSize..screen];
            // copy cfData[0] to cfData[oldsize..screen-1] and
            // lastAlpha[0] to lastAlpha[oldsize..screen-1].
            for (int i = mirrorFloatRefColors.length; i < screen+1; i++) {
                cfData[i] = new float[4 * vertexCount];
                System.arraycopy(cfData[0], 0, cfData[i], 0, 4 * vertexCount);
                lastAlpha[i] = lastAlpha[0];
            }

            mirrorFloatRefColors = cfData;

            // Issue 113 - since we copied the data from screen 0, we don't need
            // to do any further special processing.
  }

        assert lastAlpha[screen] >= 0.0;
  /*
    System.err.println("updateAlphaInFloatRefColors ** : lastAlpha[screen] " +
           lastAlpha[screen]);

    System.err.println("((colorChanged & (1<<screen)) == 0) " +
           ((colorChanged & (1<<screen)) == 0));
  */

  if ((colorChanged & (1<<screen)) == 0) {
      // color data is not modified
      if (Math.abs(lastAlpha[screen] - alpha) <= EPSILON) {
    // and if alpha is the same as the last one,
    // just return the data
    //System.err.println("updateAlphaInFloatRefColors 0 : alpha is the same as the last one " + alpha);

          return mirrorFloatRefColors[screen];
      } else {

    // if alpha is different, update the alpha values
    //System.err.println("updateAlphaInFloatRefColors 1 : alpha is different, update the alpha values " + alpha);

    float m = alpha / lastAlpha[screen];

    float[] cdata = mirrorFloatRefColors[screen];

    // We've to traverse the whole due to BugId : 4676483
    for (int i = 0, j = 0; i < vertexCount; i++, j+=4) {
            cdata[j+3] = cdata[j+3] * m;
    }
      }
  } else {
      // color data is modified
      if (screen == 0) {

    // just update alpha values since screen 0 data is
    // already updated in setupMirrorColorPointer

    //System.err.println("updateAlphaInFloatRefColors 2 : just update alpha = " + alpha);

    float[] cdata = mirrorFloatRefColors[screen];


    // This part is also incorrect due to BugId : 4676483
    // But traversing the whole array doesn't help either, as there
    // isn't a mechanism to indicate the the alpha component has
    // not changed by user.
    int j = initialColorIndex * 4;
    for (int i = initialColorIndex; i < validVertexCount; i++, j+=4) {
            cdata[j+3] = cdata[j+3] * alpha;
    }
      } else {
    // update color values from screen 0 data
    //System.err.println("updateAlphaInFloatRefColors 3 : update color values from screen 0 data " + alpha);

    float m;

    if ((colorChanged & 1) == 0) {
        // alpha is up to date in screen 0
        m = alpha / lastAlpha[0];
    } else {
        m = alpha;
    }

    float[] sdata = mirrorFloatRefColors[0];
    float[] cdata = mirrorFloatRefColors[screen];

    int j = initialColorIndex * 4;
    for (int i = initialColorIndex; i < validVertexCount; i++) {
        cdata[j] = sdata[j++];
        cdata[j] = sdata[j++];
        cdata[j] = sdata[j++];
        cdata[j] = sdata[j++] * m;
    }
      }
  }

  lastAlpha[screen] = alpha;
  colorChanged &= ~(1 << screen);
  dirtyFlag |= COLOR_CHANGED;
  return mirrorFloatRefColors[screen];
    }


    byte[] updateAlphaInByteRefColors(Canvas3D cv, int screen, float alpha) {

  /*
    System.err.println("updateAlphaInByteRefColors  screen = " + screen +
    " alpha " + alpha );
  */

  // update alpha only if vertex format includes alpha
  if (((vertexFormat | c4fAllocated) & GeometryArray.WITH_ALPHA) == 0)
      return mirrorUnsignedByteRefColors[0];

  // if alpha is smaller than EPSILON, set it to EPSILON, so that
  // even if alpha is equal to 0, we will not completely lose
  // the original alpha value
  if (alpha <= EPSILON) {
      alpha = (float)EPSILON;
  }

        assert lastAlpha != null;
        assert mirrorUnsignedByteRefColors != null;
        assert mirrorUnsignedByteRefColors.length == lastAlpha.length;

  // Issue 113 - reallocate lastAlpha array if needed, but no need to
        // update the values here
  if (lastAlpha.length <= screen) {
      float[] la = new float[screen + 1];
      for (int i = 0; i < lastAlpha.length; i++) {
    la[i] = lastAlpha[i];
      }
      lastAlpha = la;
  }

  // allocate a copy of the color data for the screen if needed.
  // this piece of code is only for multi-screens case
  if (mirrorUnsignedByteRefColors.length <= screen) {
      byte[][] cbData = new byte[screen + 1][];
      for (int i = 0; i < mirrorUnsignedByteRefColors.length; i++) {
    cbData[i] = mirrorUnsignedByteRefColors[i];
      }

            // Issue 113 - allocate entries for [oldSize..screen];
            // copy cbData[0] to cbData[oldsize..screen-1] and
            // lastAlpha[0] to lastAlpha[oldsize..screen-1].
            for (int i = mirrorUnsignedByteRefColors.length; i < screen+1; i++) {
                cbData[i] = new byte[4 * vertexCount];
                System.arraycopy(cbData[0], 0, cbData[i], 0, 4 * vertexCount);
                lastAlpha[i] = lastAlpha[0];
            }

            mirrorUnsignedByteRefColors = cbData;

            // Issue 113 - since we copied the data from screen 0, we don't need
            // to do any further special processing.
  }

        assert lastAlpha[screen] >= 0.0;
        /*
  System.err.println("updateAlphaInByteRefColors ## : lastAlpha[screen] " +
         lastAlpha[screen]);

  System.err.println("((colorChanged & (1<<screen)) == 0) " +
         ((colorChanged & (1<<screen)) == 0));
  */

        if ((colorChanged & (1<<screen)) == 0) {
            // color data is not modified
            if (Math.abs(lastAlpha[screen] - alpha) <= EPSILON) {
                // and if alpha is the same as the last one,
                // just return the data
    //System.err.println("updateAlphaInByteRefColors 0 : alpha is the same as the last one " + alpha);

                return mirrorUnsignedByteRefColors[screen];
            } else {
                // if alpha is different, update the alpha values

    //System.err.println("updateAlphaInByteRefColors 1 : alpha is different, update the alpha values " + alpha);

                float m = alpha / lastAlpha[screen];

                byte[] cdata = mirrorUnsignedByteRefColors[screen];

    // We've to traverse the whole due to BugId : 4676483
    for (int i = 0, j = 0; i < vertexCount; i++, j+=4) {
                    cdata[j+3] = (byte)( ((int)cdata[j+3] & 0xff) * m);
                }
            }
        } else {
            // color data is modified
            if (screen == 0) {
    //System.err.println("updateAlphaInByteRefColors 2 : just update alpha =" + alpha);

                // just update alpha values since screen 0 data is
                // already updated in setupMirrorColorPointer

                byte[] cdata = mirrorUnsignedByteRefColors[screen];

    // This part is also incorrect due to BugId : 4676483
    // But traversing the whole array doesn't help either, as there
    // isn't a mechanism to indicate the the alpha component has
    // not changed by user.
    int j = initialColorIndex * 4;
    for (int i = initialColorIndex; i < validVertexCount; i++, j+=4) {
                    cdata[j+3] = (byte)(((int)cdata[j+3] & 0xff) * alpha);
                }
            } else {
                // update color values from screen 0 data
                float m;

    //System.err.println("updateAlphaInByteRefColors 3 : update color values from screen 0 data " + alpha);

                if ((colorChanged & 1) == 0) {
                    // alpha is up to date in screen 0
                    m = alpha / lastAlpha[0];
                } else {
                    m = alpha;
                }
                byte[] sdata = mirrorUnsignedByteRefColors[0];
                byte[] cdata = mirrorUnsignedByteRefColors[screen];

    int j = initialColorIndex * 4;
    for (int i = initialColorIndex; i < validVertexCount; i++) {
        cdata[j] = sdata[j++];
        cdata[j] = sdata[j++];
        cdata[j] = sdata[j++];
        cdata[j] = (byte)(((int)sdata[j++]& 0xff) * m);
                }
            }
        }

        lastAlpha[screen] = alpha;
        colorChanged &= ~(1 << screen);
  dirtyFlag |= COLOR_CHANGED;
        return mirrorUnsignedByteRefColors[screen];
    }


    Object[] updateAlphaInVertexData(Canvas3D cv, int screen, float alpha) {

  Object[] retVal = new Object[2];
  retVal[0] = Boolean.FALSE;

  // update alpha only if vertex format includes alpha
  if ((vertexFormat & GeometryArray.COLOR) == 0) {
      retVal[1] = vertexData;
      return retVal;
  }

  // if alpha is smaller than EPSILON, set it to EPSILON, so that
  // even if alpha is equal to 0, we will not completely lose
  // the original alpha value
  if (alpha <= EPSILON) {
      alpha = (float)EPSILON;
  }
  retVal[0] = Boolean.TRUE;

        assert lastAlpha != null;
        assert mvertexData == null || mvertexData.length == lastAlpha.length;

  // Issue 113 - reallocate lastAlpha array if needed, but no need to
        // update the values here
  if (lastAlpha.length <= screen) {
      float[] la = new float[screen + 1];
      for (int i = 0; i < lastAlpha.length; i++) {
    la[i] = lastAlpha[i];
      }
      lastAlpha = la;
  }

  // allocate a copy of the vertex data for the screen if needed.
  // Note that a copy operation only happens in the multi-screens case.
        // We always use the existing vertexData for screen 0.
  if (mvertexData == null || mvertexData.length <= screen) {

      float[][] cfData = new float[screen + 1][];
            int oldSize = 1;

      if (mvertexData != null) {
                oldSize = mvertexData.length;
          for (int i = 0; i < mvertexData.length; i++) {
        cfData[i] = mvertexData[i];
    }
      }

      if (cfData[0] == null)  {
    cfData[0] = vertexData;
      }

            // Issue 113 - allocate entries for [oldSize..screen];
            // copy cfData[0] to cfData[oldsize..screen-1] and
            // lastAlpha[0] to lastAlpha[oldsize..screen-1].
            if (screen > 0) {
                for (int i = oldSize; i < screen+1; i++) {
                    cfData[i] = new float[stride * vertexCount];
                    System.arraycopy(cfData[0], 0, cfData[i], 0,
                            stride * vertexCount);
                    lastAlpha[i] = lastAlpha[0];
                }
            }

            mvertexData = cfData;

            // Issue 113 - since we copied the data from screen 0, we don't need
            // to do any further special processing.
  }

        assert lastAlpha[screen] >= 0.0;

  if ((colorChanged & (1<<screen)) == 0) {
      // color data is not modified
      if (Math.abs(lastAlpha[screen] - alpha) <= EPSILON) {
    // and if alpha is the same as the last one,
    // just return the data
    retVal[1] = mvertexData[screen];
          return retVal;
      } else {
    // if alpha is different, update the alpha values
    float m = alpha / lastAlpha[screen];

    float[] cdata = mvertexData[screen];
    for (int i = 0, j = colorOffset; i < vertexCount;
          i++, j+=stride) {
            cdata[j+3] *= m;
    }
      }
  } else {
      // color data is modified
      if (screen == 0) {
    // just update alpha values since screen 0 data is
    // already updated in setupMirrorColorPointer

    float[] cdata = mvertexData[screen];
    double m = alpha / lastAlpha[0];

    for (int i = 0, j = colorOffset; i < vertexCount;
          i++, j+=stride) {
            cdata[j+3] *= m;
    }
      } else {
    // update color values from screen 0 data

    float m = alpha / lastAlpha[0];
    float[] sdata = mvertexData[0];
    float[] cdata = mvertexData[screen];

    for (int i = 0, j = colorOffset; i < vertexCount;
          i++, j+=stride) {
        System.arraycopy(sdata, j, cdata, j, 3);
        cdata[j+3] = sdata[j+3] * m;
    }
      }
  }

  lastAlpha[screen] = alpha;
  colorChanged &= ~(1 << screen);
  dirtyFlag |= COLOR_CHANGED;
  retVal[1] = mvertexData[screen];
  return retVal;
    }

    Object[] updateAlphaInInterLeavedData(Canvas3D cv, int screen, float alpha) {

  Object[] retVal = new Object[2];
  retVal[0] = Boolean.FALSE;

  // update alpha only if vertex format includes alpha
  if (((vertexFormat | c4fAllocated) & GeometryArray.WITH_ALPHA) == 0) {
      retVal[1] = mirrorInterleavedColorPointer[0];
      return retVal;
  }
        int coffset = initialColorIndex << 2; // Each color is 4 floats

  // if alpha is smaller than EPSILON, set it to EPSILON, so that
  // even if alpha is equal to 0, we will not completely lose
  // the original alpha value
  if (alpha <= EPSILON) {
      alpha = (float)EPSILON;
  }
  retVal[0] = Boolean.TRUE;

        assert lastAlpha != null;
        assert mirrorInterleavedColorPointer != null;
        assert mirrorInterleavedColorPointer.length == lastAlpha.length;

  // Issue 113 - reallocate lastAlpha array if needed, but no need to
        // update the values here
  if (lastAlpha.length <= screen) {
      float[] la = new float[screen + 1];
      for (int i = 0; i < lastAlpha.length; i++) {
    la[i] = lastAlpha[i];
      }
      lastAlpha = la;
  }

  // allocate a copy of the vertex data for the screen if needed.
  // this piece of code is only for multi-screens case
  if (mirrorInterleavedColorPointer.length <= screen) {

      float[][] cfData = new float[screen + 1][];

      for (int i = 0; i < mirrorInterleavedColorPointer.length; i++) {
    cfData[i] = mirrorInterleavedColorPointer[i];
      }

            // Issue 113 - allocate entries for [oldSize..screen];
            // copy cfData[0] to cfData[oldsize..screen-1] and
            // lastAlpha[0] to lastAlpha[oldsize..screen-1].
            for (int i = mirrorInterleavedColorPointer.length; i < screen+1; i++) {
                cfData[i] = new float[4 * vertexCount];
                System.arraycopy(cfData[0], 0, cfData[i], 0, 4 * vertexCount);
                lastAlpha[i] = lastAlpha[0];
            }

      mirrorInterleavedColorPointer = cfData;

            // Issue 113 - since we copied the data from screen 0, we don't need
            // to do any further special processing.
  }

        assert lastAlpha[screen] >= 0.0;

        if ((colorChanged & (1<<screen)) == 0) {
      // color data is not modified
      if (Math.abs(lastAlpha[screen] - alpha) <= EPSILON) {
    // and if alpha is the same as the last one,
    // just return the data
    retVal[1] = mirrorInterleavedColorPointer[screen];
          return retVal;
      } else {

    // if alpha is different, update the alpha values

    float m = alpha / lastAlpha[screen];

    float[] cdata = mirrorInterleavedColorPointer[screen];

    coffset = initialColorIndex << 2;
    for (int i = coffset; i < coffset + (vertexCount << 2); i+=4) {
            cdata[i+3] = cdata[i+3] * m;
    }
      }
  } else {
      // color data is modified
      if (screen == 0) {

    // just update alpha values since screen 0 data is
    // already updated in setupMirrorInterleavedColorPointer

    float[] cdata = mirrorInterleavedColorPointer[screen];

    for (int i = coffset; i < coffset + (vertexCount << 2); i+=4) {
            cdata[i+3] = cdata[i+3] * alpha;
    }
      } else {
    // update color values from screen 0 data

    float m;

    if ((colorChanged & 1) == 0) {
        // alpha is up to date in screen 0
        m = alpha / lastAlpha[0];
    } else {
        m = alpha;
    }

    float[] sdata = mirrorInterleavedColorPointer[0];
    float[] cdata = mirrorInterleavedColorPointer[screen];

    for (int i = coffset; i < coffset + (vertexCount << 2);) {
        // System.arraycopy(sdata, i, cdata, i, 3);
        cdata[i] = sdata[i++];
        cdata[i] = sdata[i++];
        cdata[i] = sdata[i++];
        cdata[i] = sdata[i++] * m;
    }
      }
  }

  lastAlpha[screen] = alpha;
  colorChanged &= ~(1 << screen);
  dirtyFlag |= COLOR_CHANGED;
  retVal[1] = mirrorInterleavedColorPointer[screen];
  return retVal;
    }


    // pass < 0  implies underlying library supports multiTexture, so
    //      use the multiTexture extension to send all texture units
    //     data in one pass
    // pass >= 0 implies one pass for one texture unit state

    @Override
    void execute(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale,
     boolean updateAlpha, float alpha,
     int screen,
                 boolean ignoreVertexColors) {

  int cdirty;
  boolean useAlpha = false;
  Object[] retVal;

        // Check for by-copy case
  if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
            float[] vdata;

      synchronized (this) {
    cdirty = dirtyFlag;
    if (updateAlpha && !ignoreVertexColors) {
        // update the alpha values
        retVal = updateAlphaInVertexData(cv, screen, alpha);
        useAlpha = (retVal[0] == Boolean.TRUE);
        vdata = (float[])retVal[1];

        // D3D only
        if (alpha != lastScreenAlpha) {
      // handle multiple screen case
      lastScreenAlpha = alpha;
      cdirty |= COLOR_CHANGED;
        }
    } else {
        vdata = vertexData;
        // if transparency switch between on/off
        if (lastScreenAlpha != -1) {
      lastScreenAlpha = -1;
      cdirty |= COLOR_CHANGED;
        }
    }
    // geomLock is get in MasterControl when
    // RenderBin render the geometry. So it is safe
    // just to set the dirty flag here
    dirtyFlag = 0;
      }

      Pipeline.getPipeline().execute(cv.ctx,
                    this, geoType, isNonUniformScale,
        useAlpha,
        ignoreVertexColors,
        initialVertexIndex,
         validVertexCount,
        ((vertexFormat & GeometryArray.COLOR) != 0)?(vertexFormat|GeometryArray.COLOR_4):vertexFormat,
                    texCoordSetCount, texCoordSetMap,
                    (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
                    texCoordSetMapOffset,
        cv.numActiveTexUnit,
                    vertexAttrCount, vertexAttrSizes,
                    vdata, null,
                    cdirty);
  }

  //By reference with java array
  else if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) {
      // interleaved data
      if ((vertexFormat & GeometryArray.INTERLEAVED) != 0) {
    if(interLeavedVertexData == null)
        return;

    float[] cdata = null;

    synchronized (this) {
        cdirty = dirtyFlag;
        if (updateAlpha && !ignoreVertexColors) {
      // update the alpha values
      retVal = updateAlphaInInterLeavedData(cv, screen, alpha);
      useAlpha = (retVal[0] == Boolean.TRUE);
      cdata = (float[])retVal[1];
      if (alpha != lastScreenAlpha) {
          lastScreenAlpha = alpha;
          cdirty |= COLOR_CHANGED;
      }
        } else {
      // if transparency switch between on/off
      if (lastScreenAlpha != -1) {
          lastScreenAlpha = -1;
          cdirty |= COLOR_CHANGED;
      }
        }
        dirtyFlag = 0;
    }

    Pipeline.getPipeline().execute(cv.ctx,
                        this, geoType, isNonUniformScale,
      useAlpha,
      ignoreVertexColors,
      initialVertexIndex,
      validVertexCount,
      vertexFormat,
      texCoordSetCount, texCoordSetMap,
      (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
      texCoordSetMapOffset,
      cv.numActiveTexUnit,
                        vertexAttrCount, vertexAttrSizes,
                        interLeavedVertexData, cdata,
      cdirty);

      } // end of interleaved case

      // non interleaved data
      else {

    // Check if a vertexformat is set, but the array is null
    // if yes, don't draw anything
    if ((vertexType == 0) ||
        ((vertexType & VERTEX_DEFINED) == 0) ||
        (((vertexFormat & GeometryArray.COLOR) != 0) &&
         (vertexType & COLOR_DEFINED) == 0) ||
        (((vertexFormat & GeometryArray.NORMALS) != 0) &&
         (vertexType & NORMAL_DEFINED) == 0) ||
        (((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) &&
         (vertexType & VATTR_DEFINED) == 0) ||
        (((vertexFormat& GeometryArray.TEXTURE_COORDINATE) != 0) &&
         (vertexType & TEXCOORD_DEFINED) == 0)) {
        return;
    } else {
        byte[] cbdata = null;
        float[] cfdata = null;

        if ((vertexType & (CF | C3F | C4F )) != 0) {

      synchronized (this) {
          cdirty = dirtyFlag;
          if (updateAlpha && !ignoreVertexColors) {
        cfdata = updateAlphaInFloatRefColors(cv, screen, alpha);
        if (alpha != lastScreenAlpha) {
            lastScreenAlpha = alpha;
            cdirty |= COLOR_CHANGED;
        }
          } else {
        cfdata = mirrorFloatRefColors[0];
        // if transparency switch between on/off
        if (lastScreenAlpha != -1) {
            lastScreenAlpha = -1;
            cdirty |= COLOR_CHANGED;
        }

          }
          dirtyFlag = 0;
      }
        } // end of color in float format
        else if ((vertexType & (CUB| C3UB | C4UB)) != 0) {
      synchronized (this) {
          cdirty = dirtyFlag;
          if (updateAlpha && !ignoreVertexColors) {
        cbdata = updateAlphaInByteRefColors(cv, screen, alpha);
        if (alpha != lastScreenAlpha) {
            lastScreenAlpha = alpha;
            cdirty |= COLOR_CHANGED;
        }
          } else {
        cbdata = mirrorUnsignedByteRefColors[0];
        // if transparency switch between on/off
        if (lastScreenAlpha != -1) {
            lastScreenAlpha = -1;
            cdirty |= COLOR_CHANGED;
        }
          }
          dirtyFlag = 0;
      }
        } // end of color in byte format
        else {
      cdirty = dirtyFlag;
        }
        // setup vdefined to passed to native code
        int vdefined = 0;
        if((vertexType & (PF | P3F)) != 0)
      vdefined |= COORD_FLOAT;
        if((vertexType & (PD | P3D)) != 0)
      vdefined |= COORD_DOUBLE;
        if((vertexType & (CF | C3F | C4F)) != 0)
      vdefined |= COLOR_FLOAT;
        if((vertexType & (CUB| C3UB | C4UB)) != 0)
      vdefined |= COLOR_BYTE;
        if((vertexType & NORMAL_DEFINED) != 0)
      vdefined |= NORMAL_FLOAT;
        if((vertexType & VATTR_DEFINED) != 0)
      vdefined |= VATTR_FLOAT;
        if((vertexType & TEXCOORD_DEFINED) != 0)
      vdefined |= TEXCOORD_FLOAT;

                    Pipeline.getPipeline().executeVA(cv.ctx,
                            this, geoType, isNonUniformScale,
                            ignoreVertexColors,
                            validVertexCount,
                            (vertexFormat | c4fAllocated),
                            vdefined,
                            initialCoordIndex,
                            mirrorFloatRefCoords, mirrorDoubleRefCoords,
                            initialColorIndex, cfdata, cbdata,
                            initialNormalIndex, mirrorFloatRefNormals,
                            vertexAttrCount, vertexAttrSizes,
                            initialVertexAttrIndex, mirrorFloatRefVertexAttrs,
                            ((texCoordSetMap == null) ? 0:texCoordSetMap.length),
                            texCoordSetMap,
                            cv.numActiveTexUnit,
                            initialTexCoordIndex,texCoordStride,
                            mirrorRefTexCoords, cdirty);
    }// end of all vertex data being set
      }// end of non interleaved case
  }// end of by reference with java array

  //By reference with nio buffer
  else  {
      // interleaved data
      if ((vertexFormat & GeometryArray.INTERLEAVED) != 0) {

    if ( interleavedFloatBufferImpl == null)
        return;

    float[] cdata = null;
    synchronized (this) {
        cdirty = dirtyFlag;
        if (updateAlpha && !ignoreVertexColors) {
      // update the alpha values
      // XXXX: to handle alpha case
      retVal = updateAlphaInInterLeavedData(cv, screen, alpha);
      useAlpha = (retVal[0] == Boolean.TRUE);
      cdata = (float[])retVal[1];

      if (alpha != lastScreenAlpha) {
          lastScreenAlpha = alpha;
          cdirty |= COLOR_CHANGED;
      }
        } else {
      // XXXX: to handle alpha case
      cdata = null;
      // if transparency switch between on/off
      if (lastScreenAlpha != -1) {
          lastScreenAlpha = -1;
          cdirty |= COLOR_CHANGED;
      }
        }
        dirtyFlag = 0;
    }

                Pipeline.getPipeline().executeInterleavedBuffer(cv.ctx,
                        this, geoType, isNonUniformScale,
                        useAlpha,
                        ignoreVertexColors,
                        initialVertexIndex,
                        validVertexCount,
                        vertexFormat,
                        texCoordSetCount, texCoordSetMap,
                        (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
                        texCoordSetMapOffset,
                        cv.numActiveTexUnit,
                        interleavedFloatBufferImpl, cdata,
                        cdirty);

      } // end of interleaved case

      // non interleaved data
      else {

    // Check if a vertexformat is set, but the array is null
    // if yes, don't draw anything
    if ((vertexType == 0) ||
        ((vertexType & VERTEX_DEFINED) == 0) ||
        (((vertexFormat & GeometryArray.COLOR) != 0) &&
         (vertexType & COLOR_DEFINED) == 0) ||
        (((vertexFormat & GeometryArray.NORMALS) != 0) &&
         (vertexType & NORMAL_DEFINED) == 0) ||
        (((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) &&
         (vertexType & VATTR_DEFINED) == 0) ||
        (((vertexFormat& GeometryArray.TEXTURE_COORDINATE) != 0) &&
         (vertexType & TEXCOORD_DEFINED) == 0)) {
        return;
    } else {
        byte[] cbdata = null;
        float[] cfdata = null;

        if ((vertexType & CF ) != 0) {
      synchronized (this) {
          cdirty = dirtyFlag;
          if (updateAlpha && !ignoreVertexColors) {
        cfdata = updateAlphaInFloatRefColors(cv,
                     screen, alpha);
        if (alpha != lastScreenAlpha) {
            lastScreenAlpha = alpha;
            cdirty |= COLOR_CHANGED;
        }
          } else {
        // XXXX: handle transparency case
        //cfdata = null;
        cfdata = mirrorFloatRefColors[0];
        // if transparency switch between on/off
        if (lastScreenAlpha != -1) {
            lastScreenAlpha = -1;
            cdirty |= COLOR_CHANGED;
        }

          }
          dirtyFlag = 0;
      }
        } // end of color in float format
        else if ((vertexType & CUB) != 0) {
      synchronized (this) {
          cdirty = dirtyFlag;
          if (updateAlpha && !ignoreVertexColors) {
        cbdata = updateAlphaInByteRefColors(
                    cv, screen, alpha);
        if (alpha != lastScreenAlpha) {
            lastScreenAlpha = alpha;
            cdirty |= COLOR_CHANGED;
        }
          } else {
        // XXXX: handle transparency case
        //cbdata = null;
        cbdata = mirrorUnsignedByteRefColors[0];
        // if transparency switch between on/off
        if (lastScreenAlpha != -1) {
            lastScreenAlpha = -1;
            cdirty |= COLOR_CHANGED;
        }
          }
          dirtyFlag = 0;
      }
        } // end of color in byte format
        else {
      cdirty = dirtyFlag;
        }

        Buffer vcoord = null;
        Buffer cdataBuffer = null;
        FloatBuffer normal=null;

        int vdefined = 0;
        if((vertexType & PF!= 0) {
      vdefined |= COORD_FLOAT;
      vcoord = floatBufferRefCoords;
        } else if((vertexType & PD ) != 0) {
      vdefined |= COORD_DOUBLE;
      vcoord = doubleBufferRefCoords;
        }

        if((vertexType & CF ) != 0) {
      vdefined |= COLOR_FLOAT;
      cdataBuffer = floatBufferRefColors;
        } else if((vertexType & CUB) != 0) {
      vdefined |= COLOR_BYTE;
      cdataBuffer = byteBufferRefColors;
        }

        if((vertexType & NORMAL_DEFINED) != 0) {
      vdefined |= NORMAL_FLOAT;
      normal = floatBufferRefNormals;
                    }

                    if ((vertexType & VATTR_DEFINED) != 0) {
                        vdefined |= VATTR_FLOAT;
                    }

                    if((vertexType & TEXCOORD_DEFINED) != 0)
           vdefined |= TEXCOORD_FLOAT;

                    Pipeline.getPipeline().executeVABuffer(cv.ctx,
                            this, geoType, isNonUniformScale,
                            ignoreVertexColors,
                            validVertexCount,
                            (vertexFormat | c4fAllocated),
                            vdefined,
                            initialCoordIndex,
                            vcoord,
                            initialColorIndex,
                            cdataBuffer,
                            cfdata, cbdata,
                            initialNormalIndex,
                            normal,
                            vertexAttrCount, vertexAttrSizes,
                            initialVertexAttrIndex,
                            floatBufferRefVertexAttrs,
                            ((texCoordSetMap == null) ? 0:texCoordSetMap.length),
                            texCoordSetMap,
                            cv.numActiveTexUnit,
                            initialTexCoordIndex,texCoordStride,
                            refTexCoords, cdirty);
    }// end of all vertex data being set
      }// end of non interleaved case
  }// end of by reference with nio-buffer case
    }

    void buildGA(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale,
     boolean updateAlpha, float alpha, boolean ignoreVertexColors,
     Transform3D xform, Transform3D nxform) {

        float[] vdata = null;

        // NIO buffers are no longer supported in display lists
        assert (vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0;

  if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
      vdata = vertexData;
  }
  else if ((vertexFormat & GeometryArray.INTERLEAVED) != 0 &&
     ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0)) {
      vdata = interLeavedVertexData;
  }
  if (vdata != null) {
      /*
       System.err.println("calling native buildGA()");
       System.err.println("geoType = "+geoType+" initialVertexIndex = "+initialVertexIndex+" validVertexCount = "+validVertexCount+" vertexFormat = "+vertexFormat+"  vertexData = "+vertexData);
       */
      Pipeline.getPipeline().buildGA(cv.ctx,
                    this, geoType, isNonUniformScale,
        updateAlpha, alpha, ignoreVertexColors,
        initialVertexIndex,
        validVertexCount, vertexFormat,
        texCoordSetCount, texCoordSetMap,
        (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
        texCoordSetMapOffset,
                    vertexAttrCount, vertexAttrSizes,
                    (xform == null) ? null : xform.mat,
        (nxform == null) ? null : nxform.mat,
        vdata);
  }
  else {
            // Check if a vertexformat is set, but the array is null
            // if yes, don't draw anything
            if ((vertexType == 0) ||
    ((vertexType & VERTEX_DEFINED) == 0) ||
    (((vertexFormat & GeometryArray.COLOR) != 0) &&
     (vertexType & COLOR_DEFINED) == 0) ||
    (((vertexFormat & GeometryArray.NORMALS) != 0) &&
     (vertexType & NORMAL_DEFINED) == 0) ||
    (((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) &&
     (vertexType & VATTR_DEFINED) == 0) ||
    (((vertexFormat& GeometryArray.TEXTURE_COORDINATE) != 0) &&
     (vertexType & TEXCOORD_DEFINED) == 0)) {

                return;
            }

            // Either non-interleaved, by-ref or nio buffer
      if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) {
                // Java array case
        // setup vdefined to passed to native code
        int vdefined = 0;
        if((vertexType & (PF | P3F)) != 0)
      vdefined |= COORD_FLOAT;
        if((vertexType & (PD | P3D)) != 0)
      vdefined |= COORD_DOUBLE;
        if((vertexType & (CF | C3F | C4F)) != 0)
      vdefined |= COLOR_FLOAT;
        if((vertexType & (CUB| C3UB | C4UB)) != 0)
      vdefined |= COLOR_BYTE;
        if((vertexType & NORMAL_DEFINED) != 0)
      vdefined |= NORMAL_FLOAT;
        if((vertexType & VATTR_DEFINED) != 0)
      vdefined |= VATTR_FLOAT;
        if((vertexType & TEXCOORD_DEFINED) != 0)
      vdefined |= TEXCOORD_FLOAT;

        Pipeline.getPipeline().buildGAForByRef(cv.ctx,
                            this, geoType, isNonUniformScale,
          updateAlpha, alpha,
          ignoreVertexColors,
          validVertexCount,
          vertexFormat,
          vdefined,
          initialCoordIndex,
          mirrorFloatRefCoords, mirrorDoubleRefCoords,
          initialColorIndex, mirrorFloatRefColors[0], mirrorUnsignedByteRefColors[0],
          initialNormalIndex, mirrorFloatRefNormals,
          vertexAttrCount, vertexAttrSizes,
                            initialVertexAttrIndex, mirrorFloatRefVertexAttrs,
          ((texCoordSetMap == null) ? 0:texCoordSetMap.length),
          texCoordSetMap,
          initialTexCoordIndex,texCoordStride,
          mirrorRefTexCoords,
          (xform == null) ? null : xform.mat,
          (nxform == null) ? null : nxform.mat);
      }
            /*
            // NOTE: NIO buffers are no longer supported in display lists.
            // This was never enabled by default anyway (only when the
            // optimizeForSpace property was set to false), so it wasn't
            // well-tested. If future support is desired, we will need to
            // add vertex attributes to buildGAForBuffer. There are no plans
            // to ever do this.
      else {
                // NIO Buffer case
                Object vcoord = null, cdataBuffer=null, normal=null;

                int vdefined = 0;
                if((vertexType & PF)  != 0) {
                    vdefined |= COORD_FLOAT;
                    vcoord = floatBufferRefCoords.getBufferAsObject();
                } else if((vertexType & PD ) != 0) {
                    vdefined |= COORD_DOUBLE;
                    vcoord = doubleBufferRefCoords.getBufferAsObject();
                }

                if((vertexType & CF ) != 0) {
                    vdefined |= COLOR_FLOAT;
                    cdataBuffer = floatBufferRefColors.getBufferAsObject();
                } else if((vertexType & CUB) != 0) {
                    vdefined |= COLOR_BYTE;
                    cdataBuffer = byteBufferRefColors.getBufferAsObject();
                }

                if((vertexType & NORMAL_DEFINED) != 0) {
                    vdefined |= NORMAL_FLOAT;
                    normal = floatBufferRefNormals.getBufferAsObject();
                }

                if((vertexType & TEXCOORD_DEFINED) != 0)
                    vdefined |= TEXCOORD_FLOAT;
                // NOTE : need to add vertex attrs
                Pipeline.getPipeline().buildGAForBuffer(cv.ctx,
                        this, geoType, isNonUniformScale,
                        updateAlpha, alpha,
                        ignoreVertexColors,
                        validVertexCount,
                        vertexFormat,
                        vdefined,
                        initialCoordIndex,
                        vcoord,
                        initialColorIndex,cdataBuffer,
                        initialNormalIndex, normal,
                        ((texCoordSetMap == null) ? 0:texCoordSetMap.length),
                        texCoordSetMap,
                        initialTexCoordIndex,texCoordStride,
                        refTexCoords,
                        (xform == null) ? null : xform.mat,
                        (nxform == null) ? null : nxform.mat);
      }
            */

  }

    }

    void unIndexify(IndexedGeometryArrayRetained src) {
  if ((src.vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) {
      unIndexifyJavaArray(src);
  }
  else {
      unIndexifyNIOBuffer(src);
  }
    }

    private void unIndexifyJavaArray(IndexedGeometryArrayRetained src) {
//        System.err.println("unIndexifyJavaArray");

        int vOffset = 0, srcOffset, tOffset = 0;
        int index, colorStride = 0;
  float[] vdata = null;
        int i;
  int start, end;
  start = src.initialIndexIndex;
  end = src.initialIndexIndex + src.validIndexCount;
  // If its either "normal" data or interleaved data then ..
  if (((src.vertexFormat & GeometryArray.BY_REFERENCE) == 0) ||
      ((src.vertexFormat & GeometryArray.INTERLEAVED) != 0)) {

      if ((src.vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
    vdata = src.vertexData;
    if ((src.vertexFormat & GeometryArray.COLOR) != 0)
        colorStride = 4;
      }
      else if ((src.vertexFormat & GeometryArray.INTERLEAVED) != 0) {
    vdata = src.interLeavedVertexData;
    if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
        colorStride = 4;
    else if ((src.vertexFormat & GeometryArray.COLOR) != 0)
        colorStride = 3;
      }

      //      System.err.println("===> start = "+start+" end = "+end);
      for (index= start; index < end; index++) {
    if ((vertexFormat & GeometryArray.NORMALS) != 0){
        System.arraycopy(vdata,
      src.indexNormal[index]*src.stride + src.normalOffset,
      vertexData, vOffset + normalOffset, 3);
    }
    if (colorStride == 4){
        //        System.err.println("===> copying color3");
        System.arraycopy(vdata,
      src.indexColor[index]*src.stride + src.colorOffset,
      vertexData, vOffset + colorOffset, colorStride);
    } else if (colorStride == 3) {
        //        System.err.println("===> copying color4");
        System.arraycopy(vdata,
      src.indexColor[index]*src.stride + src.colorOffset,
      vertexData, vOffset + colorOffset, colorStride);
        vertexData[vOffset + colorOffset + 3] = 1.0f;
    }

    if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
        int tcOffset = vOffset + textureOffset;
                    int interleavedOffset = 0;

        for (i = 0; i < texCoordSetCount;
        i++, tcOffset += texCoordStride) {

                        if ((src.vertexFormat & GeometryArray.INTERLEAVED) != 0) {
                            interleavedOffset = i * texCoordStride;
                        }

       System.arraycopy(vdata,
          (src.indexTexCoord[i][index])*src.stride + src.textureOffset + interleavedOffset,
          vertexData, tcOffset, texCoordStride);
        }
    }

                if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
                    // vertex attributes can't be interleaved
                    assert (src.vertexFormat & GeometryArray.INTERLEAVED) == 0;

        for (i = 0; i < vertexAttrCount; i++) {
                        int vaOffset = vOffset + vertexAttrOffsets[i];

       System.arraycopy(vdata,
          (src.indexVertexAttr[i][index])*src.stride + src.vertexAttrOffsets[i],
          vertexData, vaOffset, vertexAttrSizes[i]);
        }
                }

    if ((vertexFormat & GeometryArray.COORDINATES) != 0){
        //        System.err.println("===> copying coords");
        System.arraycopy(vdata,
      src.indexCoord[index]*src.stride
        + src.coordinateOffset,
      vertexData,
      vOffset + coordinateOffset, 3);
    }
    vOffset += stride;
      }

  } else {
      if ((vertexFormat & GeometryArray.NORMALS) != 0) {
    vOffset = normalOffset;
    switch ((src.vertexType & NORMAL_DEFINED)) {
    case NF:
        for (index=start; index < end; index++) {
      System.arraycopy(src.floatRefNormals,
           src.indexNormal[index]*3,
           vertexData,
           vOffset, 3);
      vOffset += stride;
        }
        break;
    case N3F:
        for (index=start; index < end; index++) {
      srcOffset = src.indexNormal[index];
      vertexData[vOffset] = src.v3fRefNormals[srcOffset].x;
      vertexData[vOffset+1] = src.v3fRefNormals[srcOffset].y;
      vertexData[vOffset+2] = src.v3fRefNormals[srcOffset].z;
      vOffset += stride;
        }
        break;
    default:
        break;
    }
      }

      if ((vertexFormat & GeometryArray.COLOR) != 0) {
    vOffset = colorOffset;
    int multiplier = 3;
    if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
        multiplier = 4;

    switch ((src.vertexType & COLOR_DEFINED)) {
    case CF:
        for (index=start; index < end; index++) {
      if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0) {
          System.arraycopy(src.floatRefColors,
               src.indexColor[index]*multiplier,
               vertexData,
               vOffset, 4);
      }
      else {
          System.arraycopy(src.floatRefColors,
               src.indexColor[index]*multiplier,
               vertexData,
               vOffset, 3);
          vertexData[vOffset+3] = 1.0f;
      }
      vOffset += stride;
        }
        break;
    case CUB:
        for (index=start; index < end; index++) {
      srcOffset = src.indexColor[index] * multiplier;
      vertexData[vOffset] = (src.byteRefColors[srcOffset] & 0xff) * ByteToFloatScale;
      vertexData[vOffset+1] = (src.byteRefColors[srcOffset+1] & 0xff) * ByteToFloatScale;
      vertexData[vOffset+2] = (src.byteRefColors[srcOffset+2] & 0xff) * ByteToFloatScale;
      if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0) {
          vertexData[vOffset+3] = (src.byteRefColors[srcOffset+3] & 0xff) * ByteToFloatScale;
      }
      else {
          vertexData[vOffset+3] = 1.0f;
      }
      vOffset += stride;
        }
        break;
    case C3F:
        for (index=start; index < end; index++) {
      srcOffset = src.indexColor[index];
      vertexData[vOffset] = src.c3fRefColors[srcOffset].x;
      vertexData[vOffset+1] = src.c3fRefColors[srcOffset].y;
      vertexData[vOffset+2] = src.c3fRefColors[srcOffset].z;
      vertexData[vOffset+3] = 1.0f;
      vOffset += stride;
        }
        break;
    case C4F:
        for (index=start; index < end; index++) {
      srcOffset = src.indexColor[index];
      vertexData[vOffset] = src.c4fRefColors[srcOffset].x;
      vertexData[vOffset+1] = src.c4fRefColors[srcOffset].y;
      vertexData[vOffset+2] = src.c4fRefColors[srcOffset].z;
      vertexData[vOffset+3] = src.c4fRefColors[srcOffset].w;
      vOffset += stride;
        }
        break;
    case C3UB:
        for (index=start; index < end; index++) {
      srcOffset = src.indexColor[index];
      vertexData[vOffset] = (src.c3bRefColors[srcOffset].x & 0xff) * ByteToFloatScale;
      vertexData[vOffset+1] = (src.c3bRefColors[srcOffset].y & 0xff) * ByteToFloatScale;
      vertexData[vOffset+2] = (src.c3bRefColors[srcOffset].z & 0xff) * ByteToFloatScale;
      vertexData[vOffset+3] = 1.0f;
      vOffset += stride;
        }
        break;
    case C4UB:
        for (index=start; index < end; index++) {
      srcOffset = src.indexColor[index];
      vertexData[vOffset] = (src.c4bRefColors[srcOffset].x & 0xff) * ByteToFloatScale;
      vertexData[vOffset+1] = (src.c4bRefColors[srcOffset].y & 0xff) * ByteToFloatScale;
      vertexData[vOffset+2] = (src.c4bRefColors[srcOffset].z & 0xff) * ByteToFloatScale;
      vertexData[vOffset+3] = (src.c4bRefColors[srcOffset].w & 0xff) * ByteToFloatScale;
      vOffset += stride;
        }
        break;
    default:
        break;
    }
      }

      if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
    vOffset = textureOffset;
    switch ((src.vertexType & TEXCOORD_DEFINED)) {
    case TF:
        for (index=start; index < end; index++) {
      for (i = 0, tOffset = vOffset;
        i < texCoordSetCount; i++) {
          System.arraycopy(src.refTexCoords[i],
        src.indexTexCoord[i][index]*texCoordStride,
        vertexData, tOffset, texCoordStride);
          tOffset += texCoordStride;
      }
      vOffset += stride;
        }
        break;
    case T2F:
        for (index=start; index < end; index++) {
      for (i = 0, tOffset = vOffset;
              i < texCoordSetCount; i++) {
           srcOffset = src.indexTexCoord[i][index];
           vertexData[tOffset] =
            ((TexCoord2f[])src.refTexCoords[i])[srcOffset].x;
           vertexData[tOffset+1] =
            ((TexCoord2f[])src.refTexCoords[i])[srcOffset].y;
           tOffset += texCoordStride;
      }
      vOffset += stride;
        }
        break;
    case T3F:
        for (index=start; index < end; index++) {
      for (i = 0, tOffset = vOffset;
              i < texCoordSetCount; i++) {
           srcOffset = src.indexTexCoord[i][index];
           vertexData[tOffset] =
            ((TexCoord3f[])src.refTexCoords[i])[srcOffset].x;
           vertexData[tOffset+1] =
            ((TexCoord3f[])src.refTexCoords[i])[srcOffset].y;
           vertexData[tOffset+2] =
            ((TexCoord3f[])src.refTexCoords[i])[srcOffset].z;
           tOffset += texCoordStride;
      }
      vOffset += stride;
        }
        break;
    default:
        break;
    }
      }

            if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
    vOffset = 0;
    switch (src.vertexType & VATTR_DEFINED) {
    case AF:
        for (index=start; index < end; index++) {
      for (i = 0; i < vertexAttrCount; i++) {
                            int vaOffset = vOffset + vertexAttrOffsets[i];
          System.arraycopy(src.floatRefVertexAttrs[i],
        src.indexVertexAttr[i][index]*vertexAttrSizes[i],
        vertexData, vaOffset, vertexAttrSizes[i]);
      }
      vOffset += stride;
        }
        break;
    }
            }

      if ((vertexFormat & GeometryArray.COORDINATES) != 0) {
    vOffset = coordinateOffset;
    switch ((src.vertexType & VERTEX_DEFINED)) {
    case PF:
        for (index=start; index < end; index++) {
      System.arraycopy(src.floatRefCoords,
           src.indexCoord[index]*3,
           vertexData,
           vOffset, 3);
      vOffset += stride;
        }
        break;
    case PD:
        for (index=start; index < end; index++) {
      srcOffset = src.indexCoord[index] * 3;
      vertexData[vOffset] = (float)src.doubleRefCoords[srcOffset];
      vertexData[vOffset+1] = (float)src.doubleRefCoords[srcOffset+1];
      vertexData[vOffset+2] = (float)src.doubleRefCoords[srcOffset+2];
      vOffset += stride;
        }
        break;
    case P3F:
        for (index=start; index < end; index++) {
      srcOffset = src.indexCoord[index];
      vertexData[vOffset] = src.p3fRefCoords[srcOffset].x;
      vertexData[vOffset+1] = src.p3fRefCoords[srcOffset].y;
      vertexData[vOffset+2] = src.p3fRefCoords[srcOffset].z;
      vOffset += stride;
        }
        break;
    case P3D:
        for (index=start; index < end; index++) {
      srcOffset = src.indexCoord[index];
      vertexData[vOffset] = (float)src.p3dRefCoords[srcOffset].x;
      vertexData[vOffset+1] = (float)src.p3dRefCoords[srcOffset].y;
      vertexData[vOffset+2] = (float)src.p3dRefCoords[srcOffset].z;
      vOffset += stride;
        }
        break;
    default:
        break;
    }
      }

  }
    }


    private void unIndexifyNIOBuffer(IndexedGeometryArrayRetained src) {
//        System.err.println("unIndexifyNIOBuffer");

        int vOffset = 0, srcOffset, tOffset = 0;
        int index, colorStride = 0;
        int i;
  int start, end;
  start = src.initialIndexIndex;
  end = src.initialIndexIndex + src.validIndexCount;
  // If its interleaved data then ..
  if ((src.vertexFormat & GeometryArray.INTERLEAVED) != 0) {
      if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
    colorStride = 4;
      else if ((src.vertexFormat & GeometryArray.COLOR) != 0)
    colorStride = 3;

      //      System.err.println("===> start = "+start+" end = "+end);
      for (index= start; index < end; index++) {
    if ((vertexFormat & GeometryArray.NORMALS) != 0){
        src.interleavedFloatBufferImpl.position(src.indexNormal[index]*src.stride + src.normalOffset);
        src.interleavedFloatBufferImpl.get(vertexData, vOffset + normalOffset, 3);
    }

    if (colorStride == 4){
        src.interleavedFloatBufferImpl.position(src.indexColor[index]*src.stride + src.colorOffset);
        src.interleavedFloatBufferImpl.get(vertexData, vOffset + colorOffset, colorStride);
    } else if (colorStride == 3) {
        src.interleavedFloatBufferImpl.position(src.indexColor[index]*src.stride + src.colorOffset);
        src.interleavedFloatBufferImpl.get(vertexData, vOffset + colorOffset, colorStride);
        vertexData[vOffset + colorOffset + 3] = 1.0f;
    }

    if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
        int tcOffset = vOffset + textureOffset;
        for (i = 0; i < texCoordSetCount;
        i++, tcOffset += texCoordStride) {

      src.interleavedFloatBufferImpl.position((src.indexTexCoord[i][index])*src.stride +
                  src.textureOffset);
      src.interleavedFloatBufferImpl.get(vertexData, tcOffset, texCoordStride);
        }
    }
    if ((vertexFormat & GeometryArray.COORDINATES) != 0){
        src.interleavedFloatBufferImpl.position(src.indexCoord[index]*src.stride + src.coordinateOffset );
        src.interleavedFloatBufferImpl.get(vertexData, vOffset + coordinateOffset, 3);
    }
    vOffset += stride;
      }

  } else {
      if ((vertexFormat & GeometryArray.NORMALS) != 0){
    vOffset = normalOffset;
    if ((src.vertexType & NORMAL_DEFINED) != 0) {
        for (index=start; index < end; index++) {
      src.floatBufferRefNormals.position(src.indexNormal[index]*3);
      src.floatBufferRefNormals.get(vertexData, vOffset, 3);
      vOffset += stride;
        }
    }
      }

            if ((vertexFormat & GeometryArray.COLOR) != 0){
    vOffset = colorOffset;
    int multiplier = 3;
    if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
        multiplier = 4;

    switch ((src.vertexType & COLOR_DEFINED)) {
    case CF:
        for (index=start; index < end; index++) {
      if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0) {
          src.floatBufferRefColors.position(src.indexColor[index]*multiplier);
          src.floatBufferRefColors.get(vertexData, vOffset, 4);
      }
      else {
          src.floatBufferRefColors.position(src.indexColor[index]*multiplier);
          src.floatBufferRefColors.get(vertexData, vOffset, 3);
          vertexData[vOffset+3] = 1.0f;
      }
      vOffset += stride;
        }
        break;
    case CUB:
        for (index=start; index < end; index++) {
      srcOffset = src.indexColor[index] * multiplier;
      vertexData[vOffset] = (src.byteBufferRefColors.get(srcOffset) & 0xff) * ByteToFloatScale;
      vertexData[vOffset+1] = (src.byteBufferRefColors.get(srcOffset+1) & 0xff) * ByteToFloatScale;
      vertexData[vOffset+2] = (src.byteBufferRefColors.get(srcOffset+2) & 0xff) * ByteToFloatScale;

      if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0) {
          vertexData[vOffset+3] = (src.byteBufferRefColors.get(srcOffset+3) & 0xff) * ByteToFloatScale;
      }
      else {
          vertexData[vOffset+3] = 1.0f;
      }
      vOffset += stride;
        }
        break;
    default:
        break;
    }
      }

            if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
    vOffset = textureOffset;
    if ((src.vertexType & TEXCOORD_DEFINED) != 0) {
        for (index=start; index < end; index++) {
      for (i = 0, tOffset = vOffset;
        i < texCoordSetCount; i++) {
          FloatBuffer texBuffer = (FloatBuffer)src.refTexCoordsBuffer[i].getROBuffer();
          texBuffer.position(src.indexTexCoord[i][index]*texCoordStride);
          texBuffer.get(vertexData, tOffset, texCoordStride);
          tOffset += texCoordStride;
      }
      vOffset += stride;
        }
    }
      }

            if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
    vOffset = 0;
    if ((src.vertexType & VATTR_DEFINED) == AF) {
        for (index=start; index < end; index++) {
      for (i = 0; i < vertexAttrCount; i++) {
                            int vaOffset = vOffset + vertexAttrOffsets[i];
          FloatBuffer vaBuffer = src.floatBufferRefVertexAttrs[i];
          vaBuffer.position(src.indexVertexAttr[i][index]*vertexAttrSizes[i]);
          vaBuffer.get(vertexData, vaOffset, vertexAttrSizes[i]);
      }
      vOffset += stride;
        }
    }
      }

            if ((vertexFormat & GeometryArray.COORDINATES) != 0){
    vOffset = coordinateOffset;
    switch ((src.vertexType & VERTEX_DEFINED)) {
    case PF:
        for (index=start; index < end; index++) {
      src.floatBufferRefCoords.position(src.indexCoord[index]*3);
      src.floatBufferRefCoords.get(vertexData, vOffset, 3);
      vOffset += stride;
        }
        break;
    case PD:
        for (index=start; index < end; index++) {
      srcOffset = src.indexCoord[index] * 3;
      vertexData[vOffset] = (float)src.doubleBufferRefCoords.get(srcOffset);
      vertexData[vOffset+1] = (float)src.doubleBufferRefCoords.get(srcOffset+1);
      vertexData[vOffset+2] = (float)src.doubleBufferRefCoords.get(srcOffset+2);
      vOffset += stride;
        }
        break;
    default:
        break;
    }
      }

  }
    }


    /**
     * Returns the vertex stride in numbers of floats as a function
     * of the vertexFormat.
     * @return the stride in floats for this vertex array
     */
    int stride()
    {
  int stride = 0;

  if((this.vertexFormat & GeometryArray.COORDINATES) != 0) stride += 3;
  if((this.vertexFormat & GeometryArray.NORMALS) != 0) stride += 3;

  if ((this.vertexFormat & GeometryArray.COLOR) != 0) {
      if ((this.vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
    // By copy
    stride += 4;
      } else {
    if ((this.vertexFormat & GeometryArray.WITH_ALPHA) == 0) {
        stride += 3;
    }
    else {
        stride += 4;
    }
      }
  }

  if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {

      if ((this.vertexFormat &
      GeometryArray.TEXTURE_COORDINATE_2) != 0) {
          texCoordStride = 2;
      } else if ((this.vertexFormat &
      GeometryArray.TEXTURE_COORDINATE_3) != 0) {
    texCoordStride = 3;
      } else if ((this.vertexFormat &
      GeometryArray.TEXTURE_COORDINATE_4) != 0) {
    texCoordStride = 4;
      }

      stride += texCoordStride * texCoordSetCount;
  }

  if ((this.vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
      stride += vertexAttrStride;
  }

  //System.err.println("stride() = " + stride);
  return stride;
    }

    int[] texCoordSetMapOffset()
    {
  if (texCoordSetMap == null)
      return null;

        texCoordSetMapOffset = new int[texCoordSetMap.length];
  for (int i = 0; i < texCoordSetMap.length; i++) {
       if (texCoordSetMap[i] == -1) {
     texCoordSetMapOffset[i] = -1;
       } else {
           texCoordSetMapOffset[i] = texCoordSetMap[i] * texCoordStride;
       }
  }
  return texCoordSetMapOffset;
    }

    /**
     * Returns the stride of the set of vertex attributes. This is the
     * sum of the sizes of each vertex attribute.
     * @return the stride of the vertex attribute data
     */
    int vertexAttrStride() {
        int sum = 0;
        for (int i = 0; i < vertexAttrCount; i++) {
            sum += vertexAttrSizes[i];
        }
        return sum;
    }

    /**
     * Returns the offset in number of floats from the start of a vertex to
     * each per-vertex vertex attribute.
     * @return array of offsets in floats vertex start to the vertex attribute data
     */
    int[] vertexAttrOffsets() {
  int[] offsets;

        // Create array of offsets to the start of each vertex attribute.
        // The offset of the first attribute is always 0. If no vertex attributes exist,
        // then we will allocate an array of length 1 to avoid some checking elsewhere.
        if (vertexAttrCount > 0) {
            offsets = new int[vertexAttrCount];
        }
        else {
            offsets = new int[1];
        }
        offsets[0] = 0;
        for (int i = 1; i < vertexAttrCount; i++) {
            offsets[i] = offsets[i-1] + vertexAttrSizes[i-1];
        }

        return offsets;
    }

    /**
     * Returns the offset in number of floats from the start of a vertex to
     * the per-vertex texture coordinate data.
     * texture coordinate data always follows vertex attribute data
     * @return the offset in floats vertex start to the tetxure data
     */
    int textureOffset()
    {
  int offset = vertexAttrOffsets[0];

  if ((this.vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
      offset += vertexAttrStride;
  }

  return offset;
    }

    /**
     * Returns the offset in number of floats from the start of a vertex to
     * the per-vertex color data.
     * color data always follows texture data
     * @param vertexFormat the vertex format for this array
     * @return the offset in floats vertex start to the color data
     */
    int colorOffset()
    {
  int offset = textureOffset;

  if((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0)
      offset += 2 * texCoordSetCount;
  else if((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0)
      offset += 3 * texCoordSetCount;
  else if((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0)
      offset += 4 * texCoordSetCount;

  return offset;
    }

    /**
     * Returns the offset in number of floats from the start of a vertex to
     * the per-vertex normal data.
     * normal data always follows color data
     * @return the offset in floats from the start of a vertex to the normal
     */
    int normalOffset()
    {
  int offset = colorOffset;

  if ((this.vertexFormat & GeometryArray.COLOR) != 0) {
      if ((this.vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
    offset += 4;
      } else {
    if ((this.vertexFormat & GeometryArray.WITH_ALPHA) == 0) {
        offset += 3;
    }
    else {
        offset += 4;
    }
      }
  }
  return offset;
    }

    /**
     * Returns the offset in number of floats from the start of a vertex to
     * the per vertex coordinate data.
     * @return the offset in floats vertex start to the coordinate data
     */
    int coordinateOffset()
    {
  int offset = normalOffset;

  if ((this.vertexFormat & GeometryArray.NORMALS) != 0) offset += 3;
  return offset;
    }

    /**
     * Returns number of vertices in the GeometryArray
     * @return vertexCount number of vertices in the GeometryArray
     */
    int getVertexCount(){
  return vertexCount;
    }

    /**
     * Returns vertexFormat in the GeometryArray
     * @return vertexFormat format of vertices in the GeometryArray
     */
    @Override
    int getVertexFormat(){
  return vertexFormat;
    }

    /**
     * Retrieves the number of vertex attributes in this GeometryArray
     * object.
     *
     * @return the number of vertex attributes in this GeometryArray
     * object
     */
    int getVertexAttrCount() {
        return vertexAttrCount;
    }


    /**
     * Retrieves the vertex attribute sizes array from this
     * GeometryArray object.
     *
     * @param vertexAttrSizes an array that will receive a copy of
     * the vertex attribute sizes array.  The array must hold at least
     * <code>vertexAttrCount</code> elements.
     */
    void getVertexAttrSizes(int[] vertexAttrSizes) {
        for (int i = 0; i < vertexAttrCount; i++) {
            vertexAttrSizes[i] = this.vertexAttrSizes[i];
        }
    }



    void sendDataChangedMessage(boolean coordinatesChanged) {
  J3dMessage[] m;
  int i, j, k, numShapeMessages, numMorphMessages;

  synchronized(liveStateLock) {
      if (source != null && source.isLive()) {
    // System.err.println("In GeometryArrayRetained - ");

    // Send a message to renderBin to rebuild the display list or
    // process the vertex array accordingly
    // XXXX: Should I send one per universe, isn't display list
    // shared by all context/universes?
    int threads = J3dThread.UPDATE_RENDER;
    // If the geometry type is Indexed then we need to clone the geometry
    // We also need to update the cachedChangedFrequent flag
    threads |= J3dThread.UPDATE_RENDERING_ATTRIBUTES;

    synchronized (universeList) {
        numShapeMessages = universeList.size();
        m = new J3dMessage[numShapeMessages];

        k = 0;

        for (i = 0; i < numShapeMessages; i++, k++) {
      gaList.clear();

      ArrayList<Shape3DRetained> shapeList = userLists.get(i);
      for (j = 0; j < shapeList.size(); j++) {
        Shape3DRetained s = shapeList.get(j);
          LeafRetained src = (LeafRetained)s.sourceNode;
          // Should only need to update distinct localBounds.
          if (coordinatesChanged && src.boundsAutoCompute) {
        src.boundsDirty = true;
          }
      }

      for (j = 0; j < shapeList.size(); j++) {
        Shape3DRetained s = shapeList.get(j);
          LeafRetained src = (LeafRetained)s.sourceNode;
          if (src.boundsDirty) {
        // update combine bounds of mirrorShape3Ds. So we need to
        // use its bounds and not localBounds.
        // bounds is actually a reference to
        // mirrorShape3D.source.localBounds.
        src.updateBounds();
        src.boundsDirty = false;
          }
          gaList.add(Shape3DRetained.getGeomAtom(s));
      }

      m[k] = new J3dMessage();

      m[k].type = J3dMessage.GEOMETRY_CHANGED;
      // Who to send this message to ?
      m[k].threads = threads;
      m[k].args[0] = gaList.toArray();
      m[k].args[1] = this;
      m[k].args[2]= null;
      m[k].args[3] = new Integer(changedFrequent);
      m[k].universe = universeList.get(i);
        }
        VirtualUniverse.mc.processMessage(m);
    }

    if (morphUniverseList != null) {
        synchronized (morphUniverseList) {
      numMorphMessages = morphUniverseList.size();

      // take care of morph that is referencing this geometry
      if (numMorphMessages > 0) {
          synchronized (morphUniverseList) {
        for (i = 0; i < numMorphMessages; i++, k++) {
          ArrayList<MorphRetained> morphList = morphUserLists.get(i);
          for (j = 0; j < morphList.size(); j++) {
            morphList.get(j).updateMorphedGeometryArray(this, coordinatesChanged);
          }
        }
          }
      }
        }
    }
      }
  }

    }
    /**
     * Sets the coordinate associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param coordinate an array of 3 values containing the new coordinate
     */
    void setCoordinate(int index, float coordinate[]) {
  int offset = this.stride * index + coordinateOffset;
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COORDINATE_CHANGED;

  this.vertexData[offset= coordinate[0];
  this.vertexData[offset+1]= coordinate[1];
  this.vertexData[offset+2]= coordinate[2];

        if (isLive) {
            geomLock.unLock();
        }
        if (inUpdater || (source == null)) {
            return;
        }
  if (!isLive) {
    boundsDirty = true;
      return;
  }

  // Compute geo's bounds
  processCoordsChanged(false);
  sendDataChangedMessage(true);

    }

    /**
     * Sets the coordinate associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param coordinate an array of 3 values containing the new coordinate
     */
    void setCoordinate(int index, double coordinate[]) {
  int offset = this.stride * index + coordinateOffset;
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COORDINATE_CHANGED;
  this.vertexData[offset= (float)coordinate[0];
  this.vertexData[offset+1]= (float)coordinate[1];
  this.vertexData[offset+2]= (float)coordinate[2];

  if(isLive) {
            geomLock.unLock();
  }

        if (inUpdater || (source == null)) {
      return;
  }
  if (!isLive) {
            boundsDirty = true;
      return;
  }

  // Compute geo's bounds
  processCoordsChanged(false);
  sendDataChangedMessage(true);
    }

    /**
     * Sets the coordinate associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param coordinate a vector containing the new coordinate
     */
    void setCoordinate(int index, Point3f coordinate) {
  int offset = this.stride * index + coordinateOffset;
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COORDINATE_CHANGED;
  this.vertexData[offset= coordinate.x;
  this.vertexData[offset+1]= coordinate.y;
  this.vertexData[offset+2]= coordinate.z;

  if(isLive) {
            geomLock.unLock();
  }
        if (inUpdater || (source == null)) {
      return;
  }
  if (!isLive) {
      boundsDirty = true;
      return;
  }

  // Compute geo's bounds
  processCoordsChanged(false);
  sendDataChangedMessage(true);
    }

    /**
     * Sets the coordinate associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param coordinate a vector containing the new coordinate
     */
    void setCoordinate(int index, Point3d coordinate) {
  int offset = this.stride * index + coordinateOffset;
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COORDINATE_CHANGED;
  this.vertexData[offset= (float)coordinate.x;
  this.vertexData[offset+1]= (float)coordinate.y;
  this.vertexData[offset+2]= (float)coordinate.z;
  if(isLive) {
            geomLock.unLock();
  }
  if (inUpdater || source == null ) {
      return;
  }
  if (!isLive) {
      boundsDirty = true;
      return;
  }
  // Compute geo's bounds
  processCoordsChanged(false);
  sendDataChangedMessage(true);
    }

    /**
     * Sets the coordinates associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param coordinates an array of 3*n values containing n new coordinates
     */
    void setCoordinates(int index, float coordinates[]) {
  int offset = this.stride * index + coordinateOffset;
  int i, j, num = coordinates.length;
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COORDINATE_CHANGED;

  for (i=0, j= offset;i < num; i+=3, j+= this.stride)
      {
    this.vertexData[j= coordinates[i];
    this.vertexData[j+1]= coordinates[i+1];
    this.vertexData[j+2]= coordinates[i+2];
      }

  if(isLive) {
            geomLock.unLock();
  }
  if (inUpdater ||source == null ) {
      return;
  }
  if (!isLive) {
      boundsDirty = true;
      return;
  }

  // Compute geo's bounds
  processCoordsChanged(false);

  sendDataChangedMessage(true);

    }

    /**
     * Sets the coordinates associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param coordinates an array of 3*n values containing n new coordinates
     */
    void setCoordinates(int index, double coordinates[]) {
  int offset = this.stride * index + coordinateOffset;
  int i, j, num = coordinates.length;
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COORDINATE_CHANGED;

  for (i=0, j= offset;i < num; i+=3, j+= this.stride)
      {
    this.vertexData[j= (float)coordinates[i];
    this.vertexData[j+1]= (float)coordinates[i+1];
    this.vertexData[j+2]= (float)coordinates[i+2];
      }

  if(isLive) {
            geomLock.unLock();
  }

  if (inUpdater ||source == null ) {
      return;
  }
  if (!isLive) {
      boundsDirty = true;
      return;
  }

  // Compute geo's bounds
  processCoordsChanged(false);

  sendDataChangedMessage(true);
    }

    /**
     * Sets the coordinates associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param coordinates an array of vectors containing new coordinates
     */
    void setCoordinates(int index, Point3f coordinates[]) {
  int offset = this.stride * index + coordinateOffset;
  int i, j, num = coordinates.length;
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COORDINATE_CHANGED;

  for (i=0, j= offset;i < num; i++, j+= this.stride)
      {
    this.vertexData[j= coordinates[i].x;
    this.vertexData[j+1]= coordinates[i].y;
    this.vertexData[j+2]= coordinates[i].z;
      }
  if(isLive) {
            geomLock.unLock();
  }

  if (inUpdater ||source == null ) {
      return;
  }
  if (!isLive) {
      boundsDirty = true;
      return;
  }

  // Compute geo's bounds
  processCoordsChanged(false);

  sendDataChangedMessage(true);

    }

    /**
     * Sets the coordinates associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param coordinates an array of vectors containing new coordinates
     */
    void setCoordinates(int index, Point3d coordinates[]) {
  int offset = this.stride * index + coordinateOffset;
  int i, j, num = coordinates.length;
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COORDINATE_CHANGED;

  for (i=0, j= offset;i < num; i++, j+= this.stride)
      {
    this.vertexData[j= (float)coordinates[i].x;
    this.vertexData[j+1]= (float)coordinates[i].y;
    this.vertexData[j+2]= (float)coordinates[i].z;
      }
        if(isLive) {
            geomLock.unLock();
        }

        if (inUpdater ||source == null ) {
      return;
  }
  if (!isLive) {
      boundsDirty = true;
      return;
  }

  // Compute geo's bounds
  processCoordsChanged(false);

  sendDataChangedMessage(true);
    }


    /**
     * Sets the coordinates associated with the vertices starting at
     * the specified index for this object using coordinate data starting
     * from vertex index <code>start</code> for <code>length</code> vertices.
     * @param index the vertex index
     * @param coordinates an array of vectors containing new coordinates
     * @param start starting vertex index of data in <code>coordinates</code>  .
     * @param length number of vertices to be copied.
     */
    void setCoordinates(int index, float coordinates[], int start, int length) {
  int offset = this.stride * index + coordinateOffset;
  int i, j;
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COORDINATE_CHANGED;
  for (i= start * 3, j= offset; i < (start+length) * 3;
       i+=3, j+= this.stride) {
      this.vertexData[j= coordinates[i];
      this.vertexData[j+1]= coordinates[i+1];
      this.vertexData[j+2]= coordinates[i+2];
  }
  if(isLive) {
            geomLock.unLock();
  }
  if (inUpdater ||source == null ) {
      return;
  }
  if (!isLive) {
      boundsDirty = true;
      return;
  }

  // Compute geo's bounds
  processCoordsChanged(false);

  sendDataChangedMessage(true);
    }

    /**
     * Sets the coordinates associated with the vertices starting at
     * the specified index for this object  using coordinate data starting
     * from vertex index <code>start</code> for <code>length</code> vertices.
     * @param index the vertex index
     * @param coordinates an array of 3*n values containing n new coordinates
     * @param start starting vertex index of data in <code>coordinates</code>  .
     * @param length number of vertices to be copied.
     */
    void setCoordinates(int index, double coordinates[], int start, int length) {
  int offset = this.stride * index + coordinateOffset;
  int i, j;
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COORDINATE_CHANGED;

  for (i= start*3, j= offset; i < (start+length)*3;
       i+=3, j+= this.stride) {
      this.vertexData[j= (float)coordinates[i];
      this.vertexData[j+1]= (float)coordinates[i+1];
      this.vertexData[j+2]= (float)coordinates[i+2];
  }

  if(isLive) {
            geomLock.unLock();
  }
        if (inUpdater || (source == null)) {
      return;
  }
  if (!isLive) {
      boundsDirty = true;
      return;
  }


  // Compute geo's bounds
  processCoordsChanged(false);

  sendDataChangedMessage(true);
    }

    /**
     * Sets the coordinates associated with the vertices starting at
     * the specified index for this object using coordinate data starting
     * from vertex index <code>start</code> for <code>length</code> vertices.
     * @param index the vertex index
     * @param coordinates an array of vectors containing new coordinates
     * @param start starting vertex index of data in <code>coordinates</code>  .
     * @param length number of vertices to be copied.
     */
    void setCoordinates(int index, Point3f coordinates[], int start,
      int length) {
  int offset = this.stride * index + coordinateOffset;
  int i, j;
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COORDINATE_CHANGED;

  for (i=start, j= offset;i < start + length; i++, j+= this.stride) {
      this.vertexData[j= coordinates[i].x;
      this.vertexData[j+1]= coordinates[i].y;
      this.vertexData[j+2]= coordinates[i].z;
  }

  if(isLive) {
            geomLock.unLock();
        }

        if (inUpdater || (source == null)) {
      return;
  }
  if (!isLive) {
      boundsDirty = true;
      return;
  }


  // Compute geo's bounds
  processCoordsChanged(false);

  sendDataChangedMessage(true);
    }

    /**
     * Sets the coordinates associated with the vertices starting at
     * the specified index for this object using coordinate data starting
     * from vertex index <code>start</code> for <code>length</code> vertices.
     * @param index the vertex index
     * @param coordinates an array of vectors containing new coordinates
     * @param start starting vertex index of data in <code>coordinates</code>  .
     * @param length number of vertices to be copied.
     */
    void setCoordinates(int index, Point3d coordinates[], int start,
      int length) {
  int offset = this.stride * index + coordinateOffset;
  int i, j;
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COORDINATE_CHANGED;

  for (i=start, j= offset;i < start + length; i++, j+= this.stride) {
      this.vertexData[j= (float)coordinates[i].x;
      this.vertexData[j+1]= (float)coordinates[i].y;
      this.vertexData[j+2]= (float)coordinates[i].z;
  }

  if(isLive) {
            geomLock.unLock();
        }
        if (inUpdater || (source == null)) {
      return;
  }
  if (!isLive) {
      boundsDirty = true;
      return;
  }

  // Compute geo's bounds
  processCoordsChanged(false);

  sendDataChangedMessage(true);
    }

    /**
     * Sets the color associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param color an array of 3 or 4 values containing the new color
     */
    void setColor(int index, float color[]) {
  int offset = this.stride*index + colorOffset;

  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;
  this.vertexData[offset]   = color[0];
  this.vertexData[offset+1] = color[1];
  this.vertexData[offset+2] = color[2];
  if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
      this.vertexData[offset+3] = color[3]*lastAlpha[0];
  else
      this.vertexData[offset+3] = lastAlpha[0];

  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
  }

    }

    /**
     * Sets the color associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param color an array of 3 or 4 values containing the new color
     */
    void setColor(int index, byte color[]) {
  int offset = this.stride*index + colorOffset;

  boolean isLive = source!=null && source.isLive();
  if(isLive) {
            geomLock.getLock();
  }

  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;
  this.vertexData[offset]   = (color[0] & 0xff) * ByteToFloatScale;
  this.vertexData[offset+1] = (color[1] & 0xff) * ByteToFloatScale;
  this.vertexData[offset+2] = (color[2] & 0xff) * ByteToFloatScale;
  if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
      this.vertexData[offset+3] = ((color[3] & 0xff)* ByteToFloatScale)*lastAlpha[0];
        else
      this.vertexData[offset+3] = lastAlpha[0];

  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
  }

    }

    /**
     * Sets the color associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param color a vector containing the new color
     */
    void setColor(int index, Color3f color) {
  int offset = this.stride*index + colorOffset;

  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
        }

  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;
  this.vertexData[offset]   = color.x;
  this.vertexData[offset+1] = color.y;
  this.vertexData[offset+2] = color.z;
        this.vertexData[offset+3] = lastAlpha[0];

  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
  }


    }

    /**
     * Sets the color associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param color a vector containing the new color
     */
    void setColor(int index, Color4f color) {
  int offset = this.stride*index + colorOffset;
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;
  this.vertexData[offset]   = color.x;
  this.vertexData[offset+1] = color.y;
  this.vertexData[offset+2] = color.z;
  this.vertexData[offset+3] = color.w*lastAlpha[0];

  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
  }

    }

    /**
     * Sets the color associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param color a vector containing the new color
     */
    void setColor(int index, Color3b color) {
  int offset = this.stride*index + colorOffset;

  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }

  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;
  this.vertexData[offset]   = (color.x & 0xff) * ByteToFloatScale;
  this.vertexData[offset+1] = (color.y & 0xff) * ByteToFloatScale;
  this.vertexData[offset+2] = (color.z & 0xff) * ByteToFloatScale;
        this.vertexData[offset+3] = lastAlpha[0];

  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
  }

    }

    /**
     * Sets the color associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param color a vector containing the new color
     */
    void setColor(int index, Color4b color) {
  int offset = this.stride*index + colorOffset;

  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;
  this.vertexData[offset]   = (color.x & 0xff) * ByteToFloatScale;
  this.vertexData[offset+1] = (color.y & 0xff) * ByteToFloatScale;
  this.vertexData[offset+2] = (color.z & 0xff) * ByteToFloatScale;
  this.vertexData[offset+3] = ((color.w & 0xff) * ByteToFloatScale)*lastAlpha[0];

  if(isLive){
            geomLock.unLock();
            sendDataChangedMessage(false);
  }

    }

    /**
     * Sets the colors associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param colors an array of 3*n or 4*n values containing n new colors
     */
    void setColors(int index, float colors[]) {
  int offset = this.stride*index + colorOffset;
  int i, j, num = colors.length;

  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;

  if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
      {
    for (i=0, j= offset;i < num; i+= 4, j+= this.stride)
        {
      this.vertexData[j]   = colors[i];
      this.vertexData[j+1] = colors[i+1];
      this.vertexData[j+2] = colors[i+2];
      this.vertexData[j+3] = colors[i+3]*lastAlpha[0];
        }
      }
  else
      {
    for (i=0, j= offset;i < num; i+= 3, j+= this.stride)
        {
      this.vertexData[j]   = colors[i];
      this.vertexData[j+1] = colors[i+1];
      this.vertexData[j+2] = colors[i+2];
      this.vertexData[j+3] = lastAlpha[0];
        }
      }

  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
  }

    }

    /**
     * Sets the colors associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param colors an array of 3*n or 4*n values containing n new colors
     */
    void setColors(int index, byte colors[]) {
  int offset = this.stride*index + colorOffset;
  int i, j, num = colors.length;

  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;

  if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
      {
    for (i=0, j= offset;i < num; i+= 4, j+= this.stride)
        {
      this.vertexData[j]   = (colors[i] & 0xff) * ByteToFloatScale;
      this.vertexData[j+1] = (colors[i+1] & 0xff) * ByteToFloatScale;
      this.vertexData[j+2] = (colors[i+2] & 0xff) * ByteToFloatScale;
      this.vertexData[j+3] = ((colors[i+3] & 0xff) * ByteToFloatScale)*lastAlpha[0];
        }
      }
  else
      {
    for (i=0, j= offset;i < num; i+= 3, j+= this.stride)
        {
      this.vertexData[j]   = (colors[i] & 0xff) * ByteToFloatScale;
      this.vertexData[j+1] = (colors[i+1] & 0xff) * ByteToFloatScale;
      this.vertexData[j+2] = (colors[i+2] & 0xff) * ByteToFloatScale;
      this.vertexData[j+3] = lastAlpha[0];
        }
      }


  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
  }

    }

    /**
     * Sets the colors associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param colors an array of vectors containing new colors
     */
    void setColors(int index, Color3f colors[]) {
  int offset = this.stride*index + colorOffset;
  int i, j, num = colors.length;

  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;

  for (i=0, j= offset;i < num; i++, j+= this.stride)
      {
    this.vertexData[j]   = colors[i].x;
    this.vertexData[j+1] = colors[i].y;
    this.vertexData[j+2] = colors[i].z;
    this.vertexData[j+3] = lastAlpha[0];
      }
        if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
        }

    }

    /**
     * Sets the colors associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param colors an array of vectors containing new colors
     */
    void setColors(int index, Color4f colors[]) {
  int offset = this.stride*index + colorOffset;
  int i, j, num = colors.length;
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;

  for (i=0, j= offset;i < num; i++, j+= this.stride)
      {
    this.vertexData[j]   = colors[i].x;
    this.vertexData[j+1] = colors[i].y;
    this.vertexData[j+2] = colors[i].z;
    this.vertexData[j+3] = colors[i].w*lastAlpha[0];
      }
        if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
        }

    }

    /**
     * Sets the colors associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param colors an array of vectors containing new colors
     */
    void setColors(int index, Color3b colors[]) {
  int offset = this.stride*index + colorOffset;
  int i, j, num = colors.length;
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;
  for (i=0, j= offset;i < num; i++, j+= this.stride)
      {
    this.vertexData[j]   = (colors[i].x & 0xff) * ByteToFloatScale;
    this.vertexData[j+1] = (colors[i].y & 0xff) * ByteToFloatScale;
    this.vertexData[j+2] = (colors[i].z & 0xff) * ByteToFloatScale;
    this.vertexData[j+3] = lastAlpha[0];
      }

  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
        }
    }


    /**
     * Sets the colors associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param colors an array of vectors containing new colors
     */
    void setColors(int index, Color4b colors[]) {
  int offset = this.stride*index + colorOffset;
  int i, j, num = colors.length;

  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;

  for (i=0, j= offset;i < num; i++, j+= this.stride)
      {
    this.vertexData[j]   = (colors[i].x & 0xff) * ByteToFloatScale;
    this.vertexData[j+1] = (colors[i].y & 0xff) * ByteToFloatScale;
    this.vertexData[j+2] = (colors[i].z & 0xff) * ByteToFloatScale;
    this.vertexData[j+3] = ((colors[i].w & 0xff) * ByteToFloatScale)*lastAlpha[0];
      }

  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
  }

    }

    /**
     * Sets the colors associated with the vertices starting at
     * the specified index for this object using data in <code>color</code>s
     * starting at index <code>start</code> for <code>length</code> colors.
     * @param index the vertex index
     * @param colors an array of 3*n or 4*n values containing n new colors
     * @param start starting color index of data in <code>colors</code>.
     * @param length number of colors to be copied.
     */
    void setColors(int index, float colors[], int start, int length) {
        int offset = this.stride*index + colorOffset;
        int i, j;
        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;

  if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0) {
            for (i = start * 4, j = offset; i < (start + length) * 4;
     i += 4, j += this.stride) {
                this.vertexData[j]   = colors[i];
                this.vertexData[j+1] = colors[i+1];
                this.vertexData[j+2] = colors[i+2];
                this.vertexData[j+3] = colors[i+3]*lastAlpha[0];
            }
        } else {
            for (i = start * 3, j = offset; i < (start + length) * 3;
     i += 3, j += this.stride) {
                this.vertexData[j]   = colors[i];
                this.vertexData[j+1] = colors[i+1];
                this.vertexData[j+2] = colors[i+2];
                this.vertexData[j+3] = lastAlpha[0];
            }
        }

  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
        }

    }

    /**
     * Sets the colors associated with the vertices starting at
     * the specified index for this object using data in <code>color</code>s
     * starting at index <code>start</code> for <code>length</code> colors.
     * @param index the vertex index
     * @param colors an array of 3*n or 4*n values containing n new colors
     * @param start starting color index of data in <code>colors</code>.
     * @param length number of colors to be copied.
     */
    void setColors(int index, byte colors[], int start, int length) {
        int offset = this.stride*index + colorOffset;
        int i, j;

        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;

  if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0) {
            for (i = start * 4, j = offset; i < (start + length) * 4;
     i += 4, j += this.stride) {
                this.vertexData[j]   = (colors[i] & 0xff) * ByteToFloatScale;
                this.vertexData[j+1] = (colors[i+1] & 0xff) * ByteToFloatScale;
                this.vertexData[j+2] = (colors[i+2] & 0xff) * ByteToFloatScale;
                this.vertexData[j+3] = ((colors[i+3] & 0xff) * ByteToFloatScale)*lastAlpha[0];
            }
        } else {
            for (i = start * 3, j = offset; i < (start + length) * 3;
     i += 3, j += this.stride) {
                this.vertexData[j]   = (colors[i] & 0xff) * ByteToFloatScale;
                this.vertexData[j+1] = (colors[i+1] & 0xff) * ByteToFloatScale;
                this.vertexData[j+2] = (colors[i+2] & 0xff) * ByteToFloatScale;
                this.vertexData[j+3] = lastAlpha[0];
            }
        }

        if(isLive) {
            geomLock.unLock();
      sendDataChangedMessage(false);
        }

    }

    /**
     * Sets the colors associated with the vertices starting at
     * the specified index for this object using data in <code>color</code>s
     * starting at index <code>start</code> for <code>length</code> colors.
     * @param index the vertex index
     * @param colors an array of 3*n or 4*n values containing n new colors
     * @param start starting color index of data in <code>colors</code>.
     * @param length number of colors to be copied.
     */
    void setColors(int index, Color3f colors[], int start, int length) {
        int offset = this.stride*index + colorOffset;
        int i, j;
        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;

  for (i = start, j = offset; i < start+length; i++, j += this.stride) {
            this.vertexData[j]   = colors[i].x;
            this.vertexData[j+1] = colors[i].y;
            this.vertexData[j+2] = colors[i].z;
            this.vertexData[j+3] = lastAlpha[0];
        }

  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
  }

    }

    /**
     * Sets the colors associated with the vertices starting at
     * the specified index for this object using data in <code>color</code>s
     * starting at index <code>start</code> for <code>length</code> colors.
     * @param index the vertex index
     * @param colors an array of 3*n or 4*n values containing n new colors
     * @param start starting color index of data in <code>colors</code>.
     * @param length number of colors to be copied.
     */
    void setColors(int index, Color4f colors[], int start, int length) {
        int offset = this.stride*index + colorOffset;
        int i, j;
        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;

  for (i = start, j = offset; i < start+length; i++, j += this.stride) {
            this.vertexData[j]   = colors[i].x;
            this.vertexData[j+1] = colors[i].y;
            this.vertexData[j+2] = colors[i].z;
            this.vertexData[j+3] = colors[i].w*lastAlpha[0];
        }

        if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
        }

    }

    /**
     * Sets the colors associated with the vertices starting at
     * the specified index for this object using data in <code>color</code>s
     * starting at index <code>start</code> for <code>length</code> colors.
     * @param index the vertex index
     * @param colors an array of 3*n or 4*n values containing n new colors
     * @param start starting color index of data in <code>colors</code>.
     * @param length number of colors to be copied.
     */
    void setColors(int index, Color3b colors[], int start, int length) {
        int offset = this.stride*index + colorOffset;
        int i, j;
        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;

  for (i = start, j = offset; i < start+length; i++, j += this.stride) {
            this.vertexData[j]   = (colors[i].x & 0xff) * ByteToFloatScale;
            this.vertexData[j+1] = (colors[i].y & 0xff) * ByteToFloatScale;
            this.vertexData[j+2] = (colors[i].z & 0xff) * ByteToFloatScale;
            this.vertexData[j+3] = lastAlpha[0];
        }

        if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
        }

    }

    /**
     * Sets the colors associated with the vertices starting at
     * the specified index for this object using data in <code>color</code>s
     * starting at index <code>start</code> for <code>length</code> colors.
     * @param index the vertex index
     * @param colors an array of 3*n or 4*n values containing n new colors
     * @param start starting color index of data in <code>colors</code>.
     * @param length number of colors to be copied.
     */
    void setColors(int index, Color4b colors[], int start, int length) {
        int offset = this.stride*index + colorOffset;
        int i, j;
        boolean isLive = source!=null && source.isLive();
      if(isLive){
            geomLock.getLock();
      }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;

  for (i = start, j = offset; i < start+length; i++, j += this.stride) {
      this.vertexData[j]   = (colors[i].x & 0xff) * ByteToFloatScale;
      this.vertexData[j+1] = (colors[i].y & 0xff) * ByteToFloatScale;
      this.vertexData[j+2] = (colors[i].z & 0xff) * ByteToFloatScale;
      this.vertexData[j+3] = ((colors[i].w & 0xff) * ByteToFloatScale)*lastAlpha[0];
        }

  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
  }

    }

    /**
     * Sets the normal associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param normal the new normal
     */
    void setNormal(int index, float normal[]) {
  int offset = this.stride*index + normalOffset;
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= NORMAL_CHANGED;
  this.vertexData[offset]   = normal[0];
  this.vertexData[offset+1] = normal[1];
  this.vertexData[offset+2] = normal[2];

        if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
        }

    }

    /**
     * Sets the normal associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param normal the vector containing the new normal
     */
    void setNormal(int index, Vector3f normal) {
  int offset = this.stride*index + normalOffset;
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= NORMAL_CHANGED;
  this.vertexData[offset]   = normal.x;
  this.vertexData[offset+1] = normal.y;
  this.vertexData[offset+2] = normal.z;

  if(isLive){
            geomLock.unLock();
            sendDataChangedMessage(false);
  }

    }

    /**
     * Sets the normals associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param normals the new normals
     */
    void setNormals(int index, float normals[]) {
  int offset = this.stride*index + normalOffset;
  int i, j, num = normals.length;
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= NORMAL_CHANGED;
  for (i=0, j= offset;i < num;i += 3, j+= this.stride)
      {
    this.vertexData[j]   = normals[i];
    this.vertexData[j+1] = normals[i+1];
    this.vertexData[j+2] = normals[i+2];
      }
  if(isLive) {
    geomLock.unLock();
    sendDataChangedMessage(false);
  }

    }

    /**
     * Sets the normals associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param normals the vector containing the new normals
     */
    void setNormals(int index, Vector3f normals[]) {
  int offset = this.stride*index + normalOffset;
  int i, j, num = normals.length;
  boolean isLive = source!=null && source.isLive();
  if(isLive){
     geomLock.getLock();
  }
  dirtyFlag |= NORMAL_CHANGED;
  for (i=0, j= offset;i < num;i++, j+= this.stride)
      {
    this.vertexData[j]   = normals[i].x;
    this.vertexData[j+1] = normals[i].y;
    this.vertexData[j+2] = normals[i].z;
      }
  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
  }

    }

    /**
     * Sets the normals associated with the vertices starting at
     * the specified index for this object using data in <code>normals</code>
     * starting at index <code>start</code> and  ending at index <code>start+length</code>.
     * @param index the vertex index
     * @param normals the new normals
     * @param start starting normal index of data in <code>colors</code>  .
     * @param length number of normals to be copied.
     */
    void setNormals(int index, float normals[], int start, int length) {
        int offset = this.stride*index + normalOffset;
        int i, j;
        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
        dirtyFlag |= NORMAL_CHANGED;
  for (i = start * 3, j = offset; i < (start + length) * 3;
       i+=3, j += this.stride) {
      this.vertexData[j]   = normals[i];
      this.vertexData[j+1] = normals[i+1];
      this.vertexData[j+2] = normals[i+2];
        }
  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
        }

    }

    /**
     * Sets the normals associated with the vertices starting at
     * the specified index for this object using data in <code>normals</code>
     * starting at index <code>start</code> and  ending at index <code>start+length</code>.
     * @param index the vertex index
     * @param normals the new normals
     * @param start starting normal index of data in <code>colors</code>  .
     * @param length number of normals to be copied.
     */
    void setNormals(int index, Vector3f normals[], int start, int length) {
        int offset = this.stride*index + normalOffset;
        int i, j;
        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
        dirtyFlag |= NORMAL_CHANGED;
  for (i = start, j = offset; i < start+length; i++, j += this.stride) {
      this.vertexData[j]   = normals[i].x;
      this.vertexData[j+1] = normals[i].y;
      this.vertexData[j+2] = normals[i].z;
        }
  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
  }

    }


    /**
     * Sets the texture coordinates associated with the vertices starting at
     * the specified index for this object using data in <code>texCoords</code>
     * starting at index <code>start</code> and ending at index <code>start+length</code>.
     * @param index the vertex index
     * @param texCoords the new texture coordinates
     * @param start starting texture coordinate index of data in <code>texCoords</code>  .
     * @param length number of texture Coordinates to be copied.
     */
    void setTextureCoordinates(int texCoordSet, int index, float texCoords[],
        int start, int length) {

  if ((this.vertexFormat & GeometryArray.BY_REFERENCE) != 0)
            throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));

        if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE ) == 0)
            throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));

  int offset = this.stride*index + textureOffset +
      texCoordSet * texCoordStride;
        int i, j, k;
        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
  dirtyFlag |= TEXTURE_CHANGED;

  if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
            for (i = start * 4, j = offset, k = 0; k < length;
       j += this.stride, k++) {
                this.vertexData[j]   = texCoords[i++];
                this.vertexData[j+1] = texCoords[i++];
                this.vertexData[j+2] = texCoords[i++];
                this.vertexData[j+3] = texCoords[i++];
            }
  } else if ((this.vertexFormat &
      GeometryArray.TEXTURE_COORDINATE_3) != 0) {
            for (i = start * 3, j = offset, k = 0; k < length;
       j += this.stride, k++) {
                this.vertexData[j]   = texCoords[i++];
                this.vertexData[j+1] = texCoords[i++];
                this.vertexData[j+2] = texCoords[i++];
            }
        } else {
            for (i = start * 2, j = offset, k = 0; k < length;
       j += this.stride, k++) {
                this.vertexData[j]   = texCoords[i++];
                this.vertexData[j+1] = texCoords[i++];
            }
        }
  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
  }

    }

    /**
     * Sets the texture coordinates associated with the vertices starting at
     * the specified index for this object using data in <code>texCoords</code>
     * starting at index <code>start</code> and ending at index <code>start+length</code>.
     * @param index the vertex index
     * @param texCoords the new texture coordinates
     * @param start starting texture coordinate index of data in <code>texCoords</code>  .
     * @param length number of texture Coordinates to be copied.
     */
    void setTextureCoordinates(int texCoordSet, int index, Point2f texCoords[],
        int start, int length) {

  if ((this.vertexFormat & GeometryArray.BY_REFERENCE) != 0)
            throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));

        if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE ) == 0)
            throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));

  int offset = this.stride*index + textureOffset +
      texCoordSet * texCoordStride;
        int i, j;

        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= TEXTURE_CHANGED;

  for (i = start, j = offset; i < start+length; i++, j += this.stride) {
            this.vertexData[j]   = texCoords[i].x;
            this.vertexData[j+1] = texCoords[i].y;
        }
  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
  }

    }

    /**
     * Sets the texture coordinates associated with the vertices starting at
     * the specified index for this object using data in <code>texCoords</code>
     * starting at index <code>start</code> and ending at index <code>start+length</code>.
     * @param index the vertex index
     * @param texCoords the new texture coordinates
     * @param start starting texture coordinate index of data in <code>texCoords</code>  .
     * @param length number of texture Coordinates to be copied.
     */
    void setTextureCoordinates(int texCoordSet, int index, Point3f texCoords[],
        int start, int length) {

  if ((this.vertexFormat & GeometryArray.BY_REFERENCE) != 0)
            throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));

        if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE ) == 0)
            throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));

  int offset = this.stride*index + textureOffset +
      texCoordSet * texCoordStride;
        int i, j;
        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
  dirtyFlag |= TEXTURE_CHANGED;

  for (i = start, j = offset; i < start+length; i++, j += this.stride) {
            this.vertexData[j]   = texCoords[i].x;
            this.vertexData[j+1] = texCoords[i].y;
            this.vertexData[j+2] = texCoords[i].z;
        }
  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
  }

    }

    /**
     * Sets the texture coordinates associated with the vertices starting at
     * the specified index for this object using data in <code>texCoords</code>
     * starting at index <code>start</code> and ending at index <code>start+length</code>.
     * @param index the vertex index
     * @param texCoords the new texture coordinates
     * @param start starting texture coordinate index of data in <code>texCoords</code>  .
     * @param length number of texture Coordinates to be copied.
     */
    void setTextureCoordinates(int texCoordSet, int index, TexCoord2f texCoords[],
        int start, int length) {
        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
        dirtyFlag |= TEXTURE_CHANGED;
  if ((this.vertexFormat & GeometryArray.BY_REFERENCE) != 0)
            throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));

        if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE ) == 0)
            throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));

  int offset = this.stride*index + textureOffset +
      texCoordSet * texCoordStride;
        int i, j;

  for (i = start, j = offset; i < start+length; i++, j += this.stride) {
            this.vertexData[j]   = texCoords[i].x;
            this.vertexData[j+1] = texCoords[i].y;
        }
  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
  }

    }

    /**
     * Sets the texture coordinates associated with the vertices starting at
     * the specified index for this object using data in <code>texCoords</code>
     * starting at index <code>start</code> and ending at index <code>start+length</code>.
     * @param index the vertex index
     * @param texCoords the new texture coordinates
     * @param start starting texture coordinate index of data in <code>texCoords</code>  .
     * @param length number of texture Coordinates to be copied.
     */
    void setTextureCoordinates(int texCoordSet, int index,
        TexCoord3f texCoords[],
        int start, int length) {
        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
  dirtyFlag |= TEXTURE_CHANGED;

  if ((this.vertexFormat & GeometryArray.BY_REFERENCE) != 0)
            throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));

        if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE ) == 0)
            throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));

  int offset = this.stride*index + textureOffset +
      texCoordSet * texCoordStride;
        int i, j;

  for (i = start, j = offset; i < start+length; i++, j += this.stride) {
            this.vertexData[j]   = texCoords[i].x;
            this.vertexData[j+1] = texCoords[i].y;
            this.vertexData[j+2] = texCoords[i].z;
        }
  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
  }
    }

    /**
     * Sets the texture coordinates associated with the vertices starting at
     * the specified index for this object using data in <code>texCoords</code>
     * starting at index <code>start</code> and ending at index <code>start+length</code>.
     * @param index the vertex index
     * @param texCoords the new texture coordinates
     * @param start starting texture coordinate index of data in <code>texCoords</code>  .
     * @param length number of texture Coordinates to be copied.
     */
    void setTextureCoordinates(int texCoordSet, int index,
        TexCoord4f texCoords[],
        int start, int length) {
        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
  dirtyFlag |= TEXTURE_CHANGED;

  if ((this.vertexFormat & GeometryArray.BY_REFERENCE) != 0)
            throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));

        if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE ) == 0)
            throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));

  int offset = this.stride*index + textureOffset +
      texCoordSet * texCoordStride;
        int i, j;

  for (i = start, j = offset; i < start+length; i++, j += this.stride) {
            this.vertexData[j]   = texCoords[i].x;
            this.vertexData[j+1] = texCoords[i].y;
            this.vertexData[j+2] = texCoords[i].z;
            this.vertexData[j+3] = texCoords[i].w;
        }
  if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
  }
    }


    /**
     * Sets the vertex attribute associated with the vertex at the
     * specified index in the specified vertex attribute number for
     * this object.
     *
     * @param vertexAttrNum vertex attribute number in this geometry array
     * @param index destination vertex index in this geometry array
     * @param vertexAttr the Point2f containing the new vertex attribute
     */
    void setVertexAttr(int vertexAttrNum, int index,
           Point2f vertexAttr) {

  int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum];
  boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
  dirtyFlag |= VATTR_CHANGED;

  this.vertexData[offset] = vertexAttr.x;
  this.vertexData[offset+1] = vertexAttr.y;

        if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
        }
    }

    /**
     * Sets the vertex attribute associated with the vertex at the
     * specified index in the specified vertex attribute number for
     * this object.
     *
     * @param vertexAttrNum vertex attribute number in this geometry array
     * @param index destination vertex index in this geometry array
     * @param vertexAttr the Point3f containing the new vertex attribute
     */
    void setVertexAttr(int vertexAttrNum, int index,
           Point3f vertexAttr) {

  int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum];
  boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
  dirtyFlag |= VATTR_CHANGED;

  this.vertexData[offset] = vertexAttr.x;
  this.vertexData[offset+1] = vertexAttr.y;
  this.vertexData[offset+2] = vertexAttr.z;

  if (isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
  }
    }

    /**
     * Sets the vertex attribute associated with the vertex at the
     * specified index in the specified vertex attribute number for
     * this object.
     *
     * @param vertexAttrNum vertex attribute number in this geometry array
     * @param index destination vertex index in this geometry array
     * @param vertexAttr the Point4f containing the new vertex attribute
     */
    void setVertexAttr(int vertexAttrNum, int index,
           Point4f vertexAttr) {

  int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum];
  boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
  dirtyFlag |= VATTR_CHANGED;

  this.vertexData[offset] = vertexAttr.x;
  this.vertexData[offset+1] = vertexAttr.y;
  this.vertexData[offset+2] = vertexAttr.z;
  this.vertexData[offset+3] = vertexAttr.w;

        if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
        }
    }

    /**
     * Sets the vertex attributes associated with the vertices
     * starting at the specified index in the specified vertex
     * attribute number for this object using data in
     * <code>vertexAttrs</code> starting at index <code>start</code> and
     * ending at index <code>start+length</code>.
     *
     * @param index starting destination vertex index in this geometry array
     * @param vertexAttrs source array of 1*n, 2*n, 3*n, or 4*n values
     * containing n new vertex attributes
     * @param start starting source vertex index in <code>vertexAttrs</code>
     * array.
     * @param length number of vertex attributes to be copied.
     */
    void setVertexAttrs(int vertexAttrNum, int index,
      float[] vertexAttrs,
      int start, int length) {

  int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum];
        int size = vertexAttrSizes[vertexAttrNum];
        int i, j, k;
        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
        dirtyFlag |= VATTR_CHANGED;

        for (i = start * size, j = offset, k = 0; k < length; i += size, j += this.stride, k++) {
            for (int ii = 0; ii < size; ii++) {
                this.vertexData[j+ii] = vertexAttrs[i+ii];
            }
        }

        if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
        }
    }

    /**
     * Sets the vertex attributes associated with the vertices
     * starting at the specified index in the specified vertex
     * attribute number for this object using data in
     * <code>vertexAttrs</code> starting at index <code>start</code> and
     * ending at index <code>start+length</code>.
     *
     * @param vertexAttrNum vertex attribute number in this geometry array
     * @param index starting destination vertex index in this geometry array
     * @param vertexAttrs source array of Point2f objects containing new
     * vertex attributes
     * @param start starting source vertex index in <code>vertexAttrs</code>
     * array.
     * @param length number of vertex attributes to be copied.
     */
    void setVertexAttrs(int vertexAttrNum, int index,
      Point2f[] vertexAttrs,
      int start, int length) {

  int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum];
        int i, j, k;
        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
        dirtyFlag |= VATTR_CHANGED;

        for (i = start, j = offset, k = 0; k < length; i++, j += this.stride, k++) {
      this.vertexData[j] = vertexAttrs[i].x;
      this.vertexData[j+1] = vertexAttrs[i].y;
        }
        if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
        }
    }

    /**
     * Sets the vertex attributes associated with the vertices
     * starting at the specified index in the specified vertex
     * attribute number for this object using data in
     * <code>vertexAttrs</code> starting at index <code>start</code> and
     * ending at index <code>start+length</code>.
     *
     * @param vertexAttrNum vertex attribute number in this geometry array
     * @param index starting destination vertex index in this geometry array
     * @param vertexAttrs source array of Point3f objects containing new
     * vertex attributes
     * @param start starting source vertex index in <code>vertexAttrs</code>
     * array.
     * @param length number of vertex attributes to be copied.
     */
    void setVertexAttrs(int vertexAttrNum, int index,
      Point3f[] vertexAttrs,
      int start, int length) {

  int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum];
        int i, j, k;
        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
        dirtyFlag |= VATTR_CHANGED;

        for (i = start, j = offset, k = 0; k < length; i++, j += this.stride, k++) {
      this.vertexData[j] = vertexAttrs[i].x;
      this.vertexData[j+1] = vertexAttrs[i].y;
      this.vertexData[j+2] = vertexAttrs[i].z;
        }
        if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
        }
    }

    /**
     * Sets the vertex attributes associated with the vertices
     * starting at the specified index in the specified vertex
     * attribute number for this object using data in
     * <code>vertexAttrs</code> starting at index <code>start</code> and
     * ending at index <code>start+length</code>.
     *
     * @param vertexAttrNum vertex attribute number in this geometry array
     * @param index starting destination vertex index in this geometry array
     * @param vertexAttrs source array of Point4f objects containing new
     * vertex attributes
     * @param start starting source vertex index in <code>vertexAttrs</code>
     * array.
     * @param length number of vertex attributes to be copied.
     */
    void setVertexAttrs(int vertexAttrNum, int index,
      Point4f[] vertexAttrs,
      int start, int length) {

  int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum];
        int i, j, k;

        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
        dirtyFlag |= VATTR_CHANGED;

        for (i = start, j = offset, k = 0; k < length; i++, j += this.stride, k++) {
      this.vertexData[j] = vertexAttrs[i].x;
      this.vertexData[j+1] = vertexAttrs[i].y;
      this.vertexData[j+2] = vertexAttrs[i].z;
      this.vertexData[j+3] = vertexAttrs[i].w;
        }
        if(isLive) {
            geomLock.unLock();
            sendDataChangedMessage(false);
        }
    }


    /**
     * Gets the coordinate associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param coordinate an array of 3 values that will receive the new coordinate
     */
    void getCoordinate(int index, float coordinate[]) {
  int offset = this.stride*index + coordinateOffset;

  coordinate[0]= this.vertexData[offset];
  coordinate[1]= this.vertexData[offset+1];
  coordinate[2]= this.vertexData[offset+2];
    }

    /**
     * Gets the coordinate associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param coordinate an array of 3 values that will receive the new coordinate
     */
    void getCoordinate(int index, double coordinate[]) {
  int offset = this.stride*index + coordinateOffset;

  coordinate[0]= (double)this.vertexData[offset];
  coordinate[1]= (double)this.vertexData[offset+1];
  coordinate[2]= (double)this.vertexData[offset+2];
    }

    /**
     * Gets the coordinate associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param coordinate a vector that will receive the new coordinate
     */
    void getCoordinate(int index, Point3f coordinate) {
  int offset = this.stride*index + coordinateOffset;

  coordinate.x = this.vertexData[offset];
  coordinate.y = this.vertexData[offset+1];
  coordinate.z = this.vertexData[offset+2];
    }

    /**
     * Gets the coordinate associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param coordinate a vector that will receive the new coordinate
     */
    void getCoordinate(int index, Point3d coordinate) {
  int offset = this.stride*index + coordinateOffset;

  coordinate.x = (double)this.vertexData[offset];
  coordinate.y = (double)this.vertexData[offset+1];
  coordinate.z = (double)this.vertexData[offset+2];
    }

    /**
     * Gets the coordinates associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param coordinates an array of 3*n values that will receive new coordinates
     */
    void getCoordinates(int index, float coordinates[]) {
  int offset = this.stride*index + coordinateOffset;
  int i, j, num = coordinates.length;

  for (i=0,j= offset;i < num;i +=3, j += this.stride)
      {
    coordinates[i= this.vertexData[j];
    coordinates[i+1]= this.vertexData[j+1];
    coordinates[i+2]= this.vertexData[j+2];
      }
    }


    /**
     * Gets the coordinates associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param coordinates an array of 3*n values that will receive new coordinates
     */
    void getCoordinates(int index, double coordinates[]) {
  int offset = this.stride*index + coordinateOffset;
  int i, j, num = coordinates.length;

  for (i=0,j= offset;i < num;i +=3, j += this.stride)
      {
    coordinates[i= (double)this.vertexData[j];
    coordinates[i+1]= (double)this.vertexData[j+1];
    coordinates[i+2]= (double)this.vertexData[j+2];
      }
    }

    /**
     * Gets the coordinates associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param coordinates an array of vectors that will receive new coordinates
     */
    void getCoordinates(int index, Point3f coordinates[]) {
  int offset = this.stride*index + coordinateOffset;
  int i, j, num = coordinates.length;

  for (i=0,j= offset;i < num;i++, j += this.stride)
      {
    coordinates[i].x  = this.vertexData[j];
    coordinates[i].y  = this.vertexData[j+1];
    coordinates[i].z  = this.vertexData[j+2];
      }
    }

    /**
     * Gets the coordinates associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param coordinates an array of vectors that will receive new coordinates
     */
    void getCoordinates(int index, Point3d coordinates[]) {
  int offset = this.stride*index + coordinateOffset;
  int i, j, num = coordinates.length;

  for (i=0,j= offset;i < num;i++, j += this.stride)
      {
    coordinates[i].x  = (double)this.vertexData[j];
    coordinates[i].y  = (double)this.vertexData[j+1];
    coordinates[i].z  = (double)this.vertexData[j+2];
      }
    }

    /**
     * Gets the color associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param color an array of 3 or 4 values that will receive the new color
     */
    void getColor(int index, float color[]) {
  int offset = this.stride*index + colorOffset;

  color[0]= this.vertexData[offset];
  color[1]= this.vertexData[offset+1];
  color[2]= this.vertexData[offset+2];
  if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
      color[3]= this.vertexData[offset+3]/lastAlpha[0];
    }

    /**
     * Gets the color associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param color an array of 3 or 4 values that will receive the new color
     */
    void getColor(int index, byte color[]) {
  int offset = this.stride*index + colorOffset;

  color[0]= (byte)(this.vertexData[offset] * FloatToByteScale);
  color[1]= (byte)(this.vertexData[offset+1] * FloatToByteScale);
  color[2]= (byte)(this.vertexData[offset+2] * FloatToByteScale);
  if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
      color[3]= (byte)((this.vertexData[offset+3]/lastAlpha[0]) * FloatToByteScale);
    }

    /**
     * Gets the color associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param color a vector that will receive the new color
     */
    void getColor(int index, Color3f color) {
  int offset = this.stride*index + colorOffset;

  color.x = this.vertexData[offset];
  color.y = this.vertexData[offset+1];
  color.z = this.vertexData[offset+2];
    }

    /**
     * Gets the color associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param color a vector that will receive the new color
     */
    void getColor(int index, Color4f color) {
  int offset = this.stride*index + colorOffset;

  color.x = this.vertexData[offset];
  color.y = this.vertexData[offset+1];
  color.z = this.vertexData[offset+2];
  color.w= this.vertexData[offset+3]/lastAlpha[0];
    }

    /**
     * Gets the color associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param color a vector that will receive the new color
     */
    void getColor(int index, Color3b color) {
  int offset = this.stride*index + colorOffset;

  color.x = (byte)(this.vertexData[offset] * FloatToByteScale);
  color.y = (byte)(this.vertexData[offset+1] * FloatToByteScale);
  color.z = (byte)(this.vertexData[offset+2] * FloatToByteScale);
    }

    /**
     * Gets the color associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param color a vector that will receive the new color
     */
    void getColor(int index, Color4b color) {
  int offset = this.stride*index + colorOffset;

  color.x = (byte)(this.vertexData[offset] * FloatToByteScale);
  color.y = (byte)(this.vertexData[offset+1] * FloatToByteScale);
  color.z = (byte)(this.vertexData[offset+2] * FloatToByteScale);
  color.w = (byte)((this.vertexData[offset+3]/lastAlpha[0]) * FloatToByteScale);
    }

    /**
     * Gets the colors associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param colors an array of 3*n or 4*n values that will receive n new colors
     */
    void getColors(int index, float colors[]) {
  int offset = this.stride*index + colorOffset;
  int i, j, num = colors.length;
  float val = 1.0f/lastAlpha[0];

  if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
      {
    for (i=0, j= offset;i < num; i+= 4, j+= this.stride)
        {
      colors[i= this.vertexData[j];
      colors[i+1]= this.vertexData[j+1];
      colors[i+2]= this.vertexData[j+2];
      colors[i+3]= this.vertexData[j+3] * val;
        }
      }
  else
      {
    for (i=0, j= offset;i < num; i+= 3, j+= this.stride)
        {
      colors[i= this.vertexData[j];
      colors[i+1]= this.vertexData[j+1];
      colors[i+2]= this.vertexData[j+2];
        }
      }
    }

    /**
     * Gets the colors associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param colors an array of 3*n or 4*n values that will receive new colors
     */
    void getColors(int index, byte colors[]) {
  int offset = this.stride*index + colorOffset;
  int i, j, num = colors.length;
  float val = 1.0f/lastAlpha[0];


  if ((this.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
      {
    for (i=0, j= offset;i < num; i+= 4, j+= this.stride)
        {
      colors[i= (byte)(this.vertexData[j] * FloatToByteScale);
      colors[i+1]= (byte)(this.vertexData[j+1] * FloatToByteScale);
      colors[i+2]= (byte)(this.vertexData[j+2] * FloatToByteScale);
      colors[i+3]= (byte)((this.vertexData[j+3] * val) * FloatToByteScale);
        }
      }
  else
      {
    for (i=0, j= offset;i < num; i+= 3, j+= this.stride)
        {
      colors[i= (byte)(this.vertexData[j] * FloatToByteScale);
      colors[i+1]= (byte)(this.vertexData[j+1] * FloatToByteScale);
      colors[i+2]= (byte)(this.vertexData[j+2] * FloatToByteScale);
        }
      }
    }

    /**
     * Gets the colors associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param colors an array of vectors that will receive new colors
     */
    void getColors(int index, Color3f colors[]) {
  int offset = this.stride*index + colorOffset;
  int i, j, num = colors.length;

  for (i=0, j= offset;i < num; i++, j+= this.stride)
      {
    colors[i].x  = this.vertexData[j];
    colors[i].y  = this.vertexData[j+1];
    colors[i].z  = this.vertexData[j+2];
      }
    }

    /**
     * Gets the colors associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param colors an array of vectors that will receive new colors
     */
    void getColors(int index, Color4f colors[]) {
  int offset = this.stride*index + colorOffset;
  int i, j, num = colors.length;
  float val = 1.0f/lastAlpha[0];

  for (i=0, j= offset;i < num; i++, j+= this.stride)
      {
    colors[i].x  = this.vertexData[j];
    colors[i].y  = this.vertexData[j+1];
    colors[i].z  = this.vertexData[j+2];
    colors[i].w  = this.vertexData[j+3] * val;
      }
    }

    /**
     * Gets the colors associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param colors an array of vectors that will receive new colors
     */
    void getColors(int index, Color3b colors[]) {
  int offset = this.stride*index + colorOffset;
  int i, j, num = colors.length;

  for (i=0, j= offset;i < num; i++, j+= this.stride)
      {
    colors[i].x  = (byte)(this.vertexData[j] * FloatToByteScale);
    colors[i].y  = (byte)(this.vertexData[j+1] * FloatToByteScale);
    colors[i].z  = (byte)(this.vertexData[j+2] * FloatToByteScale);
      }
    }

    /**
     * Gets the colors associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param colors an array of vectors that will receive new colors
     */
    void getColors(int index, Color4b colors[]) {
  int offset = this.stride*index + colorOffset;
  int i, j, num = colors.length;
  float val = 1.0f/lastAlpha[0];

  for (i=0, j= offset;i < num; i++, j+= this.stride)
      {
    colors[i].x  = (byte)(this.vertexData[j] * FloatToByteScale);
    colors[i].y  = (byte)(this.vertexData[j+1] * FloatToByteScale);
    colors[i].z  = (byte)(this.vertexData[j+2] * FloatToByteScale);
    colors[i].w  = (byte)(this.vertexData[j+3] * val * FloatToByteScale);
      }
    }

    /**
     * Gets the normal associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param normal array that will receive the new normal
     */
    void getNormal(int index, float normal[]) {
  int offset = this.stride*index + normalOffset;

  normal[0]= this.vertexData[offset];
  normal[1]= this.vertexData[offset+1];
  normal[2]= this.vertexData[offset+2];
    }

    /**
     * Gets the normal associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param normal the vector that will receive the new normal
     */
    void getNormal(int index, Vector3f normal) {
  int offset = this.stride*index + normalOffset;

  normal.x= this.vertexData[offset];
  normal.y= this.vertexData[offset+1];
  normal.z= this.vertexData[offset+2];
    }

    /**
     * Gets the normals associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param normals array that will receive the new normals
     */
    void getNormals(int index, float normals[]) {
  int offset = this.stride*index + normalOffset;
  int i, j, num = normals.length;

  for (i=0, j= offset;i < num;i+=3, j+= this.stride)
      {
    normals[i= this.vertexData[j];
    normals[i+1]= this.vertexData[j+1];
    normals[i+2]= this.vertexData[j+2];
      }
    }

    /**
     * Gets the normals associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param normals the vector that will receive the new normals
     */
    void getNormals(int index, Vector3f normals[]) {
  int offset = this.stride*index + normalOffset;
  int i, j, num = normals.length;

  for (i=0, j= offset;i < num;i++, j+= this.stride)
      {
    normals[i].x= this.vertexData[j];
    normals[i].y= this.vertexData[j+1];
    normals[i].z= this.vertexData[j+2];
      }
    }

    /**
     * Gets the texture co-ordinate associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param texCoord array that will receive the new texture co-ordinate
     */
    void getTextureCoordinate(int texCoordSet, int index, float texCoord[]) {
  int offset = this.stride*index + textureOffset +
      texCoordSet * texCoordStride;

  texCoord[0]= this.vertexData[offset];
  texCoord[1]= this.vertexData[offset+1];
  if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
      texCoord[2]= this.vertexData[offset+2];

  } else if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE_4)
        != 0) {
      texCoord[2]= this.vertexData[offset+2];
      texCoord[3]= this.vertexData[offset+3];
  }
    }

    /**
     * Gets the texture co-ordinate associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param texCoord the vector that will receive the new texture co-ordinates
     */
    void getTextureCoordinate(int texCoordSet, int index, TexCoord2f texCoord) {
  int offset = this.stride*index + textureOffset +
      texCoordSet * texCoordStride;

  texCoord.x= this.vertexData[offset];
  texCoord.y= this.vertexData[offset+1];
    }

    /**
     * Gets the texture co-ordinate associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param texCoord the vector that will receive the new texture co-ordinates
     */
    void getTextureCoordinate(int texCoordSet, int index, TexCoord3f texCoord) {
  int offset = this.stride*index + textureOffset +
      texCoordSet * texCoordStride;

  texCoord.x= this.vertexData[offset];
  texCoord.y= this.vertexData[offset+1];
  texCoord.z= this.vertexData[offset+2];
    }

    /**
     * Gets the texture co-ordinate associated with the vertex at
     * the specified index.
     * @param index the vertex index
     * @param texCoord the vector that will receive the new texture co-ordinates
     */
    void getTextureCoordinate(int texCoordSet, int index, TexCoord4f texCoord) {
  int offset = this.stride*index + textureOffset +
      texCoordSet * texCoordStride;

  texCoord.x= this.vertexData[offset];
  texCoord.y= this.vertexData[offset+1];
  texCoord.z= this.vertexData[offset+2];
  texCoord.w= this.vertexData[offset+3];
    }

    /**
     * Gets the texture co-ordinates associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param texCoords array that will receive the new texture co-ordinates
     */
    void getTextureCoordinates(int texCoordSet, int index, float texCoords[]) {
  int offset = this.stride*index + textureOffset +
      texCoordSet * texCoordStride;
  int i, j, num = texCoords.length;

  if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
    for (i=0, j= offset;i < num;i+=4, j+= this.stride)
        {
      texCoords[i]= this.vertexData[j];
      texCoords[i+1]= this.vertexData[j+1];
      texCoords[i+2]= this.vertexData[j+2];
      texCoords[i+3]= this.vertexData[j+3];
        }
  } else if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE_3)
        != 0) {
    for (i=0, j= offset;i < num;i+=3, j+= this.stride)
        {
      texCoords[i]= this.vertexData[j];
      texCoords[i+1]= this.vertexData[j+1];
      texCoords[i+2]= this.vertexData[j+2];
        }
  } else {
    for (i=0, j= offset;i < num;i+=2, j+= this.stride)
        {
      texCoords[i]= this.vertexData[j];
      texCoords[i+1]= this.vertexData[j+1];
        }
  }
    }

    /**
     * Gets the texture co-ordinates associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param texCoords the vector that will receive the new texture co-ordinates
     */
    void getTextureCoordinates(int texCoordSet, int index,
          TexCoord2f texCoords[]) {
  int offset = this.stride*index + textureOffset +
      texCoordSet * texCoordStride;
  int i, j, num = texCoords.length;

  for (i=0, j= offset;i < num;i++, j+= this.stride)
      {
    texCoords[i].x= this.vertexData[j];
    texCoords[i].y= this.vertexData[j+1];
      }
    }

    /**
     * Gets the texture co-ordinates associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param texCoords the vector that will receive the new texture co-ordinates
     */
    void getTextureCoordinates(int texCoordSet, int index, TexCoord3f texCoords[]) {
  int offset = this.stride*index + textureOffset +
      texCoordSet * texCoordStride;
  int i, j, num = texCoords.length;

  for (i=0, j= offset;i < num;i++, j+= this.stride)
      {
    texCoords[i].x= this.vertexData[j];
    texCoords[i].y= this.vertexData[j+1];
    texCoords[i].z= this.vertexData[j+2];
      }
    }

    /**
     * Gets the texture co-ordinates associated with the vertices starting at
     * the specified index.
     * @param index the vertex index
     * @param texCoords the vector that will receive the new texture co-ordinates
     */
    void getTextureCoordinates(int texCoordSet, int index, TexCoord4f texCoords[]) {
  int offset = this.stride*index + textureOffset +
      texCoordSet * texCoordStride;
  int i, j, num = texCoords.length;

  for (i=0, j= offset;i < num;i++, j+= this.stride)
      {
    texCoords[i].x= this.vertexData[j];
    texCoords[i].y= this.vertexData[j+1];
    texCoords[i].z= this.vertexData[j+2];
    texCoords[i].w= this.vertexData[j+3];
      }
    }

    void getTextureCoordinates(int texCoordSet, int index,
                                        Point2f texCoords[]) {
        int offset = this.stride*index + textureOffset +
                        texCoordSet * texCoordStride;
        int i, j, num = texCoords.length;

        for (i=0, j= offset;i < num;i++, j+= this.stride)
            {
                texCoords[i].x= this.vertexData[j];
                texCoords[i].y= this.vertexData[j+1];
            }
    }

    void getTextureCoordinates(int texCoordSet, int index, Point3f texCoords[]) {
        int offset = this.stride*index + textureOffset +
                        texCoordSet * texCoordStride;
        int i, j, num = texCoords.length;

        for (i=0, j= offset;i < num;i++, j+= this.stride)
            {
                texCoords[i].x= this.vertexData[j];
                texCoords[i].y= this.vertexData[j+1];
                texCoords[i].z= this.vertexData[j+2];
            }
    }


    /**
     * Gets the vertex attribute associated with the vertex at
     * the specified index in the specified vertex attribute number
     * for this object.
     */
    public void getVertexAttr(int vertexAttrNum, int index,
            float[] vertexAttr) {

  int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum];
        int size = vertexAttrSizes[vertexAttrNum];

  for (int i = 0; i < size; i++) {
      vertexAttr[i] = this.vertexData[offset+i];

        }

    }

    /**
     * Gets the vertex attribute associated with the vertex at
     * the specified index in the specified vertex attribute number
     * for this object.
     */
    public void getVertexAttr(int vertexAttrNum, int index,
            Point2f vertexAttr) {

  int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum];

  vertexAttr.x = this.vertexData[offset];
  vertexAttr.y = this.vertexData[offset+1];

    }

    /**
     * Gets the vertex attribute associated with the vertex at
     * the specified index in the specified vertex attribute number
     * for this object.
     */
    public void getVertexAttr(int vertexAttrNum, int index,
            Point3f vertexAttr) {

  int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum];

  vertexAttr.x = this.vertexData[offset];
  vertexAttr.y = this.vertexData[offset+1];
  vertexAttr.z = this.vertexData[offset+2];

    }

    /**
     * Gets the vertex attribute associated with the vertex at
     * the specified index in the specified vertex attribute number
     * for this object.
     */
    public void getVertexAttr(int vertexAttrNum, int index,
            Point4f vertexAttr) {

  int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum];

  vertexAttr.x = this.vertexData[offset];
  vertexAttr.y = this.vertexData[offset+1];
  vertexAttr.z = this.vertexData[offset+2];
  vertexAttr.w = this.vertexData[offset+3];

    }

    /**
     * Gets the vertex attributes associated with the vertices starting at
     * the specified index in the specified vertex attribute number
     * for this object.
     */
    public void getVertexAttrs(int vertexAttrNum, int index,
             float[] vertexAttrs) {

  int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum];
        int size = vertexAttrSizes[vertexAttrNum];
        int i, j, k;

        for (i = 0, j = offset;
       ((i < vertexAttrs.length) && (j < this.vertexData.length)) ;
       i += size, j += this.stride) {
            for (k = 0; k < size; k++) {
                vertexAttrs[i+k] = this.vertexData[j+k];
            }
        }

    }

    /**
     * Gets the vertex attributes associated with the vertices starting at
     * the specified index in the specified vertex attribute number
     * for this object.
     */
    public void getVertexAttrs(int vertexAttrNum, int index,
             Point2f[] vertexAttrs) {

  int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum];
        int i, j;

        for (i = 0, j = offset;
       ((i < vertexAttrs.length) && (j < this.vertexData.length)) ;
       i++, j += this.stride) {
      vertexAttrs[i].x = this.vertexData[j];
      vertexAttrs[i].y = this.vertexData[j+1];
        }

    }

    /**
     * Gets the vertex attributes associated with the vertices starting at
     * the specified index in the specified vertex attribute number
     * for this object.
     */
    public void getVertexAttrs(int vertexAttrNum, int index,
             Point3f[] vertexAttrs) {

  int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum];
        int i, j;

        for (i = 0, j = offset;
       ((i < vertexAttrs.length) && (j < this.vertexData.length)) ;
       i++, j += this.stride) {
      vertexAttrs[i].x = this.vertexData[j];
      vertexAttrs[i].y = this.vertexData[j+1];
      vertexAttrs[i].z = this.vertexData[j+2];
        }

    }

    /**
     * Gets the vertex attributes associated with the vertices starting at
     * the specified index in the specified vertex attribute number
     * for this object.
     */
    public void getVertexAttrs(int vertexAttrNum, int index,
             Point4f[] vertexAttrs) {

  int offset = this.stride*index + vertexAttrOffsets[vertexAttrNum];
        int i, j;

        for (i = 0, j = offset;
       ((i < vertexAttrs.length) && (j < this.vertexData.length)) ;
       i++, j += this.stride) {
      vertexAttrs[i].x = this.vertexData[j];
      vertexAttrs[i].y = this.vertexData[j+1];
      vertexAttrs[i].z = this.vertexData[j+2];
      vertexAttrs[i].w = this.vertexData[j+3];
        }

    }


    /**
     * Updates geometry array data.
     */
    void updateData(GeometryUpdater updater) {
  boolean nullGeo = false;

   // Add yourself to obtain the geometry lock
   // and Thread.currentThread().sleep until you get the lock
   geomLock.getLock();

  inUpdater = true;
  updater.updateData((Geometry)source);
  inUpdater = false;
  if ((vertexFormat & GeometryArray.BY_REFERENCE) != 0) {
      if((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) {
    // XXXX: handle the nio buffer
    if (!(this instanceof IndexedGeometryArrayRetained) ||
        (vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) {
        if (((vertexFormat & GeometryArray.INTERLEAVED) != 0)) {
      setupMirrorInterleavedColorPointer(false);
      nullGeo = (interleavedFloatBufferImpl == null);
        }
        else {
      setupMirrorColorPointer((vertexType & COLOR_DEFINED), false);
      nullGeo = ((vertexType & GeometryArrayRetained.VERTEX_DEFINED) == 0);
        }
    }
      }
      else {
    if (!(this instanceof IndexedGeometryArrayRetained) ||
        (vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) {
        if (((vertexFormat & GeometryArray.INTERLEAVED) != 0)) {
      setupMirrorInterleavedColorPointer(false);
      nullGeo = (interLeavedVertexData == null);
        }
        else {
      setupMirrorVertexPointer(vertexType & VERTEX_DEFINED);
      setupMirrorColorPointer((vertexType & COLOR_DEFINED), false);
      setupMirrorNormalPointer(vertexType & NORMAL_DEFINED);
      setupMirrorTexCoordPointer(texCoordType);
                        setupMirrorVertexAttrPointer(vertexAttrType);
      nullGeo = ((vertexType & GeometryArrayRetained.VERTEX_DEFINED) == 0);
        }
    }
      }

            //NVaidya
            // User may or may not have changed indices in updater callback.
            // We need to presume that the user may indeed have and, thus, will
            // need to recompute maxCoordIndex unconditionally while
            // geomLock is still locked.
            if ((vertexFormat & GeometryArray.BY_REFERENCE_INDICES) != 0) {
                assert (this instanceof IndexedGeometryArrayRetained);

                if (((IndexedGeometryArrayRetained)this).getCoordIndicesRef() == null) {
                    nullGeo = true;
                }
                ((IndexedGeometryArrayRetained)this).doPostUpdaterUpdate();
            }
  }

        dirtyFlag |= VERTEX_CHANGED;
  colorChanged = 0xffff;
  geomLock.unLock();

  if (source != null && source.isLive()) {
      processCoordsChanged(nullGeo);
      sendDataChangedMessage(true);
  }
    }

    boolean intersectBoundingBox( Point3d coordinates[],
          BoundingBox box,
          double dist[],
          Point3d iPnt) {
  int i;
  int out[] = new int[6];

  //Do trivial vertex test.
  for(i=0; i<6; i++)
      out[i] = 0;
  for(i=0; i<coordinates.length; i++) {
      if((coordinates[i].x >= box.lower.x) && (coordinates[i].x <= box.upper.x) &&
         (coordinates[i].y >= box.lower.y) && (coordinates[i].y <= box.upper.y) &&
         (coordinates[i].z >= box.lower.z) && (coordinates[i].z <= box.upper.z))
    // We're done! It's inside the boundingbox.
    return true;
      else {
    if(coordinates[i].x < box.lower.x)
        out[0]++; // left
    if(coordinates[i].y < box.lower.y)
        out[1]++; // bottom
    if(coordinates[i].z < box.lower.z)
        out[2]++; // back
    if(coordinates[i].x > box.upper.x)
        out[3]++; // right
    if(coordinates[i].y > box.upper.y)
        out[4]++; // top
    if(coordinates[i].z > box.upper.z)
        out[5]++; // front
      }

  }

  if((out[0] == coordinates.length) || (out[1] == coordinates.length) ||
     (out[2] == coordinates.length) || (out[3] == coordinates.length) ||
     (out[4] == coordinates.length) || (out[5] == coordinates.length))
      // we're done. primitive is outside of boundingbox.
      return false;

  // Setup bounding planes.
  Point3d pCoor[] = new Point3d[4];
  for(i=0; i<4; i++)
      pCoor[i] = new Point3d();

  Point3d boxCenter = new Point3d();
  box.getCenter(boxCenter);

  // left plane.
  pCoor[0].set(box.lower.x, box.lower.y, box.lower.z);
  pCoor[1].set(box.lower.x, box.lower.y, box.upper.z);
  pCoor[2].set(box.lower.x, box.upper.y, box.upper.z);
  pCoor[3].set(box.lower.x, box.upper.y, box.lower.z);


  if (intersectPolygon(pCoor, coordinates)) {
      if (dist != null) {
      computeMinDistance(pCoor, boxCenter, null, dist, iPnt);
      }
      return true;
  }

  // right plane.
  pCoor[0].set(box.upper.x, box.lower.y, box.lower.z);
  pCoor[1].set(box.upper.x, box.upper.y, box.lower.z);
  pCoor[2].set(box.upper.x, box.upper.y, box.upper.z);
  pCoor[3].set(box.upper.x, box.lower.y, box.upper.z);
  if (intersectPolygon(pCoor, coordinates)) {
      if (dist != null) {
      computeMinDistance(pCoor, boxCenter, null, dist, iPnt);
      }
      return true;
  }

  // bottom plane.
  pCoor[0].set(box.upper.x, box.lower.y, box.upper.z);
  pCoor[1].set(box.lower.x, box.lower.y, box.upper.z);
  pCoor[2].set(box.lower.x, box.lower.y, box.lower.z);
  pCoor[3].set(box.upper.x, box.lower.y, box.lower.z);
  if (intersectPolygon(pCoor, coordinates)) {
      if (dist != null) {
      computeMinDistance(pCoor, boxCenter, null, dist, iPnt);
      }
      return true;
  }
  // top plane.
  pCoor[0].set(box.upper.x, box.upper.y, box.upper.z);
  pCoor[1].set(box.upper.x, box.upper.y, box.lower.z);
  pCoor[2].set(box.lower.x, box.upper.y, box.lower.z);
  pCoor[3].set(box.lower.x, box.upper.y, box.upper.z);
  if (intersectPolygon(pCoor, coordinates)) {
      if (dist != null) {
      computeMinDistance(pCoor, boxCenter, null, dist, iPnt);
      }
      return true;
  }

  // front plane.
  pCoor[0].set(box.upper.x, box.upper.y, box.upper.z);
  pCoor[1].set(box.lower.x, box.upper.y, box.upper.z);
  pCoor[2].set(box.lower.x, box.lower.y, box.upper.z);
  pCoor[3].set(box.upper.x, box.lower.y, box.upper.z);
  if (intersectPolygon(pCoor, coordinates)) {
      if (dist != null) {
      computeMinDistance(pCoor, boxCenter, null, dist, iPnt);
      }
      return true;
  }

  // back plane.
  pCoor[0].set(box.upper.x, box.upper.y, box.lower.z);
  pCoor[1].set(box.upper.x, box.lower.y, box.lower.z);
  pCoor[2].set(box.lower.x, box.lower.y, box.lower.z);
  pCoor[3].set(box.lower.x, box.upper.y, box.lower.z);
  if (intersectPolygon(pCoor, coordinates)) {
      if (dist != null) {
      computeMinDistance(pCoor, boxCenter, null, dist, iPnt);
      }
      return true;
  }
  return false;
    }


    boolean intersectBoundingSphere(Point3d coordinates[],
            BoundingSphere sphere,
            double dist[],
            Point3d iPnt)
    {
  int i, j;
  Vector3d tempV3D = new Vector3d();
  boolean esFlag;

  //Do trivial vertex test.

  for (i=0; i<coordinates.length; i++) {
      tempV3D.x = coordinates[i].x - sphere.center.x;
      tempV3D.y = coordinates[i].y - sphere.center.y;
      tempV3D.z = coordinates[i].z - sphere.center.z;

      if (tempV3D.length() <= sphere.radius) {
    // We're done! It's inside the boundingSphere.
    if (dist != null) {
        computeMinDistance(coordinates,
               sphere.getCenter(),
               null, dist, iPnt);
    }

    return true;
      }
  }

  for (i=0; i<coordinates.length; i++) {
      if (i < (coordinates.length-1))
    esFlag = edgeIntersectSphere(sphere, coordinates[i],
               coordinates[i+1]);
      else
    esFlag = edgeIntersectSphere(sphere, coordinates[i],
               coordinates[0]);
      if (esFlag == true) {
    if (dist != null) {
        computeMinDistance(coordinates,
               sphere.getCenter(),
               null,
               dist, iPnt);
    }

    return true;
      }
  }


  if (coordinates.length < 3) {
      return false; // We're done with line.
  }

      // Find rho.
      // Compute plane normal.
      Vector3d vec0 = new Vector3d(); // Edge vector from point 0 to point 1;
      Vector3d vec1 = new Vector3d(); // Edge vector from point 0 to point 2 or 3;
      Vector3d pNrm = new Vector3d();
      Vector3d pa = new Vector3d();
      Point3d q = new Point3d();
      double nLenSq, pqLen, pNrmDotPa, tq;

      // compute plane normal for coordinates.
      for(i=0; i<coordinates.length-1;) {
    vec0.x = coordinates[i+1].x - coordinates[i].x;
    vec0.y = coordinates[i+1].y - coordinates[i].y;
    vec0.z = coordinates[i+1].z - coordinates[i++].z;
    if(vec0.length() > 0.0)
        break;
      }

      for(j=i; j<coordinates.length-1; j++) {
    vec1.x = coordinates[j+1].x - coordinates[j].x;
    vec1.y = coordinates[j+1].y - coordinates[j].y;
    vec1.z = coordinates[j+1].z - coordinates[j].z;
    if(vec1.length() > 0.0)
        break;
      }

      if(j == (coordinates.length-1)) {
    // System.err.println("(1) Degenerate polygon.");
    return false// Degenerate polygon.
      }

      /*
        for(i=0; i<coordinates.length; i++)
        System.err.println("coordinates P" + i + " " + coordinates[i]);
        for(i=0; i<coord2.length; i++)
        System.err.println("coord2 P" + i + " " + coord2[i]);
        */

      pNrm.cross(vec0,vec1);

      nLenSq = pNrm.lengthSquared();
      if( nLenSq == 0.0) {
    // System.err.println("(2) Degenerate polygon.");
    return false// Degenerate polygon.
      }

      pa.x = coordinates[0].x - sphere.center.x;
      pa.y = coordinates[0].y - sphere.center.y;
      pa.z = coordinates[0].z - sphere.center.z;

      pNrmDotPa = pNrm.dot(pa);

      pqLen = Math.sqrt(pNrmDotPa * pNrmDotPa/ nLenSq);

      if(pqLen > sphere.radius) {
    return false;
      }

      tq = pNrmDotPa / nLenSq;

      q.x = sphere.center.x + tq * pNrm.x;
      q.y = sphere.center.y + tq * pNrm.y;
      q.z = sphere.center.z + tq * pNrm.z;

      // PolyPnt2D Test.
      if (pointIntersectPolygon2D( pNrm, coordinates, q)) {
    if (dist != null) {
        computeMinDistance(coordinates,
               sphere.getCenter(),
               pNrm,
               dist, iPnt);
    }
    return true;
      }
      return false;

    }


    boolean intersectBoundingPolytope(Point3d coordinates[],
              BoundingPolytope polytope,
              double dist[],
              Point3d iPnt)
    {
  boolean debug = false;

  Point4d tP4d = new Point4d();

  // this is a multiplier to the halfplane distance coefficients
  double distanceSign = -1.0;

      if(coordinates.length == 2) {
    // we'll handle line separately.
    if (polytope.intersect( coordinates[0],
          coordinates[1], tP4d)) {
      if (dist != null) {
        polytope.getCenter(iPnt);

        double x = tP4d.x - iPnt.x;
        double y = tP4d.y - iPnt.y;
        double z = tP4d.z - iPnt.z;
        dist[0] = Math.sqrt(x * x + y * y + z * z);
        iPnt.x = tP4d.x;
        iPnt.y = tP4d.y;
        iPnt.z = tP4d.z;
      }
        return true;
    }
    return false;
      }

      // It is a triangle or a quad.

      // first test to see if any of the coordinates are all inside of the
      // intersection polytope's half planes
      // essentially do a matrix multiply of the constraintMatrix K*3 with
      // the input coordinates 3*1 = K*1 vector

      if (debug) {
    System.err.println("The value of the input vertices are: ");
    for(int i=0; i < coordinates.length; i++) {
        System.err.println("The " +i+ " th vertex is: " + coordinates[i]);
    }

    System.err.println("The value of the input bounding Polytope's planes =");
    for(int i=0; i < polytope.planes.length; i++) {
        System.err.println("The " +i+ " th plane is: " + polytope.planes[i]);
    }

      }

      // the direction for the intersection cost function
      double centers[] = new double[4];
      centers[0] = 0.8; centers[1] = 0.9; centers[2] = 1.1; centers[3] = 1.2;

      boolean intersection = true;
      boolean PreTest = false;

      if(PreTest) {
    // for each coordinate, test it with each half plane
    for( int i=0; i < coordinates.length; i++) {
        for (int j=0; j < polytope.planes.length; j++) {
      if ( ( polytope.planes[j].x * coordinates[i].x +
             polytope.planes[j].y * coordinates[i].y +
             polytope.planes[j].z*coordinates[i].z) <=
           (distanceSign)*polytope.planes[j].w){
          // the point satisfies this particular hyperplane
          intersection = true;
      } else {
          // the point fails this hyper plane try with a new hyper plane
          intersection = false;
          break;
      }
        }
        if(intersection) {
      // a point was found to be completely inside the bounding hull
      if (dist != null) {
          computeMinDistance(coordinates,
                 polytope.getCenter(),
                 null,
                 dist, iPnt);
      }
      return true;
        }
    }
      }  // end of pretest

      // at this point all points are outside of the bounding hull
      // build the problem tableau for the linear program

      int numberCols = polytope.planes.length + 2 + coordinates.length + 1;
      int numberRows = 1 + coordinates.length;

      double problemTableau[][] = new double[numberRows][numberCols];

      // compute -Mtrans = -A*P

      for( int i = 0; i < polytope.planes.length; i++) {
    for( int j=0; j < coordinates.length;  j++) {
        problemTableau[j][i] = (-1.0)* (polytope.planes[i].x*coordinates[j].x+
                polytope.planes[i].y*coordinates[j].y+
                polytope.planes[i].z*coordinates[j].z);
    }
      }

      // add the other rows
      for(int i = 0; i < coordinates.length; i++) {
    problemTableau[i][polytope.planes.length] = -1.0;
    problemTableau[i][polytope.planes.length + 1] 1.0;

    for(int j=0; j < coordinates.length; j++) {
        if ( i==j ) {
      problemTableau[i][j + polytope.planes.length + 2] = 1.0;
        } else {
      problemTableau[i][j + polytope.planes.length + 2] = 0.0;
        }

        // place the last column elements the Ci's
        problemTableau[i][numberCols - 1] = centers[i];
    }
      }

      // place the final rows value
      for(int j = 0; j < polytope.planes.length; j++) {
    problemTableau[numberRows - 1][j] =
        (distanceSign)*polytope.planes[j].w;
      }
      problemTableau[numberRows - 1][polytope.planes.length] 1.0;
      problemTableau[numberRows - 1][polytope.planes.length+1] = -1.0;
      for(int j = 0; j < coordinates.length; j++) {
    problemTableau[numberRows - 1][polytope.planes.length+2+j] = 0.0;
      }

      if(debug) {
    System.err.println("The value of the problem tableau is: " );
    for(int i=0; i < problemTableau.length; i++) {
        for(int j=0; j < problemTableau[0].length; j++) {
      System.err.print(problemTableau[i][j] + "  ");
        }
        System.err.println();
    }
      }

      double distance = generalStandardSimplexSolver(problemTableau,
                 Float.NEGATIVE_INFINITY);
      if(debug) {
    System.err.println("The value returned by the general standard simplex = " +
           distance);
      }
      if (distance == Float.POSITIVE_INFINITY) {
    return false;
      }
      if (dist != null) {
    computeMinDistance(coordinates,
           polytope.getCenter(),
           null,
           dist, iPnt);
      }
      return true;

  }


    // optimized version using arrays of doubles, but using the standard simplex
    // method to solve the LP tableau.  This version has not been optimized to
    // work with a particular size input tableau and is much slower than some
    // of the other variants...supposedly
    double generalStandardSimplexSolver(double problemTableau[][],
          double stopingValue) {
  boolean debug = false;
  int numRow = problemTableau.length;
  int numCol = problemTableau[0].length;
  boolean optimal = false;
  int i, pivotRowIndex, pivotColIndex;
  double maxElement, element, endElement, ratio, prevRatio;
  double multiplier;

  if(debug) {
      System.err.println("The number of rows is : " + numRow);
      System.err.println("The number of columns is : " + numCol);
  }

  // until the optimal solution is found continue to do
  // iterations of the simplex method
  while(!optimal) {

      if(debug) {
    System.err.println("input problem tableau is:");
    for(int k=0; k < numRow; k++) {
        for(int j=0; j < numCol; j++) {
      System.err.println("kth, jth value is:" +k+" "+j+" : " +
             problemTableau[k][j]);
        }
    }
      }

      // test to see if the current solution is optimal
      // check all bottom row elements except the right most one and
      // if all positive or zero its optimal
      for(i = 0, maxElement = 0, pivotColIndex = -1; i < numCol - 1; i++) {
    // a bottom row element
    element = problemTableau[numRow - 1][i];
    if( element < maxElement) {
        maxElement = element;
        pivotColIndex = i;
    }
      }

      // if there is no negative non-zero element then we
      // have found an optimal solution (the last row of the tableau)
      if(pivotColIndex == -1) {
    // found an optimal solution
    //System.err.println("Found an optimal solution");
    optimal = true;
      }

      //System.err.println("The value of maxElement is:" + maxElement);

      if(!optimal) {
    // Case when the solution is not optimal but not known to be
    // either unbounded or infeasable

    // from the above we have found the maximum negative element in
    // bottom row, we have also found the column for this value
    // the pivotColIndex represents this

    // initialize the values for the algorithm, -1 for pivotRowIndex
    // indicates no solution

    prevRatio = Float.POSITIVE_INFINITY;
    ratio = 0.0;
    pivotRowIndex = -1;

    // note if all of the elements in the pivot column are zero or
    // negative the problem is unbounded.
    for(i = 0; i < numRow - 1; i++) {
        element = problemTableau[i][pivotColIndex]; // r value
        endElement = problemTableau[i][numCol-1]; // s value

        // pivot according to the rule that we want to choose the row
        // with smallest s/r ratio see third case
        // currently we ignore valuse of r==0 (case 1) and cases where the
        // ratio is negative, i.e. either r or s are negative (case 2)
        if(element == 0) {
      if(debug) {
          System.err.println("Division by zero has occurred");
          System.err.println("Within the linear program solver");
          System.err.println("Ignoring the zero as a potential pivot");
      }
        } else if ( (element < 0.0) || (endElement < 0.0) ){
      if(debug) {
          System.err.println("Ignoring cases where element is negative");
          System.err.println("The value of element is: " + element);
          System.err.println("The value of end Element is: " + endElement);
      }
        } else {
      ratio = endElement/element;  // should be s/r
      if(debug) {
          System.err.println("The value of element is: " + element);
          System.err.println("The value of endElement is: " + endElement);
          System.err.println("The value of ratio is: " + ratio);
          System.err.println("The value of prevRatio is: " + prevRatio);
          System.err.println("Value of ratio <= prevRatio is :" +
                 (ratio <= prevRatio));
      }
      if(ratio <= prevRatio) {
          if(debug) {
        System.err.println("updating prevRatio with ratio");
          }
          prevRatio = ratio;
          pivotRowIndex = i;
      }
        }
    }

    // if the pivotRowIndex is still -1 then we know the pivotColumn
    // has no viable pivot points and the solution is unbounded or
    // infeasable (all pivot elements were either zero or negative or
    // the right most value was negative (the later shouldn't happen?)
    if(pivotRowIndex == -1) {
        if(debug) {
      System.err.println("UNABLE TO FIND SOLUTION");
      System.err.println("The system is infeasable or unbounded");
        }
        return(Float.POSITIVE_INFINITY);
    }

    // we now have the pivot row and col all that remains is
    // to divide through by this value and subtract the appropriate
    // multiple of the pivot row from all other rows to obtain
    // a tableau which has a column of all zeros and one 1 in the
    // intersection of pivot row and col

    // divide through by the pivot value
    double pivotValue = problemTableau[pivotRowIndex][pivotColIndex];

    if(debug) {
        System.err.println("The value of row index is: " + pivotRowIndex);
        System.err.println("The value of col index is: " + pivotColIndex);
        System.err.println("The value of pivotValue is: " + pivotValue);
    }
    // divide through by s on the pivot row to obtain a 1 in pivot col
    for(i = 0; i < numCol; i++) {
        problemTableau[pivotRowIndex][i] =
      problemTableau[pivotRowIndex][i] / pivotValue;
    }

    // subtract appropriate multiple of pivot row from all other rows
    // to zero out all rows except the final row and the pivot row
    for(i = 0; i < numRow; i++) {
        if(i != pivotRowIndex) {
      multiplier = problemTableau[i][pivotColIndex];
      for(int j=0; j < numCol; j++) {
          problemTableau[i][j] = problemTableau[i][j] -
        multiplier * problemTableau[pivotRowIndex][j];
      }
        }
    }
      }
      // case when the element is optimal
  }
  return(problemTableau[numRow - 1][numCol - 1]);
    }



    boolean edgeIntersectSphere(BoundingSphere sphere, Point3d start,
        Point3d end)
  {
      double abLenSq, acLenSq, apLenSq, abDotAp, radiusSq;
      Vector3d ab = new Vector3d();
      Vector3d ap = new Vector3d();

      ab.x = end.x - start.x;
      ab.y = end.y - start.y;
      ab.z = end.z - start.z;

      ap.x = sphere.center.x - start.x;
      ap.y = sphere.center.y - start.y;
      ap.z = sphere.center.z - start.z;

      abDotAp = ab.dot(ap);

      if(abDotAp < 0.0) {
    return false; // line segment points away from sphere.
      }

      abLenSq = ab.lengthSquared();
      acLenSq = abDotAp * abDotAp / abLenSq;

      if(acLenSq < abLenSq) {
    return false; // C doesn't lies between end points of edge.
      }

      radiusSq = sphere.radius * sphere.radius;
      apLenSq = ap.lengthSquared();

      if((apLenSq - acLenSq) <= radiusSq) {
    return true;
      }

      return false;

  }


    double det2D(Point2d a, Point2d b, Point2d p)
  {
      return (((p).x - (a).x) * ((a).y - (b).y) +
        ((a).y - (p).y) * ((a).x - (b).x));
  }

    // Assume coord is CCW.
    boolean pointIntersectPolygon2D(Vector3d normal, Point3d[] coord,
            Point3d point)
  {

      double  absNrmX, absNrmY, absNrmZ;
      Point2d coord2D[] = new Point2d[coord.length];
      Point2d pnt = new Point2d();

      int i, j, axis;

      // Project 3d points onto 2d plane.
      // Note : Area of polygon is not preserve in this projection, but
      // it doesn't matter here.

      // Find the axis of projection.
      absNrmX = Math.abs(normal.x);
      absNrmY = Math.abs(normal.y);
      absNrmZ = Math.abs(normal.z);

      if(absNrmX > absNrmY)
    axis = 0;
      else
    axis = 1;

      if(axis == 0) {
    if(absNrmX < absNrmZ)
        axis = 2;
      }
      else if(axis == 1) {
    if(absNrmY < absNrmZ)
        axis = 2;
      }

      // System.err.println("Normal " + normal + " axis " + axis );

      for(i=0; i<coord.length; i++) {
    coord2D[i] = new Point2d();

    switch (axis) {
    case 0:
        coord2D[i].x = coord[i].y;
        coord2D[i].y = coord[i].z;
        break;

    case 1:
        coord2D[i].x = coord[i].x;
        coord2D[i].y = coord[i].z;
        break;

    case 2:
        coord2D[i].x = coord[i].x;
        coord2D[i].y = coord[i].y;
        break;
    }

    // System.err.println("i " + i + " u " + uCoor[i] + " v " + vCoor[i]);
      }


      switch (axis) {
      case 0:
    pnt.x = point.y;
    pnt.y = point.z;
    break;

      case 1:
    pnt.x = point.x;
    pnt.y = point.z;
    break;

      case 2:
    pnt.x = point.x;
    pnt.y = point.y;
    break;
      }

      // Do determinant test.
      for(j=0; j<coord.length; j++) {
    if(j<(coord.length-1))
        if(det2D(coord2D[j], coord2D[j+1], pnt)>0.0)
      ;
        else
      return false;
    else
        if(det2D(coord2D[j], coord2D[0], pnt)>0.0)
      ;
        else
      return false;
      }

      return true;

  }


    boolean edgeIntersectPlane(Vector3d normal, Point3d pnt, Point3d start,
             Point3d end, Point3d iPnt)
  {

      Vector3d tempV3d = new Vector3d();
      Vector3d direction = new Vector3d();
      double pD, pNrmDotrDir, tr;

      // Compute plane D.
      tempV3d.set(pnt);
      pD = normal.dot(tempV3d);

      direction.x = end.x - start.x;
      direction.y = end.y - start.y;
      direction.z = end.z - start.z;

      pNrmDotrDir = normal.dot(direction);

      // edge is parallel to plane.
      if(pNrmDotrDir== 0.0) {
    // System.err.println("Edge is parallel to plane.");
    return false;
      }

      tempV3d.set(start);

      tr = (pD - normal.dot(tempV3d))/ pNrmDotrDir;

      // Edge intersects the plane behind the edge's start.
      // or exceed the edge's length.
      if((tr < 0.0 ) || (tr > 1.0 )) {
    // System.err.println("Edge intersects the plane behind the start or exceed end.");
    return false;
      }

      iPnt.x = start.x + tr * direction.x;
      iPnt.y = start.y + tr * direction.y;
      iPnt.z = start.z + tr * direction.z;

      return true;

  }

    // Assume coord is CCW.
    boolean edgeIntersectPolygon2D(Vector3d normal, Point3d[] coord,
           Point3d[] seg)
  {

      double  absNrmX, absNrmY, absNrmZ;
      Point2d coord2D[] = new Point2d[coord.length];
      Point2d seg2D[] = new Point2d[2];

      int i, j, axis;

      // Project 3d points onto 2d plane.
      // Note : Area of polygon is not preserve in this projection, but
      // it doesn't matter here.

      // Find the axis of projection.
      absNrmX = Math.abs(normal.x);
      absNrmY = Math.abs(normal.y);
      absNrmZ = Math.abs(normal.z);

      if(absNrmX > absNrmY)
    axis = 0;
      else
    axis = 1;

      if(axis == 0) {
    if(absNrmX < absNrmZ)
        axis = 2;
      }
      else if(axis == 1) {
    if(absNrmY < absNrmZ)
        axis = 2;
      }

      // System.err.println("Normal " + normal + " axis " + axis );

      for(i=0; i<coord.length; i++) {
    coord2D[i] = new Point2d();

    switch (axis) {
    case 0:
        coord2D[i].x = coord[i].y;
        coord2D[i].y = coord[i].z;
        break;

    case 1:
        coord2D[i].x = coord[i].x;
        coord2D[i].y = coord[i].z;
        break;

    case 2:
        coord2D[i].x = coord[i].x;
        coord2D[i].y = coord[i].y;
        break;
    }

    // System.err.println("i " + i + " u " + uCoor[i] + " v " + vCoor[i]);
      }

      for(i=0; i<2; i++) {
    seg2D[i] = new Point2d();
    switch (axis) {
    case 0:
        seg2D[i].x = seg[i].y;
        seg2D[i].y = seg[i].z;
        break;

    case 1:
        seg2D[i].x = seg[i].x;
        seg2D[i].y = seg[i].z;
        break;

    case 2:
        seg2D[i].x = seg[i].x;
        seg2D[i].y = seg[i].y;
        break;
    }

    // System.err.println("i " + i + " u " + uSeg[i] + " v " + vSeg[i]);
      }

      // Do determinant test.
      boolean pntTest[][] = new boolean[2][coord.length];
      boolean testFlag;

      for(j=0; j<coord.length; j++) {
    for(i=0; i<2; i++) {
        if(j<(coord.length-1))
      pntTest[i][j] = (det2D(coord2D[j], coord2D[j+1], seg2D[i])<0.0);
        else
      pntTest[i][j] = (det2D(coord2D[j], coord2D[0], seg2D[i])<0.0);
    }

    if((pntTest[0][j]==false) && (pntTest[1][j]==false))
        return false;
      }

      testFlag = true;
      for(i=0; i<coord.length; i++) {
    if(pntTest[0][i]==false) {
        testFlag = false;
        break;
    }
      }

      if(testFlag == true)
    return true; // start point is inside polygon.

      testFlag = true;
      for(i=0; i<coord.length; i++) {
    if(pntTest[1][i]==false) {
        testFlag = false;
        break;
    }
      }

      if(testFlag == true)
    return true; // end point is inside polygon.


      int cnt = 0;
      for(i=0; i<coord.length; i++) {
    if(det2D(seg2D[0], seg2D[1], coord2D[i])<0.0)
        cnt++;
      }

      if((cnt==0)||(cnt==coord.length))
    return false;

      return true;

  }


    // New stuffs .....
    double getCompValue(Point3d v, int i) {
  switch (i) {
  case 0: return v.x;
  case 1: return v.y;
  }
  // Has to return something, so set the default to z component.
  return v.z;
    }

    double getCompValue(Point3d v0, Point3d v1, int i) {
  switch (i) {
  case 0: return (v0.x - v1.x);
  case 1: return (v0.y - v1.y);
  }
  // Has to return some, so set the default to z component.
  return (v0.z - v1.z);
    }


    boolean pointInTri(Point3d v0, Point3d u0, Point3d u1, Point3d u2,
           Vector3d normal) {

  double nAbsX, nAbsY, nAbsZ;
  int i0, i1;

  // first project onto an axis-aligned plane, that maximizes the area
  // of the triangles, compute indices i0, i1.
  nAbsX = Math.abs(normal.x);
  nAbsY = Math.abs(normal.y);
  nAbsZ = Math.abs(normal.z);

  if (nAbsX > nAbsY) {
      if(nAbsX > nAbsZ) {
    i0 = 1; //  nAbsX is greatest.
    i1 = 2;
      }
      else {
    i0 = 0; //  nAbsZ is greatest.
    i1 = 1;
      }
  } else { // nAbsX <= nAbsY
      if(nAbsZ > nAbsY) {
    i0 = 0//  nAbsZ is greatest.
    i1 = 1;
      }
      else {
    i0 = 0; //  nAbsY is greatest.
    i1 = 2;
      }
  }
  return pointInTri(v0, u0,  u1, u2, i0,  i1);
    }

    boolean pointInTri(Point3d v0, Point3d u0, Point3d u1, Point3d u2,
           int i0, int i1) {

  double a, b, c, d0, d1, d2;
  // is T1 completely inside T2 ?
  // check if v0 is inside tri(u0,u1,u2)

  a = getCompValue(u1, u0, i1);
  b = -(getCompValue(u1, u0, i0));
  c = -a * getCompValue(u0, i0) - b * getCompValue(u0, i1);
  d0 = a * getCompValue(v0, i0) + b * getCompValue(v0, i1) + c;

  a = getCompValue(u2, u1, i1);
  b = -(getCompValue(u2, u1, i0));
  c = -a * getCompValue(u1, i0) - b * getCompValue(u1, i1);
  d1 = a * getCompValue(v0, i0) + b * getCompValue(v0, i1) + c;

  a = getCompValue(u0, u2, i1);
  b = -(getCompValue(u0, u2, i0));
  c = -a * getCompValue(u2, i0) - b * getCompValue(u2, i1);
  d2 = a * getCompValue(v0, i0) + b * getCompValue(v0, i1) + c;

  if(d0*d1>0.0) {
      if(d0*d2>0.0) {
    return true;
      }
  }
  return false;
    }


    // this edge to edge test is based on Franlin Antonio's gem:
    // "Faster line segment intersection", in Graphics Gems III, pp 199-202
    boolean edgeAgainstEdge(Point3d v0, Point3d u0, Point3d u1, double aX, double aY,
          int i0, int i1) {
  double bX, bY, cX, cY, e, d, f;

  bX = getCompValue(u0, u1,i0);
  bY = getCompValue(u0, u1, i1);
  cX = getCompValue(v0, u0, i0);
  cY = getCompValue(v0, u0, i1);

  f = aY * bX - aX * bY;
  d = bY * cX - bX * cY;
  if((f>0 && d>=0 && d<=f) || (f<0 && d<=0 && d>=f)) {
      e = aX * cY - aY * cX;
      if(f>0) {
    if(e>=0 && e<=f)
        return true;
      }
      else {
    if(e<=0 && e>=f)
        return true;
      }
  }

  return false;
    }


    boolean edgeAgainstTriEdges(Point3d v0, Point3d v1, Point3d u0,
        Point3d u1, Point3d u2, int i0, int i1) {
  double aX, aY;

  // aX = v1[i0] - v0[i0];
  // aY = v1[i1] - v0[i1];
  aX = getCompValue(v1, v0, i0);
  aY = getCompValue(v1, v0, i1);

  // test edge u0, u1 against v0, v1
  if(edgeAgainstEdge(v0, u0, u1, aX, aY, i0, i1))
      return true;
  // test edge u1, u2 against v0, v1
  if(edgeAgainstEdge(v0, u1, u2, aX, aY, i0, i1))
      return true;
  // test edge u2, u0 against v0, v1
  if(edgeAgainstEdge(v0, u2, u0, aX, aY, i0, i1))
      return true;

  return false;

    }

    boolean coplanarTriTri(Vector3d normal, Point3d v0, Point3d v1, Point3d v2,
         Point3d u0, Point3d u1, Point3d u2) {

  double nAbsX, nAbsY, nAbsZ;
  int i0, i1;

  // first project onto an axis-aligned plane, that maximizes the area
  // of the triangles, compute indices i0, i1.
  nAbsX = Math.abs(normal.x);
  nAbsY = Math.abs(normal.y);
  nAbsZ = Math.abs(normal.z);

  if(nAbsX > nAbsY) {
      if(nAbsX > nAbsZ) {
    i0 = 1; //  nAbsX is greatest.
    i1 = 2;
      }
      else {
    i0 = 0; //  nAbsZ is greatest.
    i1 = 1;
      }
  }
  else { // nAbsX <= nAbsY
      if(nAbsZ > nAbsY) {
    i0 = 0//  nAbsZ is greatest.
    i1 = 1;
      }
      else {
    i0 = 0; //  nAbsY is greatest.
    i1 = 2;
      }
  }

  // test all edges of triangle 1 against the edges of triangle 2
  if(edgeAgainstTriEdges(v0, v1, u0, u1, u2, i0, i1))
      return true;

  if(edgeAgainstTriEdges(v1, v2, u0, u1, u2, i0, i1))
      return true;

  if(edgeAgainstTriEdges(v2, v0, u0, u1, u2, i0, i1))
      return true;

  // finally, test if tri1 is totally contained in tri2 or vice versa.
  if(pointInTri(v0, u0, u1, u2, i0, i1))
      return true;

  if(pointInTri(u0, v0, v1, v2, i0, i1))
      return true;

  return false;
    }





    boolean intersectTriPnt(Point3d v0, Point3d v1, Point3d v2, Point3d u) {

  Vector3d e1 = new Vector3d();
  Vector3d e2 = new Vector3d();
  Vector3d n1 = new Vector3d();
  Vector3d tempV3d = new Vector3d();

  double d1, du;

  // compute plane equation of triange(coord1)
  e1.x = v1.x - v0.x;
  e1.y = v1.y - v0.y;
  e1.z = v1.z - v0.z;

  e2.x = v2.x - v0.x;
  e2.y = v2.y - v0.y;
  e2.z = v2.z - v0.z;

  n1.cross(e1,e2);

  if(n1.length() == 0.0) {
      // System.err.println("(1) Degenerate triangle.");
      return false// Degenerate triangle.
  }

  tempV3d.set(v0);
  d1 = - n1.dot(tempV3d); // plane equation 1: n1.x + d1 = 0

  // put u to compute signed distance to the plane.
  tempV3d.set(u);
  du = n1.dot(tempV3d) + d1;

  // coplanarity robustness check
  if(Math.abs(du)<EPS) du = 0.0;

  // no intersection occurs
  if(du != 0.0) {
      return false;
  }

  double nAbsX, nAbsY, nAbsZ;
  int i0, i1;

  // first project onto an axis-aligned plane, that maximizes the area
  // of the triangles, compute indices i0, i1.
  nAbsX = Math.abs(n1.x);
  nAbsY = Math.abs(n1.y);
  nAbsZ = Math.abs(n1.z);

  if(nAbsX > nAbsY) {
      if(nAbsX > nAbsZ) {
    i0 = 1; //  nAbsX is greatest.
    i1 = 2;
      }
      else {
    i0 = 0; //  nAbsZ is greatest.
    i1 = 1;
      }
  }
  else { // nAbsX <= nAbsY
      if(nAbsZ > nAbsY) {
    i0 = 0//  nAbsZ is greatest.
    i1 = 1;
      }
      else {
    i0 = 0; //  nAbsY is greatest.
    i1 = 2;
      }
  }


  // finally, test if u is totally contained in tri.
  if(pointInTri(u, v0, v1, v2, i0, i1)) {
      return true;
  }

  return false;
    }


    /**
     * Return flag indicating whether two triangles intersect.  This
     * uses Tomas Moller's code for fast triangle-triangle
     * intersection from his "Real-Time Rendering" book.
     *
     * The code is now divisionless. It tests for separating by planes
     * parallel to either triangle.  If neither separate the
     * triangles, then two cases are considered. First case is if the
     * normals to the triangles are parallel. In that case, the
     * triangles are coplanar and a sequence of tests are made to see
     * if edges of each triangle intersect the other triangle. If the
     * normals are not parallel, then the two triangles can intersect
     * only on the line of intersection of the two planes. The
     * intervals of intersection of the triangles with the line of
     * intersection of the two planes are computed and tested for
     * overlap.
     */
    boolean intersectTriTri(Point3d v0, Point3d v1, Point3d v2,
          Point3d u0, Point3d u1, Point3d u2) {

  // System.err.println("In intersectTriTri ...");
  Vector3d e1 = new Vector3d();
  Vector3d e2 = new Vector3d();
  Vector3d n1 = new Vector3d();
  Vector3d n2 = new Vector3d();
  Vector3d tempV3d = new Vector3d();

  double d1, d2;
  double du0, du1, du2, dv0, dv1, dv2;
  double du0du1, du0du2, dv0dv1, dv0dv2;
  int index;
  double vp0=0.0, vp1=0.0, vp2=0.0;
  double up0=0.0, up1=0.0, up2=0.0;
  double bb, cc, max;

  // compute plane equation of triange(coord1)
  e1.x = v1.x - v0.x;
  e1.y = v1.y - v0.y;
  e1.z = v1.z - v0.z;

  e2.x = v2.x - v0.x;
  e2.y = v2.y - v0.y;
  e2.z = v2.z - v0.z;

  n1.cross(e1,e2);

  if(n1.length() == 0.0) {
      // System.err.println("(1) Degenerate triangle.");
      return false// Degenerate triangle.
  }

  tempV3d.set(v0);
  d1 = - n1.dot(tempV3d); // plane equation 1: n1.x + d1 = 0

  // put u0, u1, and u2 into plane equation 1
  // to compute signed distance to the plane.
  tempV3d.set(u0);
  du0 = n1.dot(tempV3d) + d1;
  tempV3d.set(u1);
  du1 = n1.dot(tempV3d) + d1;
  tempV3d.set(u2);
  du2 = n1.dot(tempV3d) + d1;

  // coplanarity robustness check
  if(Math.abs(du0)<EPS) du0 = 0.0;
  if(Math.abs(du1)<EPS) du1 = 0.0;
  if(Math.abs(du2)<EPS) du2 = 0.0;

  du0du1 = du0 * du1;
  du0du2 = du0 * du2;

  // same sign on all of them + not equal 0 ?
  // no intersection occurs
  if(du0du1>0.0 && du0du2>0.0) {
      // System.err.println("In intersectTriTri : du0du1>0.0 && du0du2>0.0");
      return false;
  }

  // compute plane of triangle(coord2)
  e1.x = u1.x - u0.x;
  e1.y = u1.y - u0.y;
  e1.z = u1.z - u0.z;

  e2.x = u2.x - u0.x;
  e2.y = u2.y - u0.y;
  e2.z = u2.z - u0.z;

  n2.cross(e1,e2);

  if(n2.length() == 0.0) {
      // System.err.println("(2) Degenerate triangle.");
      return false// Degenerate triangle.
  }

  tempV3d.set(u0);
  d2 = - n2.dot(tempV3d); // plane equation 2: n2.x + d2 = 0

  // put v0, v1, and v2 into plane equation 2
  // to compute signed distance to the plane.
  tempV3d.set(v0);
  dv0 = n2.dot(tempV3d) + d2;
  tempV3d.set(v1);
  dv1 = n2.dot(tempV3d) + d2;
  tempV3d.set(v2);
  dv2 = n2.dot(tempV3d) + d2;

  // coplanarity robustness check
  if(Math.abs(dv0)<EPS) dv0 = 0.0;
  if(Math.abs(dv1)<EPS) dv1 = 0.0;
  if(Math.abs(dv2)<EPS) dv2 = 0.0;

  dv0dv1 = dv0 * dv1;
  dv0dv2 = dv0 * dv2;

  // same sign on all of them + not equal 0 ?
  // no intersection occurs
  if(dv0dv1>0.0 && dv0dv2>0.0) {
      // System.err.println("In intersectTriTri : dv0dv1>0.0 && dv0dv2>0.0");
      return false;
  }
  // compute direction of intersection line.
  tempV3d.cross(n1, n2);

  // compute and index to the largest component of tempV3d.
  max = Math.abs(tempV3d.x);
  index = 0;
  bb = Math.abs(tempV3d.y);
  cc = Math.abs(tempV3d.z);
  if(bb>max) {
      max=bb;
      index=1;
  }
  if(cc>max) {
      max=cc;
      index=2;
  }

  // this is the simplified projection onto L.

  switch (index) {
  case 0:
      vp0 = v0.x;
      vp1 = v1.x;
      vp2 = v2.x;

      up0 = u0.x;
      up1 = u1.x;
      up2 = u2.x;
      break;
  case 1:
      vp0 = v0.y;
      vp1 = v1.y;
      vp2 = v2.y;

      up0 = u0.y;
      up1 = u1.y;
      up2 = u2.y;
      break;
  case 2:
      vp0 = v0.z;
      vp1 = v1.z;
      vp2 = v2.z;

      up0 = u0.z;
      up1 = u1.z;
      up2 = u2.z;
      break;
  }

  // compute intereval for triangle 1.
  double a=0.0, b=0.0, c=0.0, x0=0.0, x1=0.0;
  if(dv0dv1>0.0) {
      // here we know that dv0dv2 <= 0.0 that is dv0 and dv1 are on the same side,
      // dv2 on the other side or on the plane.
      a = vp2; b = (vp0 - vp2) * dv2; c = (vp1 - vp2) * dv2;
      x0 = dv2 - dv0; x1 = dv2 - dv1;
  }
  else if(dv0dv2>0.0) {
      // here we know that dv0dv1<=0.0
      a = vp1; b = (vp0 - vp1) * dv1; c = (vp2 - vp1) * dv1;
      x0 = dv1 - dv0; x1 = dv1 - dv2;
  }
  else if((dv1*dv2>0.0) || (dv0 != 0.0)) {
      // here we know that dv0vd1<=0.0 or that dv0!=0.0
      a = vp0; b = (vp1 - vp0) * dv0; c = (vp2 - vp0) * dv0;
      x0 = dv0 - dv1; x1 = dv0 - dv2;
  }
  else if(dv1 != 0.0) {
      a = vp1; b = (vp0 - vp1) * dv1; c = (vp2 - vp1) * dv1;
      x0 = dv1 - dv0; x1 = dv1 - dv2;
  }
  else if(dv2 != 0.0) {
      a = vp2; b = (vp0 - vp2) * dv2; c = (vp1 - vp2) * dv2;
      x0 = dv2 - dv0; x1 = dv2 - dv1;
  }
  else {
      // triangles are coplanar
      boolean toreturn = coplanarTriTri(n1, v0, v1, v2, u0, u1, u2);
      return toreturn;
  }


  // compute intereval for triangle 2.
  double d=0.0, e=0.0, f=0.0, y0=0.0, y1=0.0;
  if(du0du1>0.0) {
      // here we know that du0du2 <= 0.0 that is du0 and du1 are on the same side,
      // du2 on the other side or on the plane.
      d = up2; e = (up0 - up2) * du2; f = (up1 - up2) * du2;
      y0 = du2 - du0; y1 = du2 - du1;
  }
  else if(du0du2>0.0) {
      // here we know that du0du1<=0.0
      d = up1; e = (up0 - up1) * du1; f = (up2 - up1) * du1;
      y0 = du1 - du0; y1 = du1 - du2;
  }
  else if((du1*du2>0.0) || (du0 != 0.0)) {
      // here we know that du0du1<=0.0 or that D0!=0.0
      d = up0; e = (up1 - up0) * du0; f = (up2 - up0) * du0;
      y0 = du0 - du1; y1 = du0 - du2;
  }
  else if(du1 != 0.0) {
      d = up1; e = (up0 - up1) * du1; f = (up2 - up1) * du1;
      y0 = du1 - du0; y1 = du1 - du2;
  }
  else if(du2 != 0.0) {
      d = up2; e = (up0 - up2) * du2; f = (up1 - up2) * du2;
      y0 = du2 - du0; y1 = du2 - du1;
  }
  else {
      // triangles are coplanar
      //      System.err.println("In intersectTriTri : coplanarTriTri test 2");
      boolean toreturn =  coplanarTriTri(n2, v0, v1, v2, u0, u1, u2);
      return toreturn;
  }

  double xx, yy, xxyy, tmp, isect1S, isect1E, isect2S, isect2E;
  xx = x0 * x1;
  yy = y0 * y1;
  xxyy = xx * yy;

  tmp = a * xxyy;
  isect1S = tmp + b * x1 * yy;
  isect1E = tmp + c * x0 * yy;

  tmp = d * xxyy;
  isect2S = tmp + e * y1 * xx;
  isect2E = tmp + f * y0 * xx;

  // sort so that isect1S <= isect1E
  if(isect1S > isect1E) {
      tmp = isect1S;
      isect1S = isect1E;
      isect1E = tmp;
  }

  // sort so that isect2S <= isect2E
  if(isect2S > isect2E) {
      tmp = isect2S;
      isect2S = isect2E;
      isect2E = tmp;
  }

  if(isect1E<isect2S || isect2E<isect1S) {
      // System.err.println("In intersectTriTri :isect1E<isect2S || isect2E<isect1S");
      // System.err.println("In intersectTriTri : return false");
      return false;
  }

  //   System.err.println("In intersectTriTri : return true");
  return true;

    }



    boolean intersectPolygon(Point3d coord1[], Point3d coord2[]) {
  int i, j;
  Vector3d vec0 = new Vector3d(); // Edge vector from point 0 to point 1;
  Vector3d vec1 = new Vector3d(); // Edge vector from point 0 to point 2 or 3;
  Vector3d pNrm = new Vector3d();
  boolean epFlag;


  // compute plane normal for coord1.
  for(i=0; i<coord1.length-1;) {
      vec0.x = coord1[i+1].x - coord1[i].x;
      vec0.y = coord1[i+1].y - coord1[i].y;
      vec0.z = coord1[i+1].z - coord1[i++].z;
      if(vec0.length() > 0.0)
    break;
  }

  for(j=i; j<coord1.length-1; j++) {
      vec1.x = coord1[j+1].x - coord1[j].x;
      vec1.y = coord1[j+1].y - coord1[j].y;
      vec1.z = coord1[j+1].z - coord1[j].z;
      if(vec1.length() > 0.0)
    break;
  }

  if(j == (coord1.length-1)) {
      // System.err.println("(1) Degenerate polygon.");
      return false// Degenerate polygon.
  }

  /*
    for(i=0; i<coord1.length; i++)
    System.err.println("coord1 P" + i + " " + coord1[i]);
    for(i=0; i<coord2.length; i++)
    System.err.println("coord2 P" + i + " " + coord2[i]);
    */

  pNrm.cross(vec0,vec1);

  if(pNrm.length() == 0.0) {
      // System.err.println("(2) Degenerate polygon.");
      return false// Degenerate polygon.
  }

  j = 0;
  Point3d seg[] = new Point3d[2];
  seg[0] = new Point3d();
  seg[1] = new Point3d();

  for(i=0; i<coord2.length; i++) {
      if(i < (coord2.length-1))
    epFlag = edgeIntersectPlane(pNrm, coord1[0], coord2[i],
              coord2[i+1], seg[j]);
      else
    epFlag = edgeIntersectPlane(pNrm, coord1[0], coord2[i],
              coord2[0], seg[j]);
      if (epFlag) {
    if(++j>1) {
        break;
    }
      }
  }

  if (j==0) {
      return false;
  }

  if (coord2.length < 3) {
      boolean toreturn = pointIntersectPolygon2D(pNrm, coord1, seg[0]);
      return toreturn;
  } else {
      boolean toreturn = edgeIntersectPolygon2D(pNrm, coord1, seg);
      return toreturn;
  }
    }


    /**
     * Return true if triangle or quad intersects with ray and the
     * distance is stored in dist[0] and the intersect point in iPnt
     * (if iPnt is not null).
     */
    boolean intersectRay(Point3d coordinates[], PickRay ray, double dist[],
       Point3d iPnt) {

  return intersectRayOrSegment(coordinates, ray.direction, ray.origin,
             dist, iPnt, false);

    }

    /**
     * Return true if triangle or quad intersects with segment and
     * the distance is stored in dist[0].
     */
    boolean intersectSegment( Point3d coordinates[], Point3d start, Point3d end,
            double dist[], Point3d iPnt ) {
  boolean result;
  Vector3d direction = new Vector3d();
  direction.x = end.x - start.x;
  direction.y = end.y - start.y;
  direction.z = end.z - start.z;
  result = intersectRayOrSegment(coordinates, direction, start, dist, iPnt, true);
  return result;
    }



    /**
     *  Return true if triangle or quad intersects with ray and the distance is
     *  stored in pr.
     */
    boolean intersectRayOrSegment(Point3d coordinates[],
          Vector3d direction, Point3d origin,
          double dist[], Point3d iPnt, boolean isSegment) {
  Vector3d vec0, vec1, pNrm, tempV3d;
  vec0 = new Vector3d();
  vec1 = new Vector3d();
  pNrm = new Vector3d();

  double  absNrmX, absNrmY, absNrmZ, pD = 0.0;
  double pNrmDotrDir = 0.0;

  boolean isIntersect = false;
  int i, j, k=0, l = 0;

  // Compute plane normal.
  for (i=0; i<coordinates.length; i++) {
      if (i != coordinates.length-1) {
    l = i+1;
      } else {
    l = 0;
      }
      vec0.x = coordinates[l].x - coordinates[i].x;
      vec0.y = coordinates[l].y - coordinates[i].y;
      vec0.z = coordinates[l].z - coordinates[i].z;
      if (vec0.length() > 0.0) {
    break;
      }
  }


  for (j=l; j<coordinates.length; j++) {
      if (j != coordinates.length-1) {
    k = j+1;
      } else {
    k = 0;
      }
      vec1.x = coordinates[k].x - coordinates[j].x;
      vec1.y = coordinates[k].y - coordinates[j].y;
      vec1.z = coordinates[k].z - coordinates[j].z;
      if (vec1.length() > 0.0) {
    break;
      }
  }

  pNrm.cross(vec0,vec1);

  if ((vec1.length() == 0) || (pNrm.length() == 0)) {
      // degenerate to line if vec0.length() == 0
      // or vec0.length > 0 and vec0 parallel to vec1
      k = (l == 0 ? coordinates.length-1: l-1);
      isIntersect = intersectLineAndRay(coordinates[l],
                coordinates[k],
                origin,
                direction,
                dist,
                iPnt);

      // put the Vectors on the freelist
      return isIntersect;
  }

  // It is possible that Quad is degenerate to Triangle
  // at this point

  pNrmDotrDir = pNrm.dot(direction);

      // Ray is parallel to plane.
  if (pNrmDotrDir == 0.0) {
      // Ray is parallel to plane
      // Check line/triangle intersection on plane.
      for (i=0; i < coordinates.length ;i++) {
    if (i != coordinates.length-1) {
        k = i+1;
    } else {
        k = 0;
    }
    if (intersectLineAndRay(coordinates[i],
          coordinates[k],
          origin,
          direction,
          dist,
          iPnt)) {
        isIntersect = true;
        break;
    }
      }
      return isIntersect;
  }

  // Plane equation: (p - p0)*pNrm = 0 or p*pNrm = pD;
  tempV3d = new Vector3d();
  tempV3d.set(coordinates[0]);
  pD = pNrm.dot(tempV3d);
  tempV3d.set(origin);

  // Substitute Ray equation:
  // p = origin + pi.distance*direction
  // into the above Plane equation

  dist[0] = (pD - pNrm.dot(tempV3d))/ pNrmDotrDir;

  // Ray intersects the plane behind the ray's origin.
  if ((dist[0] < -EPS ) ||
      (isSegment && (dist[0] > 1.0+EPS))) {
      // Ray intersects the plane behind the ray's origin
      // or intersect point not fall in Segment
      return false;
  }

  // Now, one thing for sure the ray intersect the plane.
  // Find the intersection point.
  if (iPnt == null) {
      iPnt = new Point3d();
  }
  iPnt.x = origin.x + direction.x * dist[0];
  iPnt.y = origin.y + direction.y * dist[0];
  iPnt.z = origin.z + direction.z * dist[0];

  // Project 3d points onto 2d plane
  // Find the axis so that area of projection is maximize.
  absNrmX = Math.abs(pNrm.x);
  absNrmY = Math.abs(pNrm.y);
  absNrmZ = Math.abs(pNrm.z);

  // All sign of (y - y0) (x1 - x0) - (x - x0) (y1 - y0)
  // must agree.
  double sign, t, lastSign = 0;
  Point3d p0 = coordinates[coordinates.length-1];
  Point3d p1 = coordinates[0];

  isIntersect = true;

  if (absNrmX > absNrmY) {
      if (absNrmX < absNrmZ) {
    for (i=0; i < coordinates.length; i++) {
        p0 = coordinates[i];
        p1 = (i != coordinates.length-1 ? coordinates[i+1]: coordinates[0]);
         sign = (iPnt.y - p0.y)*(p1.x - p0.x) -
         (iPnt.x - p0.x)*(p1.y - p0.y);
        if (isNonZero(sign)) {
      if (sign*lastSign < 0) {
          isIntersect = false;
          break;
      }
      lastSign = sign;
        } else { // point on line, check inside interval
      t = p1.y - p0.y;
      if (isNonZero(t)) {
          t = (iPnt.y - p0.y)/t;
          isIntersect = ((t > -EPS) && (t < 1+EPS));
          break;
      } else {
          t = p1.x - p0.x;
          if (isNonZero(t)) {
        t = (iPnt.x - p0.x)/t;
        isIntersect = ((t > -EPS) && (t < 1+EPS));
        break;
          } else {
    // Ignore degenerate line=>point happen when Quad => Triangle.
    // Note that by next round sign*lastSign = 0 so it will
    // not pass the interest test. This should only happen once in the
    // loop because we already check for degenerate geometry before.
          }
      }
        }
    }
      } else {
    for (i=0; i<coordinates.length; i++) {
        p0 = coordinates[i];
        p1 = (i != coordinates.length-1 ? coordinates[i+1]: coordinates[0]);
        sign = (iPnt.y - p0.y)*(p1.z - p0.z) -
         (iPnt.z - p0.z)*(p1.y - p0.y);
        if (isNonZero(sign)) {
      if (sign*lastSign < 0) {
          isIntersect = false;
          break;
      }
      lastSign = sign;
        } else { // point on line, check inside interval
      t = p1.y - p0.y;

      if (isNonZero(t)) {
          t = (iPnt.y - p0.y)/t;
          isIntersect = ((t > -EPS) && (t < 1+EPS));
          break;

      } else {
          t = p1.z - p0.z;
          if (isNonZero(t)) {
        t = (iPnt.z - p0.z)/t;
        isIntersect = ((t > -EPS) && (t < 1+EPS));
        break;
          } else {
        //degenerate line=>point
          }
      }
        }
    }
      }
  } else {
      if (absNrmY < absNrmZ) {
    for (i=0; i<coordinates.length; i++) {
        p0 = coordinates[i];
        p1 = (i != coordinates.length-1 ? coordinates[i+1]: coordinates[0]);
        sign = (iPnt.y - p0.y)*(p1.x - p0.x) -
         (iPnt.x - p0.x)*(p1.y - p0.y);
        if (isNonZero(sign)) {
      if (sign*lastSign < 0) {
          isIntersect = false;
          break;
      }
      lastSign = sign;
        } else { // point on line, check inside interval
      t = p1.y - p0.y;
      if (isNonZero(t)) {
          t = (iPnt.y - p0.y)/t;
          isIntersect = ((t > -EPS) && (t < 1+EPS));
          break;
      } else {
          t = p1.x - p0.x;
          if (isNonZero(t)) {
        t = (iPnt.x - p0.x)/t;
        isIntersect = ((t > -EPS) && (t < 1+EPS));
        break;
          } else {
        //degenerate line=>point
          }
      }
        }
    }
      } else {
    for (i=0; i<coordinates.length; i++) {
        p0 = coordinates[i];
        p1 = (i != coordinates.length-1 ? coordinates[i+1]: coordinates[0]);
        sign = (iPnt.x - p0.x)*(p1.z - p0.z) -
         (iPnt.z - p0.z)*(p1.x - p0.x);
        if (isNonZero(sign)) {
      if (sign*lastSign < 0) {
          isIntersect = false;
          break;
      }
      lastSign = sign;
        } else { // point on line, check inside interval
      t = p1.x - p0.x;
      if (isNonZero(t)) {
          t = (iPnt.x - p0.x)/t;
          isIntersect = ((t > -EPS) && (t < 1+EPS));
          break;
      } else {
          t = p1.z - p0.z;
          if (isNonZero(t)) {
        t = (iPnt.z - p0.z)/t;
        isIntersect = ((t > -EPS) && (t < 1+EPS));
        break;
          } else {
        //degenerate line=>point
          }
      }
        }
    }
      }
  }

  if (isIntersect) {
      dist[0] *= direction.length();
  }
  return isIntersect;
    }



    static final boolean isNonZero(double v) {
  return ((v > EPS) || (v < -EPS));

    }

    /**
     * Return true if point is on the inside of halfspace test. The
     * halfspace is partition by the plane of triangle or quad.
     */
    boolean inside( Point3d coordinates[], PickPoint point, int ccw ) {

  Vector3d vec0 = new Vector3d(); // Edge vector from point 0 to point 1;
  Vector3d vec1 = new Vector3d(); // Edge vector from point 0 to point 2 or 3;
  Vector3d pNrm = new Vector3d();
  double pD = 0.0;
  Vector3d tempV3d = new Vector3d();

  int i, j;

  // Compute plane normal.
  for(i=0; i<coordinates.length-1;) {
      vec0.x = coordinates[i+1].x - coordinates[i].x;
      vec0.y = coordinates[i+1].y - coordinates[i].y;
      vec0.z = coordinates[i+1].z - coordinates[i++].z;
      if(vec0.length() > 0.0)
    break;
  }

  for(j=i; j<coordinates.length-1; j++) {
      vec1.x = coordinates[j+1].x - coordinates[j].x;
      vec1.y = coordinates[j+1].y - coordinates[j].y;
      vec1.z = coordinates[j+1].z - coordinates[j].z;
      if(vec1.length() > 0.0)
    break;
  }

  if(j == (coordinates.length-1)) {
      // System.err.println("(1) Degenerate polygon.");
      return false// Degenerate polygon.
  }

  /*
     System.err.println("Ray orgin : " + ray.origin + " dir " + ray.direction);
     System.err.println("Triangle/Quad :");
     for(i=0; i<coordinates.length; i++)
     System.err.println("P" + i + " " + coordinates[i]);
     */

  if( ccw == 0x1)
      pNrm.cross(vec0,vec1);
  else
      pNrm.cross(vec1,vec0);

  if(pNrm.length() == 0.0) {
      // System.err.println("(2) Degenerate polygon.");
      return false// Degenerate polygon.
  }
  // Compute plane D.
  tempV3d.set(coordinates[0]);
  pD = pNrm.dot(tempV3d);
  tempV3d.set(point.location);

  return ((pD - pNrm.dot(tempV3d)) <= 0);
    }

    boolean intersectPntAndPnt( Point3d pnt1, Point3d pnt2 ) {
  return ((pnt1.x == pnt2.x) &&
    (pnt1.y == pnt2.y) &&
    (pnt1.z == pnt2.z));
    }

    boolean intersectPntAndRay(Point3d pnt, Point3d ori, Vector3d dir,
             double dist[]) {
  int flag = 0;
  double temp;

  if(dir.x != 0.0) {
      flag = 0;
      dist[0] = (pnt.x - ori.x)/dir.x;
  }
  else if(dir.y != 0.0) {
      if(pnt.x != ori.x)
    return false;
      flag = 1;
      dist[0] = (pnt.y - ori.y)/dir.y;
  }
  else if(dir.z != 0.0) {
      if((pnt.x != ori.x)||(pnt.y != ori.y))
    return false;
      flag = 2;
      dist[0] = (pnt.z - ori.z)/dir.z;

  }
  else
      return false;

  if(dist[0] < 0.0)
      return false;

  if(flag == 0) {
      temp = ori.y + dist[0] * dir.y;
      if((pnt.y < (temp - EPS)) || (pnt.y > (temp + EPS)))
    return false;
  }

  if(flag < 2) {
      temp = ori.z + dist[0] * dir.z;
      if((pnt.z < (temp - EPS)) || (pnt.z > (temp + EPS)))
    return false;
  }

  return true;

    }

    boolean intersectLineAndRay(Point3d start, Point3d end,
        Point3d ori, Vector3d dir, double dist[],
        Point3d iPnt) {

  double m00, m01, m10, m11;
  double mInv00, mInv01, mInv10, mInv11;
  double dmt, t, s, tmp1, tmp2;
  Vector3d lDir;

  //     System.err.println("GeometryArrayRetained : intersectLineAndRay");
  //     System.err.println("start " + start + " end " + end );
  //     System.err.println("ori " + ori + " dir " + dir);

  lDir = new Vector3d();
  lDir.x = (end.x - start.x);
  lDir.y = (end.y - start.y);
  lDir.z = (end.z - start.z);

  m00 = lDir.x;
  m01 = -dir.x;
  m10 = lDir.y;
  m11 = -dir.y;

  // Get the determinant.
  dmt = (m00 * m11) - (m10 * m01);

  if (dmt==0.0) { // No solution, check degenerate line
      boolean isIntersect = false;
      if ((lDir.x == 0) && (lDir.y == 0) && (lDir.z == 0)) {
    isIntersect = intersectPntAndRay(start, ori, dir, dist);
    if (isIntersect && (iPnt != null)) {
        iPnt.set(start);
    }
      }
      return isIntersect;
  }
  // Find the inverse.
  tmp1 = 1/dmt;

  mInv00 = tmp1 * m11;
  mInv01 = tmp1 * (-m01);
  mInv10 = tmp1 * (-m10);
  mInv11 = tmp1 * m00;

  tmp1 = ori.x - start.x;
  tmp2 = ori.y - start.y;

  t = mInv00 * tmp1 + mInv01 * tmp2;
  s = mInv10 * tmp1 + mInv11 * tmp2;

  if(s<0.0) { // Before the origin of ray.
      // System.err.println("Before the origin of ray " + s);
      return false;
  }
  if((t<0)||(t>1.0)) {// Before or after the end points of line.
      // System.err.println("Before or after the end points of line. " + t);
      return false;
  }

  tmp1 = ori.z + s * dir.z;
  tmp2 = start.z + t * lDir.z;

  if((tmp1 < (tmp2 - EPS)) || (tmp1 > (tmp2 + EPS))) {
      // System.err.println("No intersection : tmp1 " + tmp1 + " tmp2 " + tmp2);
      return false;
  }

  dist[0] = s;

  if (iPnt != null) {
      // compute point of intersection.
      iPnt.x = ori.x + dir.x * dist[0];
      iPnt.y = ori.y + dir.y * dist[0];
      iPnt.z = ori.z + dir.z * dist[0];
  }

  // System.err.println("Intersected : tmp1 " + tmp1 + " tmp2 " + tmp2);
  return true;
    }

    /**
      Return true if triangle or quad intersects with cylinder. The
      distance is stored in dist.
      */
    boolean intersectCylinder(Point3d coordinates[], PickCylinder cyl,
            double dist[], Point3d iPnt) {

  Point3d origin = new Point3d();
  Point3d end = new Point3d();
  Vector3d direction = new Vector3d();
  Point3d iPnt1 = new Point3d();
  Vector3d originToIpnt = new Vector3d();

  if (iPnt == null) {
      iPnt = new Point3d();
  }

  // Get cylinder information
  cyl.getOrigin (origin);
  cyl.getDirection (direction);
  double radius = cyl.getRadius ();

  if (cyl instanceof PickCylinderSegment) {
      ((PickCylinderSegment)cyl).getEnd (end);
  }

  // If the ray intersects, we're good (do not do this if we only have
  // a segment
  if (coordinates.length > 2) {
      if (cyl instanceof PickCylinderRay) {
    if (intersectRay(coordinates,
         new PickRay(origin, direction),
         dist, iPnt)) {
        return true;
    }
      }
      else {
    if (intersectSegment(coordinates, origin, end, dist, iPnt)) {
        return true;
    }
      }
  }

  // Ray doesn't intersect, check distance to edges
  double sqDistToEdge;
  int j;
  for (int i=0; i<coordinates.length;i++) {
      j = (i < coordinates.length-1 ? i+1: 0);
      if (cyl instanceof PickCylinderSegment) {
    sqDistToEdge =
        Utils.segmentToSegment(origin, end,
                coordinates[i], coordinates[j],
                iPnt1, iPnt, null);
      }
      else {
    sqDistToEdge =
        Utils.rayToSegment(origin, direction,
            coordinates[i], coordinates[j],
            iPnt1, iPnt, null);
      }
      if (sqDistToEdge <= radius*radius) {
    originToIpnt.sub (iPnt1, origin);
    dist[0] = originToIpnt.length();
    return true;
      }
  }
  return false;
    }

    /**
      Return true if triangle or quad intersects with cone. The
      distance is stored in dist.
      */
    boolean intersectCone(Point3d coordinates[], PickCone cone,
        double[] dist, Point3d iPnt) {

  Point3d origin = new Point3d();
  Point3d end = new Point3d();
  Vector3d direction = new Vector3d();
  Vector3d originToIpnt = new Vector3d();
  double distance;

  Point3d iPnt1 = new Point3d();

  if (iPnt == null) {
      iPnt = new Point3d();
  }
  // Get cone information
  cone.getOrigin (origin);
  cone.getDirection (direction);
  double radius;

  if (cone instanceof PickConeSegment) {
      ((PickConeSegment)cone).getEnd (end);
  }

  // If the ray intersects, we're good (do not do this if we only have
  // a segment
  if (coordinates.length > 2) {
      if (cone instanceof PickConeRay) {
    if (intersectRay(coordinates,
         new PickRay (origin, direction),
         dist, iPnt)) {
        return true;
    }
      }
      else {
    if (intersectSegment(coordinates, origin, end, dist, iPnt)) {
        return true;
    }
      }
  }

  // Ray doesn't intersect, check distance to edges
  double sqDistToEdge;
  int j = 0;
  for (int i=0; i<coordinates.length;i++) {
      j = (i < coordinates.length-1 ? i+1: 0);
      if (cone instanceof PickConeSegment) {
    sqDistToEdge =
        Utils.segmentToSegment (origin, end,
                 coordinates[i], coordinates[j],
                 iPnt1, iPnt, null);
      }
      else {
    sqDistToEdge =
        Utils.rayToSegment (origin, direction,
             coordinates[i], coordinates[j],
             iPnt1, iPnt, null);
      }
      originToIpnt.sub(iPnt1, origin);
      distance = originToIpnt.length();
      radius = Math.tan (cone.getSpreadAngle()) * distance;
      if (sqDistToEdge <= radius*radius) {
    //  System.err.println ("intersectCone: edge "+i+" intersected");
    dist[0] = distance;
    return true;
      }
  }
  return false;
    }


    /**
      Return true if point intersects with cylinder and the distance is
      stored in dist.
      */
    boolean intersectCylinder(Point3d pt, PickCylinder cyl,
            double[] dist) {

  Point3d origin = new Point3d();
  Point3d end = new Point3d();
  Vector3d direction = new Vector3d();
  Point3d iPnt = new Point3d();
  Vector3d originToIpnt = new Vector3d();

  // Get cylinder information
  cyl.getOrigin (origin);
  cyl.getDirection (direction);
  double radius = cyl.getRadius ();
  double sqDist;

  if (cyl instanceof PickCylinderSegment) {
      ((PickCylinderSegment)cyl).getEnd (end);
      sqDist = Utils.ptToSegSquare(pt, origin, end, iPnt);
  }
  else {
      sqDist = Utils.ptToRaySquare(pt, origin, direction, iPnt);
  }
  if (sqDist <= radius*radius) {
      originToIpnt.sub (iPnt, origin);
      dist[0] = originToIpnt.length();
      return true;
  }
  return false;
    }

    /**
      Return true if point intersects with cone and the
      distance is stored in pi.
      */
    boolean intersectCone(Point3d pt, PickCone cone, double[] dist)
    {
  Point3d origin = new Point3d();
  Point3d end = new Point3d();
  Vector3d direction = new Vector3d();
  Point3d iPnt = new Point3d();
  Vector3d originToIpnt = new Vector3d();

  // Get cone information
  cone.getOrigin (origin);
  cone.getDirection (direction);
  double radius;
  double distance;
  double sqDist;

  if (cone instanceof PickConeSegment) {
      ((PickConeSegment)cone).getEnd (end);
      sqDist = Utils.ptToSegSquare(pt, origin, end, iPnt);
  }
  else {
      sqDist = Utils.ptToRaySquare(pt, origin, direction, iPnt);
  }
  originToIpnt.sub(iPnt, origin);
  distance = originToIpnt.length();
  radius = Math.tan (cone.getSpreadAngle()) * distance;
  if (sqDist <= radius*radius) {
      dist[0] = distance;
      return true;
  }
  return false;
    }


    void setCoordRefBuffer(J3DBuffer coords) {
  if (coords != null) {
      switch (coords.bufferType) {
      case FLOAT:
    assert ((FloatBuffer)coords.getROBuffer()).isDirect();
    break;
      case DOUBLE:
    assert ((DoubleBuffer)coords.getROBuffer()).isDirect();
    break;
      case NULL:
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray115"));

      default:
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray116"));
      }

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
    if (3 * idx.maxCoordIndex >= coords.getROBuffer().limit()) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
    }
      } else if (coords.getROBuffer().limit() < (3*(initialCoordIndex+validVertexCount))) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
      }
  }

  // lock the geometry and start to do real work
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COORDINATE_CHANGED;
  coordRefBuffer = coords;
  if(coords == null) {
      floatBufferRefCoords = null;
      doubleBufferRefCoords = null;
      // XXXX: if not mix java array with nio buffer
      // vertexType can be used as vertexTypeBuffer
      vertexType &= ~PD;
      vertexType &= ~PF;
  }else {
      switch (coords.bufferType) {
      case FLOAT:
    floatBufferRefCoords = (FloatBuffer)coords.getROBuffer();
    doubleBufferRefCoords = null;
    vertexType |= PF;
    vertexType &= ~PD;
    break;
      case DOUBLE:
    floatBufferRefCoords = null;
    doubleBufferRefCoords = (DoubleBuffer)coords.getROBuffer();
    vertexType |= PD;
    vertexType &= ~PF;
    break;
      default:
    break;
      }
  }

  // need not call setupMirrorVertexPointer() since
  // we are not going to set mirror in NIO buffer case
  // XXXX: if we need to mix java array with buffer,
  //        we may need to consider setupMirrorVertexPointer()

  if(isLive) {
            geomLock.unLock();
        }
  if (!inUpdater && source != null) {
      if (isLive) {
    processCoordsChanged((coords == null));
    sendDataChangedMessage(true);
      } else {
    boundsDirty = true;
      }
  }

    }


    J3DBuffer getCoordRefBuffer() {
  return coordRefBuffer;
    }


    void setCoordRefFloat(float[] coords) {

  // If non-null coordinate and vertType is either  defined
  // to be something other than PF, then issue an error
  if (coords != null) {
      if ((vertexType & VERTEX_DEFINED) != 0 &&
    (vertexType & VERTEX_DEFINED) != PF) {
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
      }


      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;

    if (3 * idx.maxCoordIndex >= coords.length) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
    }
      } else if (coords.length < 3 * (initialCoordIndex+validVertexCount)) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
      }
  }
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COORDINATE_CHANGED;

  floatRefCoords = coords;
  if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
        ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
      if (coords == null)
    vertexType &= ~PF;
      else
    vertexType |= PF;
  }
  else {
      setupMirrorVertexPointer(PF);
  }

  if(isLive) {
            geomLock.unLock();
        }
  if (!inUpdater && source != null) {
            if (isLive) {
                processCoordsChanged(coords == null);
    sendDataChangedMessage(true);
            } else {
    boundsDirty = true;
      }
  }
    }


    float[] getCoordRefFloat() {
  return floatRefCoords;
    }


    void setCoordRefDouble(double[] coords) {

  if (coords != null) {
      if ((vertexType & VERTEX_DEFINED) != 0 &&
    (vertexType & VERTEX_DEFINED) != PD) {
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
      }

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
    if (3 * idx.maxCoordIndex >= coords.length) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
    }
      } else if (coords.length < 3 * (initialCoordIndex+validVertexCount)) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
      }
  }
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COORDINATE_CHANGED;
  doubleRefCoords = coords;
  if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
        ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
      if (coords == null)
    vertexType &= ~PD;
      else
    vertexType |= PD;
  }
  else {
      setupMirrorVertexPointer(PD);
  }
  if(isLive) {
            geomLock.unLock();
        }
  if (!inUpdater && source != null) {
      if (isLive) {
    processCoordsChanged(coords == null);
    sendDataChangedMessage(true);
      } else {
    boundsDirty = true;
      }
  }
    }

    double[] getCoordRefDouble() {
  return doubleRefCoords;
    }

    void setCoordRef3f(Point3f[] coords) {

  if (coords != null) {
      if ((vertexType & VERTEX_DEFINED) != 0 &&
    (vertexType & VERTEX_DEFINED) != P3F) {
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
      }

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;

    if (idx.maxCoordIndex >= coords.length) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
    }
      } else if (coords.length < (initialCoordIndex+validVertexCount) ) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
      }
  }
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COORDINATE_CHANGED;
  p3fRefCoords = coords;
  if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
        ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
      if (coords == null)
    vertexType &= ~P3F;
      else
    vertexType |= P3F;
  }
  else {
      setupMirrorVertexPointer(P3F);
  }
  if(isLive) {
            geomLock.unLock();
        }
  if (!inUpdater && source != null) {
      if (isLive) {
    processCoordsChanged(coords == null);
    sendDataChangedMessage(true);
      } else {
    boundsDirty = true;
      }
  }
    }

    Point3f[] getCoordRef3f() {
  return p3fRefCoords;

    }

    void setCoordRef3d(Point3d[] coords) {

  if (coords != null) {
      if ((vertexType & VERTEX_DEFINED) != 0 &&
    (vertexType & VERTEX_DEFINED) != P3D) {
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
      }

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;

    if (idx.maxCoordIndex >= coords.length) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
    }
      } else if (coords.length <  (initialCoordIndex+validVertexCount) ) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
      }
  }
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COORDINATE_CHANGED;
  p3dRefCoords = coords;
  if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
        ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
      if (coords == null)
    vertexType &= ~P3D;
      else
    vertexType |= P3D;
  } else {
      setupMirrorVertexPointer(P3D);
  }
  if(isLive) {
            geomLock.unLock();
        }
  if (!inUpdater && source != null) {
      if (isLive) {
    processCoordsChanged(coords == null);
    sendDataChangedMessage(true);
      } else {
    boundsDirty = true;
      }
  }
    }

    Point3d[] getCoordRef3d() {
  return p3dRefCoords;
    }

    void setColorRefFloat(float[] colors) {

  if (colors != null) {
      if ((vertexType & COLOR_DEFINED) != 0 &&
    (vertexType & COLOR_DEFINED) != CF) {
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
      }

      if ((vertexFormat & GeometryArray.COLOR) == 0) {
    throw new IllegalStateException(J3dI18N.getString("GeometryArray123"));
      }

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;

    if (getColorStride() * idx.maxColorIndex >= colors.length) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
    }
      } else  if (colors.length < getColorStride() * (initialColorIndex+ validVertexCount) ) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
      }
  }

  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;
  floatRefColors = colors;
  if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
        ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
      if (colors == null)
    vertexType &= ~CF;
      else
    vertexType |= CF;
  }
  else {
      setupMirrorColorPointer(CF, false);
  }

  if(isLive) {
            geomLock.unLock();
  }
  if (!inUpdater && isLive) {
      sendDataChangedMessage(false);
  }

    }

    float[] getColorRefFloat() {
  return floatRefColors;
    }


    // set the color with nio buffer
    void setColorRefBuffer(J3DBuffer colors) {
  if (colors != null) {
      switch(colors.bufferType) {
      case FLOAT:
    assert ((FloatBuffer)colors.getROBuffer()).isDirect();
    break;
      case BYTE:
    assert ((ByteBuffer)colors.getROBuffer()).isDirect();
    break;
      case NULL:
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray115"));

      default:
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray116"));
      }

      if ((vertexFormat & GeometryArray.COLOR) == 0) {
    throw new IllegalStateException(J3dI18N.getString("GeometryArray123"));
      }

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;

    if (getColorStride() * idx.maxColorIndex >= colors.getROBuffer().limit()) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
    }
      } else if (colors.getROBuffer().limit() <
           getColorStride() * (initialColorIndex+validVertexCount)) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
      }
  }

  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;
  colorRefBuffer = colors;
  if(colors == null) {
      floatBufferRefColors = null;
      byteBufferRefColors = null;
  } else {
      switch (colors.bufferType) {
      case FLOAT:
    floatBufferRefColors = (FloatBuffer)colors.getROBuffer();
    byteBufferRefColors = null;
    break;

      case BYTE:
    byteBufferRefColors = (ByteBuffer)colors.getROBuffer();
    floatBufferRefColors = null;
    break;
      default:
    break;
      }
  }
  if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
        ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
      if(colors == null) {
    vertexType &= ~CF;
    vertexType &= ~CUB;
      } else {
    switch (colors.bufferType) {
    case FLOAT:
        vertexType |= CF;
        vertexType &= ~CUB;
        break;

    case BYTE:
        vertexType |= CUB;
        vertexType &= ~CF;
        break;
    default:
        break;
    }
      }
  }
  else {
      setupMirrorColorPointer(CF|CUB, false);
  }

  if(isLive) {
            geomLock.unLock();
  }

  if (!inUpdater && isLive) {
      sendDataChangedMessage(false);
  }
    }

    // return the color data in nio buffer format
    J3DBuffer getColorRefBuffer() {
  return colorRefBuffer;
    }

    void setColorRefByte(byte[] colors) {

  if (colors != null) {
      if ((vertexType & COLOR_DEFINED) != 0 &&
    (vertexType & COLOR_DEFINED) != CUB) {
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
      }

      if ((vertexFormat & GeometryArray.COLOR) == 0) {
    throw new IllegalStateException(J3dI18N.getString("GeometryArray123"));
      }

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;

    if (getColorStride() * idx.maxColorIndex >= colors.length) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
    }
      } else if (colors.length < getColorStride() * (initialColorIndex + validVertexCount)) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
      }
  }
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;
  byteRefColors = colors;
  if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
        ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
      if (colors == null)
    vertexType &= ~CUB;
      else
    vertexType |= CUB;
  }
  else {
      setupMirrorColorPointer(CUB, false);
  }
  if(isLive){
            geomLock.unLock();
  }

  if (!inUpdater && isLive) {
      sendDataChangedMessage(false);
  }

    }

    byte[] getColorRefByte() {
  return byteRefColors;
    }

    void setColorRef3f(Color3f[] colors) {

  if (colors != null) {
      if ((vertexType & COLOR_DEFINED) != 0 &&
    (vertexType & COLOR_DEFINED) != C3F) {
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
      }

      if ((vertexFormat & GeometryArray.COLOR_3) == 0) {
    throw new IllegalStateException(J3dI18N.getString("GeometryArray92"));
      }

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
    if (idx.maxColorIndex >= colors.length) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
    }
      } else if (colors.length < (initialColorIndex + validVertexCount) ) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
      }
  }

  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;
  c3fRefColors = colors;
  if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
        ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
      if (colors == null)
    vertexType &= ~C3F;
      else
    vertexType |= C3F;
  }
  else {
      setupMirrorColorPointer(C3F, false);
  }

  if(isLive) {
            geomLock.unLock();
  }

  if (!inUpdater && isLive) {
      sendDataChangedMessage(false);
  }

    }

    Color3f[] getColorRef3f() {
  return c3fRefColors;
    }


    void setColorRef4f(Color4f[] colors) {

  if (colors != null) {
      if ((vertexType & COLOR_DEFINED) != 0 &&
    (vertexType & COLOR_DEFINED) != C4F) {
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
      }
      if ((vertexFormat & GeometryArray.COLOR_4) == 0) {
    throw new IllegalStateException(J3dI18N.getString("GeometryArray93"));
      }

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
    if (idx.maxColorIndex >= colors.length) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
    }
      } else if (colors.length < (initialColorIndex + validVertexCount) ) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
      }
  }
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;
  c4fRefColors = colors;
  if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
        ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
      if (colors == null)
    vertexType &= ~C4F;
      else
    vertexType |= C4F;
  }
  else {
      setupMirrorColorPointer(C4F, false);
  }
  if(isLive) {
            geomLock.unLock();
  }

  if (!inUpdater && isLive) {
      sendDataChangedMessage(false);
  }
    }

    Color4f[] getColorRef4f() {
  return c4fRefColors;
    }


    void setColorRef3b(Color3b[] colors) {

  if (colors != null) {

      if ((vertexType & COLOR_DEFINED) != 0 &&
    (vertexType & COLOR_DEFINED) != C3UB) {
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
      }

      if ((vertexFormat & GeometryArray.COLOR_3) == 0) {
    throw new IllegalStateException(J3dI18N.getString("GeometryArray92"));
      }

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;

    if (idx.maxColorIndex >= colors.length) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
    }
      } else if (colors.length < (initialColorIndex + validVertexCount)) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
      }
  }

  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;
  c3bRefColors = colors;
  if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
        ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
      if (colors == null)
    vertexType &= ~C3UB;
      else
    vertexType |= C3UB;
  }
  else {
      setupMirrorColorPointer(C3UB, false);
  }

  if(isLive) {
            geomLock.unLock();
  }

  if (!inUpdater && isLive) {
      sendDataChangedMessage(false);
        }
    }


    Color3b[] getColorRef3b() {
  return c3bRefColors;
    }

    void setColorRef4b(Color4b[] colors) {

  if (colors != null) {
      if ((vertexType & COLOR_DEFINED) != 0 &&
    (vertexType & COLOR_DEFINED) != C4UB) {
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
      }

      if ((vertexFormat & GeometryArray.COLOR_4) == 0) {
    throw new IllegalStateException(J3dI18N.getString("GeometryArray93"));
      }

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained) this;

    if (idx.maxColorIndex >= colors.length) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
    }
      } else if (colors.length < (initialColorIndex + validVertexCount) ) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
      }
  }
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;
  c4bRefColors = colors;
  if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
        ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
      if (colors == null)
    vertexType &= ~C4UB;
      else
    vertexType |= C4UB;
  }
  else {
      setupMirrorColorPointer(C4UB, false);
  }

  if(isLive) {
            geomLock.unLock();
  }
  if (!inUpdater && isLive) {
      sendDataChangedMessage(false);
        }
    }


    Color4b[] getColorRef4b() {
  return c4bRefColors;
    }

    void setNormalRefFloat(float[] normals) {

  if (normals != null) {
      if ((vertexType & NORMAL_DEFINED) != 0 &&
    (vertexType & NORMAL_DEFINED) != NF) {
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
      }

      if ((vertexFormat & GeometryArray.NORMALS) == 0) {
    throw new IllegalStateException(J3dI18N.getString("GeometryArray122"));
      }

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;

    if (idx.maxNormalIndex*3 >= normals.length) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray26"));
    }
      } else if (normals.length < 3 * (initialNormalIndex + validVertexCount )) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray111"));
      }
  }
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= NORMAL_CHANGED;
  floatRefNormals = normals;
  if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
        ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
      if (normals == null)
    vertexType &= ~NF;
      else
    vertexType |= NF;
  }
  else {
      setupMirrorNormalPointer(NF);
  }
  if(isLive) {
            geomLock.unLock();
  }
  if (!inUpdater && isLive) {
    sendDataChangedMessage(false);
  }

    }

    float[] getNormalRefFloat() {
  return floatRefNormals;
    }

    // setup the normal with nio buffer
    void setNormalRefBuffer(J3DBuffer normals) {

  FloatBuffer bufferImpl = null;

  if (normals != null) {
      if(normals.bufferType != J3DBuffer.Type.FLOAT)
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray116"));

      bufferImpl = (FloatBuffer)normals.getROBuffer();

      assert bufferImpl.isDirect();

      if ((vertexFormat & GeometryArray.NORMALS) == 0) {
    throw new IllegalStateException(J3dI18N.getString("GeometryArray122"));
      }

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
    if (idx.maxNormalIndex * 3 >=
        ((FloatBuffer)normals.getROBuffer()).limit()) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray26"));
    }
      } else if (bufferImpl.limit() < 3 * (initialNormalIndex + validVertexCount )) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray111"));
      }
  }
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= NORMAL_CHANGED;
  normalRefBuffer = normals;

  if (normals == null) {
      vertexType &= ~NF;
      floatBufferRefNormals = null;
  }
  else {
      vertexType |= NF;
      floatBufferRefNormals = bufferImpl;
  }
  if(isLive) {
            geomLock.unLock();
  }
  if (!inUpdater && isLive) {
    sendDataChangedMessage(false);
  }
    }

    J3DBuffer getNormalRefBuffer() {
  return normalRefBuffer;
    }

    void setNormalRef3f(Vector3f[] normals) {

  if (normals != null) {
      if ((vertexType & NORMAL_DEFINED) != 0 &&
    (vertexType & NORMAL_DEFINED) != N3F) {
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray98"));
      }

      if ((vertexFormat & GeometryArray.NORMALS) == 0) {
    throw new IllegalStateException(J3dI18N.getString("GeometryArray122"));
      }

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
    if (idx.maxNormalIndex >= normals.length) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray26"));
    }
      } else if (normals.length < (initialNormalIndex + validVertexCount) ) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray111"));
      }
  }
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= NORMAL_CHANGED;
  v3fRefNormals = normals;
  if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
        ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
      if (normals == null)
    vertexType &= ~N3F;
      else
    vertexType |= N3F;
  }
  else {
      setupMirrorNormalPointer(N3F);
  }
  if(isLive) {
            geomLock.unLock();
  }
  if (!inUpdater && isLive) {
      sendDataChangedMessage(false);
  }
    }

    Vector3f[] getNormalRef3f() {
  return v3fRefNormals;
    }

    final int getColorStride() {
  return ((vertexFormat & GeometryArray.WITH_ALPHA) != 0 ? 4 : 3);
    }

    final int getTexStride() {
  if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
      return 2;
  }
  if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
      return 3;
  }
  if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
      return 4;
  }

  throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray121"));
    }

    void setTexCoordRefFloat(int texCoordSet, float[] texCoords) {

        if (texCoordType != 0 && texCoordType != TF) {
            if (texCoords != null) {
                throw new IllegalArgumentException(
                        J3dI18N.getString("GeometryArray98"));
            }
            return;
        }

        if (texCoords != null) {

            int ts = getTexStride();

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;

    if (idx.maxTexCoordIndices[texCoordSet]*ts >= texCoords.length) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray25"));
    }
      } else if (texCoords.length < ts*(initialTexCoordIndex[texCoordSet]+validVertexCount)) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray113"));
      }
  }
        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
  dirtyFlag |= TEXTURE_CHANGED;
  refTexCoords[texCoordSet] = texCoords;
  if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
        ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
      texCoordType = TF;
            validateTexCoordPointerType();
  }
  else {
      setupMirrorTexCoordPointer(texCoordSet, TF);
  }
  if(isLive) {
            geomLock.unLock();
  }
  if (!inUpdater && isLive) {
      sendDataChangedMessage(false);
  }
    }


    float[] getTexCoordRefFloat(int texCoordSet) {
  return ((float[])refTexCoords[texCoordSet]);
    }

    // set the tex coord with nio buffer
    void setTexCoordRefBuffer(int texCoordSet, J3DBuffer texCoords) {

  FloatBuffer bufferImpl = null;

  if (texCoords != null) {
      if(texCoords.bufferType != J3DBuffer.Type.FLOAT)
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray116"));

      bufferImpl = (FloatBuffer)texCoords.getROBuffer();
      int bufferSize = bufferImpl.limit();

      assert bufferImpl.isDirect();

      int ts = getTexStride();

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
    if (idx.maxTexCoordIndices[texCoordSet] * ts >= bufferSize) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray25"));
    }
      } else if (bufferSize < ts*(initialTexCoordIndex[texCoordSet] + validVertexCount)) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray113"));
      }
  }
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= TEXTURE_CHANGED;
  // refTexCoordsBuffer contains J3DBuffer object for tex coord
  refTexCoordsBuffer[texCoordSet] = texCoords;
  if (texCoords == null) {
      refTexCoords[texCoordSet] = null;
  }
  else {
      // refTexCoords contains NIOBuffer object for tex coord
      refTexCoords[texCoordSet] = bufferImpl;
  }
        texCoordType = TF;
        validateTexCoordPointerType();
        if(isLive) {
            geomLock.unLock();
        }
  if (!inUpdater && isLive) {
      sendDataChangedMessage(false);
  }
    }

    J3DBuffer getTexCoordRefBuffer(int texCoordSet) {
  return refTexCoordsBuffer[texCoordSet];
    }

    void setTexCoordRef2f(int texCoordSet, TexCoord2f[] texCoords) {

        if (texCoordType != 0 && texCoordType != T2F) {
            if (texCoords != null) {
                throw new IllegalArgumentException(
                        J3dI18N.getString("GeometryArray98"));
            }
            return;
        }

        if (texCoords != null) {
      if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) == 0) {
    throw new IllegalStateException(
        J3dI18N.getString("GeometryArray94"));
      }

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;

    if (idx.maxTexCoordIndices[texCoordSet] >= texCoords.length) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray25"));
    }
      } else if (texCoords.length < (initialTexCoordIndex[texCoordSet] + validVertexCount) ) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray113"));
      }

  }
        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
  dirtyFlag |= TEXTURE_CHANGED;
  refTexCoords[texCoordSet] = texCoords;
  if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
        ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
      texCoordType = T2F;
            validateTexCoordPointerType();
  }
  else {
      setupMirrorTexCoordPointer(texCoordSet, T2F);
  }
  if(isLive) {
            geomLock.unLock();
        }
  if (!inUpdater && isLive) {
    sendDataChangedMessage(false);
  }
    }


    TexCoord2f[] getTexCoordRef2f(int texCoordSet) {
  if (refTexCoords != null && refTexCoords[texCoordSet] != null &&
    refTexCoords[texCoordSet] instanceof TexCoord2f[]) {
      return ((TexCoord2f[])refTexCoords[texCoordSet]);
  } else {
      return null;
  }
    }


    void setTexCoordRef3f(int texCoordSet, TexCoord3f[] texCoords) {

        if (texCoordType != 0 && texCoordType != T3F) {
            if (texCoords != null) {
                throw new IllegalArgumentException(
                        J3dI18N.getString("GeometryArray98"));
            }
            return;
        }

  if (texCoords != null) {

      if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) == 0) {
    throw new IllegalStateException(
        J3dI18N.getString("GeometryArray95"));
      }

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;

    if (idx.maxTexCoordIndices[texCoordSet] >= texCoords.length) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray25"));
    }

      } else if (texCoords.length < (initialTexCoordIndex[texCoordSet] + validVertexCount) ) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray113"));
      }

  }
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= TEXTURE_CHANGED;
  refTexCoords[texCoordSet] = texCoords;
  if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
        ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
      texCoordType = T3F;
            validateTexCoordPointerType();
  }
  else {
      setupMirrorTexCoordPointer(texCoordSet, T3F);
  }
  if(isLive) {
            geomLock.unLock();
  }
  if (!inUpdater && isLive) {
      sendDataChangedMessage(false);
  }
    }


    TexCoord3f[] getTexCoordRef3f(int texCoordSet) {
  if (refTexCoords != null && refTexCoords[texCoordSet] != null &&
    refTexCoords[texCoordSet] instanceof TexCoord3f[]) {
      return ((TexCoord3f[])refTexCoords[texCoordSet]);
  } else {
      return null;
  }
    }


    /**
     * Sets the float vertex attribute array reference for the
     * specified vertex attribute number to the specified array.
     */
    void setVertexAttrRefFloat(int vertexAttrNum, float[] vertexAttrs) {

        // XXXX: Add the following test if we ever add double-precision types
        /*
        if (vertexAttrType != 0 && vertexAttrType != AF) {
            if (vertexAttrs != null) {
                // XXXX: new exception string
                throw new IllegalArgumentException(
                        J3dI18N.getString("GeometryArray98-XXX"));
            }
            return;
        }
        */

        if (vertexAttrs != null) {
            int sz = vertexAttrSizes[vertexAttrNum];

            if (this instanceof IndexedGeometryArrayRetained) {
                IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;

    if (sz*idx.maxVertexAttrIndices[vertexAttrNum] >= vertexAttrs.length) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray30"));
    }

      } else if (vertexAttrs.length < sz*(initialVertexAttrIndex[vertexAttrNum] + validVertexCount) ) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray129"));
      }
  }
        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
  dirtyFlag |= VATTR_CHANGED;
  floatRefVertexAttrs[vertexAttrNum] = vertexAttrs;
  if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
        ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
      vertexAttrType = AF;
            validateVertexAttrPointerType();
  }
  else {
      setupMirrorVertexAttrPointer(vertexAttrNum, AF);
  }
  if(isLive) {
            geomLock.unLock();
        }
  if (!inUpdater && isLive) {
      sendDataChangedMessage(false);
  }
    }

    /**
     * Gets the float vertex attribute array reference for the specified
     * vertex attribute number.
     */
    float[] getVertexAttrRefFloat(int vertexAttrNum) {
        return floatRefVertexAttrs[vertexAttrNum];
    }


    /**
     * Sets the vertex attribute buffer reference for the specified
     * vertex attribute number to the specified buffer object.
     */
    void setVertexAttrRefBuffer(int vertexAttrNum, J3DBuffer vertexAttrs) {

  FloatBuffer bufferImpl = null;

  if (vertexAttrs != null) {
      if(vertexAttrs.bufferType != J3DBuffer.Type.FLOAT)
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray116"));

      bufferImpl = (FloatBuffer)vertexAttrs.getROBuffer();
      int bufferSize = bufferImpl.limit();

      assert bufferImpl.isDirect();

      int sz = vertexAttrSizes[vertexAttrNum];

            if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;

    if (idx.maxVertexAttrIndices[vertexAttrNum] * sz >= bufferSize) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray30"));
    }
      } else if (bufferSize < sz*(initialVertexAttrIndex[vertexAttrNum] + validVertexCount)) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray129"));
            }
        }
        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
        dirtyFlag |= VATTR_CHANGED;
        vertexAttrsRefBuffer[vertexAttrNum] = vertexAttrs;
        if (vertexAttrs == null) {
            floatBufferRefVertexAttrs[vertexAttrNum] = null;
        }
        else {
            floatBufferRefVertexAttrs[vertexAttrNum] = bufferImpl;
        }
        vertexAttrType = AF;
        validateVertexAttrPointerType();
        if(isLive) {
            geomLock.unLock();
        }
        if (!inUpdater && isLive) {
            sendDataChangedMessage(false);
        }

    }

    /**
     * Gets the vertex attribute array buffer reference for the specified
     * vertex attribute number.
     */
    J3DBuffer getVertexAttrRefBuffer(int vertexAttrNum) {
  return vertexAttrsRefBuffer[vertexAttrNum];
    }


    void setInterleavedVertices(float[] vertexData) {
  if (vertexData != null) {

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;

    if (stride * idx.maxCoordIndex >= vertexData.length) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
    }

    if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
        for (int i = 0; i < texCoordSetCount; i++) {
      if (stride * idx.maxTexCoordIndices[i] >= vertexData.length) {
          throw new ArrayIndexOutOfBoundsException(
                                      J3dI18N.getString("IndexedGeometryArray25"));
      }
        }
    }

    if (((this.vertexFormat & GeometryArray.COLOR) != 0) &&
        (stride * idx.maxColorIndex >= vertexData.length)) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
    }

    if (((this.vertexFormat & GeometryArray.NORMALS) != 0) &&
        (stride * idx.maxNormalIndex >= vertexData.length)) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray26"));
    }
      } else {
    if (vertexData.length < (stride * (initialVertexIndex+validVertexCount)))
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray114"));
      }
  }

  // If the geometry has been rendered transparent, then make a copy
  // of the color pointer with 4f
        boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= VERTEX_CHANGED;
  colorChanged = 0xffff;
  interLeavedVertexData = vertexData;
  if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
        ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
      setupMirrorInterleavedColorPointer(false);
  }
  if(isLive) {
            geomLock.unLock();
  }
  if (!inUpdater && isLive) {
      processCoordsChanged(vertexData == null);
      sendDataChangedMessage(true);
  }
    }

    // set the interleaved vertex with NIO buffer
    void setInterleavedVertexBuffer(J3DBuffer vertexData) {

  FloatBuffer bufferImpl = null;

  if (vertexData != null ){

      if (vertexData.bufferType != J3DBuffer.Type.FLOAT)
    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray116"));

      bufferImpl = (FloatBuffer)vertexData.getROBuffer();

            assert bufferImpl.isDirect();

      int bufferSize = bufferImpl.limit();

      if (this instanceof IndexedGeometryArrayRetained) {
    IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;

    if (stride * idx.maxCoordIndex >= bufferSize) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
    }

    if ((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
        for (int i = 0; i < texCoordSetCount; i++) {
      if (stride * idx.maxTexCoordIndices[i] >= bufferSize) {
          throw new ArrayIndexOutOfBoundsException(
                                  J3dI18N.getString("IndexedGeometryArray25"));
      }
        }
    }

    if (((this.vertexFormat & GeometryArray.COLOR) != 0) &&
        (stride * idx.maxColorIndex >= bufferSize)) {
          throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray24"));
    }

    if (((this.vertexFormat & GeometryArray.NORMALS) != 0) &&
        (stride * idx.maxNormalIndex >= bufferSize)) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
    }
      } else {
    if (bufferSize < (stride * (initialVertexIndex+validVertexCount)))
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray114"));
      }
  }
  // If the geometry has been rendered transparent, then make a copy
  // of the color pointer with 4f
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= VERTEX_CHANGED;
  colorChanged = 0xffff;
  interleavedVertexBuffer = vertexData;

  if(vertexData == null)
      interleavedFloatBufferImpl = null;
  else
      interleavedFloatBufferImpl = bufferImpl;

  if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
        ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
      setupMirrorInterleavedColorPointer(false);
  }
  if(isLive) {
            geomLock.unLock();
  }
  if (!inUpdater && isLive) {
      processCoordsChanged(vertexData == null);
      sendDataChangedMessage(true);
  }
    }

    float[] getInterleavedVertices() {
  return interLeavedVertexData;
    }

    J3DBuffer getInterleavedVertexBuffer() {
  return interleavedVertexBuffer;
    }

    void setValidVertexCount(int validVertexCount) {

  boolean nullGeo = false;
  if (validVertexCount < 0) {
      throw new IllegalArgumentException(J3dI18N.getString("GeometryArray110"));
  }

        if ((initialVertexIndex + validVertexCount) > vertexCount) {
            throw new IllegalArgumentException(J3dI18N.getString("GeometryArray100"));
        }

        if ((vertexFormat & GeometryArray.INTERLEAVED) != 0) {
            // Interleaved, by-ref

            // use nio buffer for interleaved data
      if(( vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0 && interleavedFloatBufferImpl != null){
    if(interleavedFloatBufferImpl.limit() <  stride * (initialVertexIndex + validVertexCount)) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray114"));
    }
      }
      //use java array for interleaved data
      else if( interLeavedVertexData != null) {
    if(interLeavedVertexData.length < stride * (initialVertexIndex + validVertexCount)) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray114"));
    }
      }
      else {
    nullGeo = true;
      }
  } else if ((vertexFormat & GeometryArray.BY_REFERENCE) != 0) {
            // Non-interleaved, by-ref

            if ((initialCoordIndex + validVertexCount) > vertexCount) {
                throw new IllegalArgumentException(J3dI18N.getString("GeometryArray104"));
            }
            if ((initialColorIndex + validVertexCount) > vertexCount) {
                throw new IllegalArgumentException(J3dI18N.getString("GeometryArray101"));
            }
            if ((initialNormalIndex + validVertexCount) > vertexCount) {
                throw new IllegalArgumentException(J3dI18N.getString("GeometryArray102"));
            }

            if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
                for (int i = 0; i < texCoordSetCount; i++) {
                    if ((initialTexCoordIndex[i] + validVertexCount) > vertexCount) {
                        throw new IllegalArgumentException(J3dI18N.getString(
                                "GeometryArray103"));
                    }
                }
            }

            if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
                for (int i = 0; i < vertexAttrCount; i++) {
                    if ((initialVertexAttrIndex[i] + validVertexCount) > vertexCount) {
                        throw new IllegalArgumentException(J3dI18N.getString(
                                "GeometryArray130"));
                    }
                }
            }

            if ((vertexType & GeometryArrayRetained.VERTEX_DEFINED) == 0) {
    nullGeo = true;
            }

      if (( vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) {
    // by reference with nio buffer
    switch ((vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
    case PF:
        if(floatBufferRefCoords.limit() < 3 * (initialCoordIndex+validVertexCount) ) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
        }
        break;
    case PD:
        if(doubleBufferRefCoords.limit() < 3 * (initialCoordIndex+validVertexCount) ) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
        }
        break;
    }

    switch ((vertexType & COLOR_DEFINED)) {
    case CF:
        if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
      if (floatBufferRefColors.limit() < 3 * (initialColorIndex+validVertexCount)) {
          throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
      }
        }
        else if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
      if (floatBufferRefColors.limit() < 4 * (initialColorIndex+validVertexCount)) {
          throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
      }
        }
        break;
    case CUB:
        if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
      if (byteBufferRefColors.limit() < 3 * (initialColorIndex + validVertexCount)) {
          throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
      }
        }
        else if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
      if (byteBufferRefColors.limit() < 4 * (initialColorIndex + validVertexCount) ) {
          throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
      }
        }
        break;
    }
    switch ((vertexType & GeometryArrayRetained.TEXCOORD_DEFINED)) {
    case TF:
        FloatBuffer texBuffer;
        for (int i = 0; i < texCoordSetCount; i++) {
      texBuffer = (FloatBuffer)refTexCoordsBuffer[i].getROBuffer();
      if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
          if (texBuffer.limit() 2 * (initialTexCoordIndex[i] + validVertexCount) ) {
        throw new ArrayIndexOutOfBoundsException(
                   J3dI18N.getString("GeometryArray113"));
          }
      } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
          if (texBuffer.limit() < 3 * (initialTexCoordIndex[i] + validVertexCount) ) {
        throw new ArrayIndexOutOfBoundsException(
                   J3dI18N.getString("GeometryArray113"));
          }
      } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
          if (texBuffer.limit() < 4 * (initialTexCoordIndex[i] + validVertexCount)) {
        throw new ArrayIndexOutOfBoundsException(
                   J3dI18N.getString("GeometryArray113"));
          }
      }
        }
        break;
    }
    switch ((vertexType & GeometryArrayRetained.NORMAL_DEFINED)) {
    case NF:
        if (floatBufferRefNormals.limit() < 3 * (initialNormalIndex + validVertexCount )) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray111"));
        }
        break;
    }
    switch ((vertexType & GeometryArrayRetained.VATTR_DEFINED)) {
    case AF:
                    for (int i = 0; i < vertexAttrCount; i++) {
                        int sz = vertexAttrSizes[i];
                        if (floatBufferRefVertexAttrs[i].limit() <
                                (sz * (initialVertexAttrIndex[i] + validVertexCount)) ) {
                            throw new ArrayIndexOutOfBoundsException(
                                    J3dI18N.getString("GeometryArray129"));
                        }
                    }
        break;
    }
      }
      // By reference with java array
      else {
    switch ((vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
    case PF:
        if (floatRefCoords.length < 3 * (initialCoordIndex+validVertexCount)) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
        }
        break;
    case PD:
        if (doubleRefCoords.length < 3 * (initialCoordIndex+validVertexCount)) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
        }
        break;
    case P3F:
        if (p3fRefCoords.length < (initialCoordIndex+validVertexCount) ) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
        }
        break;
    case P3D:
        if (p3dRefCoords.length <  (initialCoordIndex+validVertexCount) ) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
        }
        break;
    }
    switch ((vertexType & COLOR_DEFINED)) {
    case CF:
        if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
      if (floatRefColors.length < 3 * (initialColorIndex+validVertexCount)) {
          throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
      }
        }
        else if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
      if (floatRefColors.length < 4 * (initialColorIndex+ validVertexCount) ) {
          throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
      }
        }
        break;
    case CUB:
        if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
      if (byteRefColors.length < 3 * (initialColorIndex + validVertexCount)) {
          throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
      }
        }
        else if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
      if (byteRefColors.length < 4 * (initialColorIndex + validVertexCount) ) {
          throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
      }
        }
        break;
    case C3F:
        if (c3fRefColors.length < (initialColorIndex + validVertexCount) ) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
        }
        break;
    case C4F:
        if (c4fRefColors.length < (initialColorIndex + validVertexCount) ) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
        }
        break;
    case C3UB:
        if (c3bRefColors.length < (initialColorIndex + validVertexCount)) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
        }
        break;
    case C4UB:
        if (c4bRefColors.length < (initialColorIndex + validVertexCount) ) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
        }
        break;
    }
    switch ((vertexType & GeometryArrayRetained.TEXCOORD_DEFINED)) {
    case TF:
        for (int i = 0; i < texCoordSetCount; i++) {
      if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
          if (((float[])refTexCoords[i]).length < 2 * (initialTexCoordIndex[i] + validVertexCount) ) {
        throw new ArrayIndexOutOfBoundsException(
                   J3dI18N.getString("GeometryArray113"));
          }
      } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
          if (((float[])refTexCoords[i]).length < 3 * (initialTexCoordIndex[i] + validVertexCount) ) {
        throw new ArrayIndexOutOfBoundsException(
                   J3dI18N.getString("GeometryArray113"));
          } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
        if (((float[])refTexCoords[i]).length < 4 * (initialTexCoordIndex[i] + validVertexCount)) {
            throw new ArrayIndexOutOfBoundsException(
                       J3dI18N.getString("GeometryArray113"));
        }
          }
      }
        }
        break;
    case T2F:
        for (int i = 0; i < texCoordSetCount; i++) {
      if (((TexCoord2f[])refTexCoords[i]).length < (initialTexCoordIndex[i] + validVertexCount) ) {
          throw new ArrayIndexOutOfBoundsException(
                     J3dI18N.getString("GeometryArray113"));
      }
        }
        break;
    case T3F:
        for (int i = 0; i < texCoordSetCount; i++) {
      if (((TexCoord3f[])refTexCoords[i]).length < (initialTexCoordIndex[i] + validVertexCount) ) {
          throw new ArrayIndexOutOfBoundsException(
                     J3dI18N.getString("GeometryArray113"));
      }
        }
        break;
    }
    switch ((vertexType & GeometryArrayRetained.NORMAL_DEFINED)) {
    case NF:
        if (floatRefNormals.length < 3 * (initialNormalIndex + validVertexCount )) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray111"));
        }
        break;
    case N3F:
        if (v3fRefNormals.length < (initialNormalIndex + validVertexCount) ) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray111"));
        }
    }
    switch ((vertexType & GeometryArrayRetained.VATTR_DEFINED)) {
    case AF:
                    for (int i = 0; i < vertexAttrCount; i++) {
                        int sz = vertexAttrSizes[i];
                        if (floatRefVertexAttrs[i].length <
                                (sz * (initialVertexAttrIndex[i] + validVertexCount)) ) {
                            throw new ArrayIndexOutOfBoundsException(
                                    J3dI18N.getString("GeometryArray129"));
                        }
                    }
        break;
    }
      }
  }
        boolean isLive = source!=null && source.isLive();
        if(isLive){
            geomLock.getLock();
        }
  dirtyFlag |= VERTEX_CHANGED;
  this.validVertexCount = validVertexCount;

  if(isLive){
            geomLock.unLock();
  }

  if (!inUpdater && isLive) {
      processCoordsChanged(nullGeo);
      sendDataChangedMessage(true);
  }
    }


    int getValidVertexCount() {
  return validVertexCount;
    }

    //Used for interleaved data (array or nio buffer)
    void setInitialVertexIndex(int initialVertexIndex) {
  boolean nullGeo = false;

  if ((initialVertexIndex + validVertexCount) > vertexCount) {
      throw new IllegalArgumentException(J3dI18N.getString("GeometryArray100"));
  }

  if((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0 && interleavedFloatBufferImpl != null) {
      if(interleavedFloatBufferImpl.limit() <  stride * (initialVertexIndex + validVertexCount)) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray114"));
      }
  }
  // interleaved data using java array
  else if(interLeavedVertexData != null) {
    if (interLeavedVertexData.length < stride * (initialVertexIndex + validVertexCount)) {
    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray114"));
      }
  }
  else {
      nullGeo = (vertexFormat & GeometryArray.INTERLEAVED) != 0; // Only for byRef
  }
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
        }
  dirtyFlag |= VERTEX_CHANGED;
  this.initialVertexIndex = initialVertexIndex;
  if(isLive) {
            geomLock.unLock();
  }
  if (!inUpdater && isLive) {
      processCoordsChanged(nullGeo);
      sendDataChangedMessage(true);
  }
    }

    int getInitialVertexIndex() {
  return initialVertexIndex;
    }

    void setInitialCoordIndex(int initialCoordIndex) {
  if ((initialCoordIndex + validVertexCount) > vertexCount) {
      throw new IllegalArgumentException(J3dI18N.getString("GeometryArray104"));
  }
  // use NIO buffer
  if((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0){
      switch ((vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
      case PF:
    if(floatBufferRefCoords.limit() < (initialCoordIndex+validVertexCount) ) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
    }
    break;
      case PD:
    if(doubleBufferRefCoords.limit() < (initialCoordIndex+validVertexCount) ) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
    }
    break;
      }
  } else {
      switch ((vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
      case PF:
    if (floatRefCoords.length < 3 * (initialCoordIndex+validVertexCount)) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
    }
    break;
      case PD:
    if (doubleRefCoords.length < 3 * (initialCoordIndex+validVertexCount)) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
    }
    break;
      case P3F:
    if (p3fRefCoords.length < (initialCoordIndex+validVertexCount) ) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
    }
    break;
      case P3D:
    if (p3dRefCoords.length <  (initialCoordIndex+validVertexCount) ) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray99"));
    }
    break;
      }
  }
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COORDINATE_CHANGED;
  this.initialCoordIndex = initialCoordIndex;
  dirtyFlag |= COORDINATE_CHANGED;
  if(isLive) {
            geomLock.unLock();
  }
  // Send a message, since bounds changed
  if (!inUpdater && isLive) {
      processCoordsChanged((vertexType & GeometryArrayRetained.VERTEX_DEFINED) == 0);
      sendDataChangedMessage(true);
  }
    }

    int getInitialCoordIndex() {
  return initialCoordIndex;
    }

    void setInitialColorIndex(int initialColorIndex) {
  if ((initialColorIndex + validVertexCount) > vertexCount) {
      throw new IllegalArgumentException(J3dI18N.getString("GeometryArray101"));
  }
  // NIO BUFFER CASE
  if((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0){
      switch ((vertexType & COLOR_DEFINED)) {
      case CF:
    if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
        if (floatBufferRefColors.limit() < 3 * (initialColorIndex+validVertexCount)) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
        }
    }
    else if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
        if (floatBufferRefColors.limit() < 4 * (initialColorIndex+validVertexCount)) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
        }
    }
    break;

      case CUB:
    if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
        if (byteBufferRefColors.limit() < 3 * (initialColorIndex + validVertexCount)) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
        }
    }
    else if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
        if (byteBufferRefColors.limit() < 4 * (initialColorIndex + validVertexCount) ) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
        }
    }
    break;
      }
  }
  // Java ARRAY CASE
  else {
      switch ((vertexType & COLOR_DEFINED)) {
      case CF:
    if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
        if (floatRefColors.length < 3 * (initialColorIndex+validVertexCount)) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
        }
    }
    else if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
        if (floatRefColors.length < 4 * (initialColorIndex+ validVertexCount) ) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
        }
    }
    break;
      case CUB:
    if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
        if (byteRefColors.length < 3 * (initialColorIndex + validVertexCount)) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
        }
    }
    else if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
        if (byteRefColors.length < 4 * (initialColorIndex + validVertexCount) ) {
      throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
        }
    }
    break;
      case C3F:
    if (c3fRefColors.length < (initialColorIndex + validVertexCount) ) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
    }
    break;
      case C4F:
    if (c4fRefColors.length < (initialColorIndex + validVertexCount) ) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
    }
    break;
      case C3UB:
    if (c3bRefColors.length < (initialColorIndex + validVertexCount)) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
    }
    break;
      case C4UB:
    if (c4bRefColors.length < (initialColorIndex + validVertexCount) ) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray112"));
    }
    break;
      }
  }
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= COLOR_CHANGED;
  colorChanged = 0xffff;
  this.initialColorIndex = initialColorIndex;
  if(isLive) {
            geomLock.unLock();
  }
  // There is no need to send message for by reference, since we
  // use VA

    }

    int getInitialColorIndex() {
  return initialColorIndex;
    }

    void setInitialNormalIndex(int initialNormalIndex) {
  if ((initialNormalIndex + validVertexCount) > vertexCount) {
      throw new IllegalArgumentException(J3dI18N.getString("GeometryArray102"));
  }
  if((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0){
      if((vertexType & NORMAL_DEFINED) == NF){
    if (floatBufferRefNormals.limit() < 3 * (initialNormalIndex + validVertexCount )) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray111"));
    }
      }
  } else {
      switch((vertexType & NORMAL_DEFINED)){
      case NF:
    if (floatRefNormals.length < 3 * (initialNormalIndex + validVertexCount )) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray111"));
    }
    break;
      case N3F:
    if (v3fRefNormals.length < (initialNormalIndex + validVertexCount) ) {
        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray111"));
    }
      }
  }
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= NORMAL_CHANGED;
  this.initialNormalIndex = initialNormalIndex;
  if(isLive) {
            geomLock.unLock();
  }
  // There is no need to send message for by reference, since we
  // use VA
    }

    int getInitialNormalIndex() {
  return initialNormalIndex;
    }

    /**
     * Sets the initial vertex attribute index for the specified
     * vertex attribute number for this GeometryArray object.
     */
    void setInitialVertexAttrIndex(int vertexAttrNum,
            int initialVertexAttrIndex) {

        if ((initialVertexAttrIndex + validVertexCount) > vertexCount) {
            throw new IllegalArgumentException(J3dI18N.getString("GeometryArray130"));
        }

        int sz = vertexAttrSizes[vertexAttrNum];
        int minLength = sz * (initialVertexAttrIndex + validVertexCount);
        if ((vertexType & VATTR_DEFINED) == AF) {
            if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) {
                if (floatBufferRefVertexAttrs[vertexAttrNum].limit() < minLength) {
                    throw new ArrayIndexOutOfBoundsException(
                            J3dI18N.getString("GeometryArray129"));
                }
            } else {
                if (floatRefVertexAttrs[vertexAttrNum].length < minLength ) {
                    throw new ArrayIndexOutOfBoundsException(
                            J3dI18N.getString("GeometryArray129"));
                }
            }
        }
        boolean isLive = source!=null && source.isLive();
      if(isLive){
            geomLock.getLock();
      }
        dirtyFlag |= VATTR_CHANGED;
        this.initialVertexAttrIndex[vertexAttrNum] = initialVertexAttrIndex;
        if(isLive) {
            geomLock.unLock();
      }
        // There is no need to send message for by reference, since we
        // use VA
    }


    /**
     * Gets the initial vertex attribute index for the specified
     * vertex attribute number for this GeometryArray object.
     */
    int getInitialVertexAttrIndex(int vertexAttrNum) {
        return initialVertexAttrIndex[vertexAttrNum];
    }

    void setInitialTexCoordIndex(int texCoordSet, int initialTexCoordIndex) {
  if ((initialTexCoordIndex + validVertexCount) > vertexCount) {
      throw new IllegalArgumentException(J3dI18N.getString("GeometryArray103"));
  }

  if((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0){
      if((vertexType & TEXCOORD_DEFINED) == TF) {
    FloatBuffer texBuffer = (FloatBuffer)refTexCoordsBuffer[texCoordSet].getROBuffer();
    if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
        if (texBuffer.limit() < 2 * (initialTexCoordIndex+ validVertexCount) ) {
      throw new ArrayIndexOutOfBoundsException(
                 J3dI18N.getString("GeometryArray113"));
        }
    } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
        if (texBuffer.limit() < 3 * (initialTexCoordIndex + validVertexCount) ) {
      throw new ArrayIndexOutOfBoundsException(
                 J3dI18N.getString("GeometryArray113"));
        }
    } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
        if (texBuffer.limit() < 4 * (initialTexCoordIndex + validVertexCount)) {
      throw new ArrayIndexOutOfBoundsException(
                 J3dI18N.getString("GeometryArray113"));
        }
    }
      }
  } else {
      switch ((vertexType & TEXCOORD_DEFINED)) {
      case TF:
    if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
        if (((float[])refTexCoords[texCoordSet]).length < 2 * (initialTexCoordIndex+ validVertexCount) ) {
      throw new ArrayIndexOutOfBoundsException(
                 J3dI18N.getString("GeometryArray113"));
        }
    } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
        if (((float[])refTexCoords[texCoordSet]).length < 3 * (initialTexCoordIndex + validVertexCount) ) {
      throw new ArrayIndexOutOfBoundsException(
                 J3dI18N.getString("GeometryArray113"));
        }
    } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
        if (((float[])refTexCoords[texCoordSet]).length < 4 * (initialTexCoordIndex + validVertexCount)) {
      throw new ArrayIndexOutOfBoundsException(
                 J3dI18N.getString("GeometryArray113"));
        }
    }
    break;
      case T2F:
    if (((TexCoord2f[])refTexCoords[texCoordSet]).length < (initialTexCoordIndex+ validVertexCount) ) {
        throw new ArrayIndexOutOfBoundsException(
                   J3dI18N.getString("GeometryArray113"));
    }
    break;
      case T3F:
    if (((TexCoord3f[])refTexCoords[texCoordSet]).length < (initialTexCoordIndex+ validVertexCount) ) {
        throw new ArrayIndexOutOfBoundsException(
                   J3dI18N.getString("GeometryArray113"));
    }
    break;
      }
  }
  boolean isLive = source!=null && source.isLive();
  if(isLive){
            geomLock.getLock();
  }
  dirtyFlag |= TEXTURE_CHANGED;
  this.initialTexCoordIndex[texCoordSet] = initialTexCoordIndex;
  if(isLive) {
            geomLock.unLock();
  }
  // There is no need to send message for by reference, since we
  // use VA
    }

    int getInitialTexCoordIndex(int texCoordSet) {
  return initialTexCoordIndex[texCoordSet];
    }


    int getTexCoordSetCount() {
  return this.texCoordSetCount;
    }

    int getTexCoordSetMapLength() {
  if (this.texCoordSetMap != null)
      return this.texCoordSetMap.length;
  else
      return 0;
    }

    void getTexCoordSetMap(int [] texCoordSetMap) {

  if (this.texCoordSetMap!=null) {
      for (int i = 0; i < this.texCoordSetMap.length; i++) {
           texCoordSetMap[i] = this.texCoordSetMap[i];
      }
  }
    }

    void freeDlistId() {
  if (dlistId != -1) {
      VirtualUniverse.mc.freeDisplayListId(dlistObj);
      dlistId = -1;
  }
    }

    void assignDlistId() {
  if (dlistId == -1) {
      dlistObj = VirtualUniverse.mc.getDisplayListId();
      dlistId = dlistObj.intValue();
  }
    }

// Add the specified render atom as a user of this geometry array
// (for the specified render bin)
void addDlistUser(RenderBin renderBin, RenderAtomListInfo ra) {
  if (dlistUsers == null)
    dlistUsers = new HashMap<RenderBin, HashSet<RenderAtomListInfo>>(2, 1.0f);

  HashSet<RenderAtomListInfo> raSet = dlistUsers.get(renderBin);
  if (raSet == null) {
    raSet = new HashSet<RenderAtomListInfo>();
    dlistUsers.put(renderBin, raSet);
  }
  raSet.add(ra);
}

// Remove the specified render atom from the set of users of this
// geometry array (for the specified render bin)
void removeDlistUser(RenderBin renderBin, RenderAtomListInfo ra) {
  if (dlistUsers == null)
    return;

  HashSet<RenderAtomListInfo> raSet = dlistUsers.get(renderBin);
  if (raSet == null)
    return;

  raSet.remove(ra);
}

// Returns true if the set of render atoms using this geometry
// array in the specified render bin is empty.
boolean isDlistUserSetEmpty(RenderBin renderBin) {
  if (dlistUsers == null)
    return true;

  HashSet<RenderAtomListInfo> raSet = dlistUsers.get(renderBin);
  if (raSet == null) {
    return true;
  }
  return raSet.isEmpty();
}

// This method is used for debugging only
int numDlistUsers(RenderBin renderBin) {
  if (isDlistUserSetEmpty(renderBin))
    return 0;

  HashSet<RenderAtomListInfo> raSet = dlistUsers.get(renderBin);
  return raSet.size();
}

    void setDlistTimeStamp(int rdrBit, long timeStamp) {
  int index = getIndex(rdrBit);
  if (index >= timeStampPerDlist.length) {
      long[] newList = new long[index * 2];
      for (int i = 0; i < timeStampPerDlist.length; i++) {
     newList[i] = timeStampPerDlist[i];
      }
      timeStampPerDlist = newList;
  }
  timeStampPerDlist[index] = timeStamp;
    }

    long getDlistTimeStamp(int rdrBit) {
  int index = getIndex(rdrBit);
       // If index is greater than what currently exists, increase
       // the array and return zero
       if (index >= timeStampPerDlist.length) {
           setDlistTimeStamp(rdrBit, 0);
       }
       return timeStampPerDlist[index];
    }

    int getIndex(int bit) {
  int num = 0;

  while (bit > 0) {
      num++;
      bit >>= 1;
  }
  return num;
    }


    boolean isWriteStatic() {

  if (source.getCapability(GeometryArray.ALLOW_COORDINATE_WRITE ) ||
      source.getCapability(GeometryArray.ALLOW_COLOR_WRITE) ||
      source.getCapability(GeometryArray.ALLOW_NORMAL_WRITE) ||
      source.getCapability(GeometryArray.ALLOW_TEXCOORD_WRITE) ||
            source.getCapability(GeometryArray.ALLOW_VERTEX_ATTR_WRITE) ||
      source.getCapability(GeometryArray.ALLOW_COUNT_WRITE) ||
      source.getCapability(GeometryArray.ALLOW_REF_DATA_WRITE))
      return false;

  return true;
    }

    /**
     * The functions below are only used in compile mode
     */
    void setCompiled(ArrayList curList) {
  int i;
  int num = curList.size();
  int offset = 0;
  geoOffset = new int[num];
  compileVcount = new int[num];
  int vcount = 0, vformat = 0;
  vcount = 0;
  isCompiled = true;

  if (num > 0)
      source = ((SceneGraphObjectRetained)curList.get(0)).source;
  for (i = 0; i < num; i++) {
      // Build the back mapping
      GeometryArrayRetained geo = (GeometryArrayRetained)curList.get(i);
      ((GeometryArray)geo.source).retained = this;
      compileVcount[i] = geo.getValidVertexCount();
      vcount += geo.getValidVertexCount();
      geoOffset[i] = offset;
      offset += geo.stride() * compileVcount[i];
      vformat = geo.getVertexFormat();
  }
  createGeometryArrayData(vcount, vformat);

  // Assign the initial and valid fields
  validVertexCount = vcount;
  initialVertexIndex = 0;

  mergeGeometryArrays(curList);

    }

    /*
    // Ununsed
    int getVertexCount(int index) {
  return compileVcount[index];
    }


    int getValidVertexCount(int index) {
  return compileVcount[index];
    }


    int getInitialVertexIndex(int index) {
  return 0;
    }
    */

    void mergeGeometryArrays(ArrayList list) {
  float[] curVertexData;
  int length, srcOffset;
  int curOffset = 0;
  // We only merge if the texCoordSetCount is 1 and there are no
        // vertex attrs
  if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
      texCoordSetCount = 1;
      texCoordSetMap = new int[1];
      texCoordSetMap[0] = 1;
  }
  for (int i = 0; i < list.size(); i++) {
      GeometryArrayRetained geo = (GeometryArrayRetained)list.get(i);
      // Take into account the validVertexCount and initialVertexIndex
      curVertexData = geo.vertexData;
      length = geo.validVertexCount * stride;
      srcOffset = geo.initialVertexIndex * stride;
      System.arraycopy(curVertexData, srcOffset, this.vertexData, curOffset,
           length);
      curOffset += length;

      // assign geoBounds
      geoBounds.combine(geo.geoBounds);

  }
  geoBounds.getCenter(this.centroid);
    }

    boolean isMergeable() {

  // For now, turn off by ref geometry
  if ((vertexFormat & GeometryArray.BY_REFERENCE) != 0)
      return false;

  if (!isStatic())
      return false;

  // If there is more than one set of texture coordinate set defined
  // then don't merge geometry (we avoid dealing with texCoordSetMap
  if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0 &&
      (texCoordSetCount > 1 ||
       texCoordSetMap != null && texCoordSetMap.length > 1)) {
      return false;
  }

        // We will avoid merging geometry if there are any vertex attributes.
        if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
            return false;
        }

  // If intersect is allowed turn off merging
  if (source.getCapability(Geometry.ALLOW_INTERSECT))
      return false;

  return true;
    }

    @Override
    void compile(CompileState compState) {
        super.compile(compState);

  if ((vertexFormat & GeometryArray.NORMALS) != 0) {
      compState.needNormalsTransform = true;
  }
    }

    @Override
    void mergeTransform(TransformGroupRetained xform) {
  if (geoBounds != null) {
      geoBounds.transform(xform.transform);
  }
    }

    // This adds a MorphRetained to the list of users of this geometry
    void addMorphUser(MorphRetained m) {
        int index;

  if(morphUniverseList == null) {
      morphUniverseList = new ArrayList<VirtualUniverse>(1);
      morphUserLists = new ArrayList<ArrayList<MorphRetained>>(1);
  }
        synchronized (morphUniverseList) {
            if (morphUniverseList.contains(m.universe)) {
                index = morphUniverseList.indexOf(m.universe);
                morphUserLists.get(index).add(m);
            } else {
                morphUniverseList.add(m.universe);
                ArrayList<MorphRetained> morphList = new ArrayList<MorphRetained>(5);
                morphList.add(m);
                morphUserLists.add(morphList);
            }
        }
    }

    // This adds a MorphRetained to the list of users of this geometry
    void removeMorphUser(MorphRetained m) {
        int index;

  if(morphUniverseList == null)
      return;

        synchronized (morphUniverseList) {
            index = morphUniverseList.indexOf(m.universe);
            ArrayList<MorphRetained> morphList = morphUserLists.get(index);
            morphList.remove(morphList.indexOf(m));
            if (morphList.size() == 0) {
                morphUserLists.remove(index);
                morphUniverseList.remove(index);
            }
        }
    }
    // Initialize mirror object when geometry is first setLived
    void initMirrorGeometry() {
  geomLock.getLock();
  if (this instanceof IndexedGeometryArrayRetained) {
      if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
    mirrorGeometry =
        ((IndexedGeometryArrayRetained)this).cloneNonIndexedGeometry();
      }
      else {
    mirrorGeometry = null;
      }
  }
  geomLock.unLock();

    }

    // Update Mirror Object in response to change in geometry
    void updateMirrorGeometry() {
  geomLock.getLock();
  if (this instanceof IndexedGeometryArrayRetained) {
      if (mirrorGeometry != null) {
    mirrorGeometry =
        ((IndexedGeometryArrayRetained)this).cloneNonIndexedGeometry();
      }
  }
  geomLock.unLock();

    }


    // Used by the picking intersect routines
    void getVertexData(int i, Point3d pnts) {
  int offset;
  if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
      offset = stride * i + coordinateOffset;
      pnts.x = this.vertexData[offset];
      pnts.y = this.vertexData[offset+1];
      pnts.z = this.vertexData[offset+2];
      return;
  }

  if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0 ) {
      if ((vertexFormat & GeometryArray.INTERLEAVED) != 0) {
    offset = stride * i + coordinateOffset;
    pnts.x = this.interLeavedVertexData[offset];
    pnts.y = this.interLeavedVertexData[offset+1];
    pnts.z = this.interLeavedVertexData[offset+2];
      }
      else {
    switch ((vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
    case GeometryArrayRetained.PF:
        offset = i*3;
        pnts.x = this.floatRefCoords[offset];
        pnts.y = this.floatRefCoords[offset+1];
        pnts.z = this.floatRefCoords[offset+2];
        break;
    case GeometryArrayRetained.PD:
        offset = i*3;
        pnts.x = this.doubleRefCoords[offset];
        pnts.y = this.doubleRefCoords[offset+1];
        pnts.z = this.doubleRefCoords[offset+2];
        break;
    case GeometryArrayRetained.P3F:
        pnts.x = this.p3fRefCoords[i].x;
        pnts.y = this.p3fRefCoords[i].y;
        pnts.z = this.p3fRefCoords[i].z;
        break;
    case GeometryArrayRetained.P3D:
        pnts.x = this.p3dRefCoords[i].x;
        pnts.y = this.p3dRefCoords[i].y;
        pnts.z = this.p3dRefCoords[i].z;
        break;
    }
      }
  }// end of non nio buffer
  else { // NIO BUFFER
      if ((vertexFormat & GeometryArray.INTERLEAVED) != 0) {
    offset = stride * i + coordinateOffset;
    pnts.x = this.interleavedFloatBufferImpl.get(offset);
    pnts.y = this.interleavedFloatBufferImpl.get(offset+1);
    pnts.z = this.interleavedFloatBufferImpl.get(offset+2);
      }
      else {
    switch ((vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
    case GeometryArrayRetained.PF:
        offset = i*3;
        pnts.x = this.floatBufferRefCoords.get(offset);
        pnts.y = this.floatBufferRefCoords.get(offset+1);
        pnts.z = this.floatBufferRefCoords.get(offset+2);
        break;
    case GeometryArrayRetained.PD:
        offset = i*3;
        pnts.x = this.doubleBufferRefCoords.get(offset);
        pnts.y = this.doubleBufferRefCoords.get(offset+1);
        pnts.z = this.doubleBufferRefCoords.get(offset+2);
        break;
    }
      }
  } // end of nio buffer
    }

    void getCrossValue(Point3d p1, Point3d p2, Vector3d value) {
        value.x += p1.y*p2.z - p1.z*p2.y;
  value.y += p2.x*p1.z - p2.z*p1.x;
        value.z += p1.x*p2.y - p1.y*p2.x;
    }


    @Override
    boolean intersect(Transform3D thisLocalToVworld,
          Transform3D otherLocalToVworld, GeometryRetained  geom) {

  Transform3D t3d =  new Transform3D();
  boolean isIntersect = false;

  if (geom instanceof GeometryArrayRetained ) {
      GeometryArrayRetained geomArray = (GeometryArrayRetainedgeom;

      if (geomArray.validVertexCount >= validVertexCount) {
    t3d.invert(otherLocalToVworld);
    t3d.mul(thisLocalToVworld);
    isIntersect = intersect(t3d, geom);
      } else {
    t3d.invert(thisLocalToVworld);
    t3d.mul(otherLocalToVworld);
    isIntersect = geomArray.intersect(t3d, this);
      }
  } else {
    t3d.invert(thisLocalToVworld);
    t3d.mul(otherLocalToVworld);
    isIntersect = geom.intersect(t3d, this);
  }
  return isIntersect;
    }

    int getNumCoordCount() {
  int count = 0;
  if ((vertexFormat & GeometryArray.COORDINATES) != 0){
      if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0){
    count = vertexCount;
    return count;
      }

      if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) {
    if ((vertexFormat & GeometryArray.INTERLEAVED) == 0){
        switch ((vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
        case PF:
      count =  floatRefCoords.length/3;
      break;
        case PD:
      count = doubleRefCoords.length/3;
      break;
        case P3F:
      count = p3fRefCoords.length;
      break;
        case P3D:
      count = p3dRefCoords.length;
      break;
        }
    }
    else {
        count = interLeavedVertexData.length/stride;
    }
      }
      else { // nio buffer
    if ((vertexFormat & GeometryArray.INTERLEAVED) == 0){
        switch ((vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
        case PF:
      count =  floatBufferRefCoords.limit()/3; // XXXX: limit or capacity?
      break;
        case PD:
      count = doubleBufferRefCoords.limit()/3;
      break;
        }
    }
    else {
        count = interleavedFloatBufferImpl.limit()/stride;
    }
      }
  }
  return count;
    }

    int getNumColorCount() {
  int count = 0;
  if ((vertexFormat & GeometryArray.COLOR) != 0){
      if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0){
    count = vertexCount;
    return count;
      }
      if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) {
    if ((vertexFormat & GeometryArray.INTERLEAVED) == 0){
        switch ((vertexType & GeometryArrayRetained.COLOR_DEFINED)) {
        case CF:
      if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
          count =  floatRefColors.length/3;
      }
      else if ((vertexFormat & GeometryArray.COLOR_4)== GeometryArray.COLOR_4){
          count =  floatRefColors.length/4;
      }
      break;
        case CUB:
      if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
          count =  byteRefColors.length/3;
      }
      else if ((vertexFormat & GeometryArray.COLOR_4)== GeometryArray.COLOR_4){
          count =  byteRefColors.length/4;
      }
      break;
        case C3F:
      count = c3fRefColors.length;
      break;
        case C4F:
      count = c4fRefColors.length;
      break;
        case C3UB:
      count = c3bRefColors.length;
      break;
        case C4UB:
      count = c4bRefColors.length;
      break;
        }
    }
    else {
        count = interLeavedVertexData.length/stride;
    }
      } // end of non nio buffer
      else {
    if ((vertexFormat & GeometryArray.INTERLEAVED) == 0){
        switch ((vertexType & GeometryArrayRetained.COLOR_DEFINED)) {
        case CF:
      if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
          count =  floatBufferRefColors.limit()/3;
      }
      else if ((vertexFormat & GeometryArray.COLOR_4)== GeometryArray.COLOR_4){
          count =  floatBufferRefColors.limit()/4;
      }
      break;
        case CUB:
      if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_3) {
          count =  byteBufferRefColors.limit()/3;
      }
      else if ((vertexFormat & GeometryArray.COLOR_4)== GeometryArray.COLOR_4){
          count =  byteBufferRefColors.limit()/4;
      }
      break;
        }
    }
    else {
        count = interleavedFloatBufferImpl.limit()/stride;
    }
      } // end of nio buffer
  }
  return count;
    }

    int getNumNormalCount() {
  int count = 0;
  if ((vertexFormat & GeometryArray.NORMALS) != 0){
      if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0){
    count = vertexCount;
    return count;
      }

      if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) {
    if ((vertexFormat & GeometryArray.INTERLEAVED) == 0){
        switch ((vertexType & NORMAL_DEFINED)) {
        case NF:
      count =  floatRefNormals.length/3;
      break;
        case N3F:
      count = v3fRefNormals.length;
      break;
        }
    }
    else {
        count = interLeavedVertexData.length/stride;
    }
      } // end of non nio buffer
      else {
    if ((vertexFormat & GeometryArray.INTERLEAVED) == 0){
        if ((vertexType & NORMAL_DEFINED) == NF ) {
      count =  floatBufferRefNormals.limit()/3;
        }
    }
    else {
        count = interleavedFloatBufferImpl.limit()/stride;
    }
      }
  }
  return count;
    }

    int getNumTexCoordCount(int i) {
  int count = 0;
  if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0){
      if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0){
    count = vertexCount;
    return count;
      }

      if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) == 0) {
    if ((vertexFormat & GeometryArray.INTERLEAVED) == 0){
        switch ((vertexType & TEXCOORD_DEFINED)) {
        case TF:
      if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
          count = ((float[])refTexCoords[i]).length/2;
      } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
          count = ((float[])refTexCoords[i]).length/3;
      } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
          count = ((float[])refTexCoords[i]).length/4;
      }

      break;
        case T2F:
      count = ((TexCoord2f[])refTexCoords[i]).length;
      break;
        case T3F:
      count = ((TexCoord3f[])refTexCoords[i]).length;
        }
    }
    else {
        count = interLeavedVertexData.length/stride;
    }
      }
      else { // nio buffer
    if ((vertexFormat & GeometryArray.INTERLEAVED) == 0){
        if ((vertexType & TEXCOORD_DEFINED) == TF) {
      FloatBuffer texBuffer = (FloatBuffer)refTexCoordsBuffer[i].getROBuffer();
      if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
          count = texBuffer.limit()/2;
      } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
          count = texBuffer.limit()/3;
      } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
          count = texBuffer.limit()/4;
      }
        }
    }
    else {
        count = interleavedFloatBufferImpl.limit()/stride;
    }
      }
  }
  return count;
    }

    // NOTE: we don't need a getNumVertexAttrCount method, since getNum*Count
    // is only called by Morph, which doesn't support vertex attrs


    // Found the min distance from center to the point/line/tri/quad
    // form by dist[]
    void computeMinDistance(Point3d coordinates[], Point3d center,
          Vector3d normal,
          double dist[], Point3d iPnt) {
  double x, y, z;
  int i, j;

  if (coordinates.length == 1) {
      // a point
      iPnt.x = coordinates[0].x;
      iPnt.y = coordinates[0].y;
      iPnt.z = coordinates[0].z;
      x = iPnt.x - center.x;
      y = iPnt.y - center.y;
      z = iPnt.z - center.z;
      dist[0] = Math.sqrt(x*x + y*y + z*z);
      return;
  }


  if (coordinates.length == 2) {
      // a line
      dist[0] = Math.sqrt(Utils.ptToSegSquare(center, coordinates[0], coordinates[1], iPnt));
      return;
  }

  double normalLen = 0;

  if (normal == null) {
      Vector3d vec0 = new Vector3d();
      Vector3d vec1 = new Vector3d();
      normal = new Vector3d();
      // compute plane normal for coordinates.
      for (i=0; i<coordinates.length-1;) {
    vec0.x = coordinates[i+1].x - coordinates[i].x;
    vec0.y = coordinates[i+1].y - coordinates[i].y;
    vec0.z = coordinates[i+1].z - coordinates[i++].z;
    if(vec0.length() > 0.0)
        break;
      }

      for (j=i; j<coordinates.length-1; j++) {
    vec1.x = coordinates[j+1].x - coordinates[j].x;
    vec1.y = coordinates[j+1].y - coordinates[j].y;
    vec1.z = coordinates[j+1].z - coordinates[j].z;
    if(vec1.length() > 0.0)
        break;
      }

      if (j == (coordinates.length-1)) {
    // Degenerate polygon, check with edge only
    normal = null;
      } else {
    normal.cross(vec0,vec1);
      }
  }

  if (normal != null) {
      normalLen = normal.length();
      if ( normalLen == 0.0) {
    // Degenerate polygon, check with edge only
    normal = null;
      }
  }


  if (coordinates.length == 3) {
      // a triangle
      if (normal != null) {
    double d = -(normal.x*coordinates[0].x +
           normal.y*coordinates[0].y +
           normal.z*coordinates[0].z);
    dist[0] = (normal.x*center.x + normal.y*center.y +
         normal.z*center.z +
         d)/normalLen;
    iPnt.x = center.x - dist[0]*normal.x/normalLen;
    iPnt.y = center.y - dist[0]*normal.y/normalLen;
    iPnt.z = center.z - dist[0]*normal.z/normalLen;

     if (pointInTri(iPnt, coordinates[0], coordinates[1],
        coordinates[2], normal)) {
         return;
     }
      }

      // checking point to line distance
      double minDist;
      Point3d minPnt = new Point3d();

      dist[0] = Utils.ptToSegSquare(center, coordinates[0], coordinates[1], iPnt);
      minDist = Utils.ptToSegSquare(center, coordinates[1], coordinates[2], minPnt);
      if (minDist < dist[0]) {
    dist[0] = minDist;
    iPnt.x = minPnt.x;
    iPnt.y = minPnt.y;
    iPnt.z = minPnt.z;
      }
      minDist = Utils.ptToSegSquare(center, coordinates[2], coordinates[0], minPnt);
      if (minDist < dist[0]) {
    dist[0] = minDist;
    iPnt.x = minPnt.x;
    iPnt.y = minPnt.y;
    iPnt.z = minPnt.z;
      }
      dist[0] = Math.sqrt(dist[0]);
      return;
  }

  // a quad
  if (normal != null) {
      double d = -(normal.x*coordinates[0].x +
       normal.y*coordinates[0].y +
       normal.z*coordinates[0].z);
      dist[0] = (normal.x*center.x + normal.y*center.y +
           normal.z*center.z +
           d)/normalLen;
      iPnt.x = center.x - dist[0]*normal.x/normalLen;
      iPnt.y = center.y - dist[0]*normal.y/normalLen;
      iPnt.z = center.z - dist[0]*normal.z/normalLen;

      if (pointInTri(iPnt, coordinates[0], coordinates[1],
         coordinates[2], normal) ||
    pointInTri(iPnt, coordinates[1], coordinates[2],
         coordinates[3], normal)) {
    return;
      }
  }

  // checking point to line distance
  double minDist;
  Point3d minPnt = new Point3d();

  dist[0] = Utils.ptToSegSquare(center, coordinates[0], coordinates[1], iPnt);
  minDist = Utils.ptToSegSquare(center, coordinates[1], coordinates[2], minPnt);
  if (minDist < dist[0]) {
      dist[0] = minDist;
      iPnt.x = minPnt.x;
      iPnt.y = minPnt.y;
      iPnt.z = minPnt.z;
  }
  minDist = Utils.ptToSegSquare(center, coordinates[2], coordinates[3], minPnt);
  if (minDist < dist[0]) {
      dist[0] = minDist;
      iPnt.x = minPnt.x;
      iPnt.y = minPnt.y;
      iPnt.z = minPnt.z;
  }

  minDist = Utils.ptToSegSquare(center, coordinates[3], coordinates[0], minPnt);
  if (minDist < dist[0]) {
      dist[0] = minDist;
      iPnt.x = minPnt.x;
      iPnt.y = minPnt.y;
      iPnt.z = minPnt.z;
  }

  dist[0] = Math.sqrt(dist[0]);
    }

    @Override
    void handleFrequencyChange(int bit) {
  int mask = 0;
  if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
      if ((bit == GeometryArray.ALLOW_COORDINATE_WRITE) ||
    (((vertexFormat & GeometryArray.COLOR) != 0) &&
     bit == GeometryArray.ALLOW_COLOR_WRITE)||
    (((vertexFormat & GeometryArray.NORMALS) != 0) &&
     bit == GeometryArray.ALLOW_NORMAL_WRITE) ||
    (((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) &&
     bit == GeometryArray.ALLOW_TEXCOORD_WRITE) ||
    (((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) &&
     bit == GeometryArray.ALLOW_VERTEX_ATTR_WRITE) ||
    (bit == GeometryArray.ALLOW_COUNT_WRITE)) {
    mask = 1;
      }
  }
  else {
      if (bit == GeometryArray.ALLOW_REF_DATA_WRITE)
    mask = 1;
  }
  if (mask != 0) {
      setFrequencyChangeMask(bit, mask);
  }
    }

    int getTexCoordType() {
        return texCoordType;
    }

    int getVertexAttrType() {
        return vertexAttrType;
    }

}
TOP

Related Classes of javax.media.j3d.GeometryArrayRetained

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.