From 356bbf2bd325380e16fd77d34fffd084dc1c0928 Mon Sep 17 00:00:00 2001
From: Kevin Rushforth <kcr@dev.java.net>
Date: Mon, 17 Oct 2005 22:56:23 +0000
Subject: Merged changes from dev-1_4 branch into the main trunk.

NOTE: all 1.4 development will now proceed on the main trunk. The dev-1_4 branch is closed.


git-svn-id: https://svn.java.net/svn/j3d-core~svn/trunk@445 ba19aa83-45c5-6ac9-afd3-db810772062c
---
 .../share/javax/media/j3d/AlternateAppearance.java |   32 +-
 .../media/j3d/AlternateAppearanceRetained.java     |    2 +-
 src/classes/share/javax/media/j3d/Appearance.java  |   20 +-
 .../share/javax/media/j3d/AppearanceRetained.java  |   21 +-
 .../share/javax/media/j3d/AttributeBin.java        |  267 +--
 .../share/javax/media/j3d/AuralAttributes.java     |   29 +
 .../javax/media/j3d/AuralAttributesRetained.java   |    2 +-
 src/classes/share/javax/media/j3d/Background.java  |   32 +-
 .../share/javax/media/j3d/BehaviorStructure.java   |    2 +-
 src/classes/share/javax/media/j3d/BoundingBox.java |    2 +-
 .../share/javax/media/j3d/BoundingLeaf.java        |   13 +-
 .../share/javax/media/j3d/BoundingPolytope.java    |   30 +-
 .../share/javax/media/j3d/BoundingSphere.java      |    2 +-
 src/classes/share/javax/media/j3d/BranchGroup.java |  399 +++-
 .../share/javax/media/j3d/BranchGroupRetained.java |  152 +-
 src/classes/share/javax/media/j3d/Canvas3D.java    |  468 ++++-
 .../share/javax/media/j3d/CanvasViewCache.java     |   81 +-
 .../javax/media/j3d/CanvasViewEventCatcher.java    |   14 +-
 .../share/javax/media/j3d/CapabilityBits.java      |   37 +-
 .../share/javax/media/j3d/CgShaderProgram.java     |  173 ++
 .../javax/media/j3d/CgShaderProgramRetained.java   |  283 +++
 src/classes/share/javax/media/j3d/Clip.java        |   11 +
 .../share/javax/media/j3d/ColoringAttributes.java  |   21 +-
 .../share/javax/media/j3d/CompressedGeometry.java  |   75 +-
 .../javax/media/j3d/CompressedGeometryHeader.java  |    2 +
 .../media/j3d/CompressedGeometryRetained.java      |   34 +-
 src/classes/share/javax/media/j3d/ConeSound.java   |   40 +
 .../share/javax/media/j3d/DepthComponent.java      |    8 +
 .../share/javax/media/j3d/DirectionalLight.java    |   15 +
 .../javax/media/j3d/DisplayListRenderMethod.java   |    6 +-
 .../javax/media/j3d/ExceptionStrings.properties    |   72 +-
 .../share/javax/media/j3d/ExponentialFog.java      |   21 +
 .../javax/media/j3d/ExponentialFogRetained.java    |   25 +-
 src/classes/share/javax/media/j3d/Fog.java         |   19 +-
 src/classes/share/javax/media/j3d/FogRetained.java |   21 +-
 src/classes/share/javax/media/j3d/Font3D.java      |   45 +-
 .../share/javax/media/j3d/FreeListManager.java     |    8 +-
 .../share/javax/media/j3d/GLSLShaderProgram.java   |  158 ++
 .../javax/media/j3d/GLSLShaderProgramRetained.java |  281 +++
 src/classes/share/javax/media/j3d/Geometry.java    |   10 +-
 .../share/javax/media/j3d/GeometryArray.java       | 2119 +++++++++++++++++---
 .../javax/media/j3d/GeometryArrayRetained.java     | 1794 +++++++++++++----
 .../media/j3d/GeometryDecompressorRetained.java    |    2 +-
 .../share/javax/media/j3d/GeometryRetained.java    |   73 +-
 .../share/javax/media/j3d/GeometryStripArray.java  |  146 +-
 .../media/j3d/GeometryStripArrayRetained.java      |  158 +-
 .../share/javax/media/j3d/GeometryStructure.java   |    1 -
 .../media/j3d/GraphStructureChangeListener.java    |   54 +
 .../share/javax/media/j3d/GraphicsConfigInfo.java  |   35 +
 .../javax/media/j3d/GraphicsConfigTemplate3D.java  |   35 +-
 .../share/javax/media/j3d/GraphicsContext3D.java   |  265 ++-
 src/classes/share/javax/media/j3d/Group.java       |    9 +
 .../share/javax/media/j3d/GroupRetained.java       |   30 +-
 .../share/javax/media/j3d/ImageComponent.java      |   14 +
 .../javax/media/j3d/ImageComponent2DRetained.java  |    6 +-
 .../share/javax/media/j3d/ImageComponent3D.java    |    2 +-
 .../javax/media/j3d/IndexedGeometryArray.java      |  447 ++++-
 .../media/j3d/IndexedGeometryArrayRetained.java    | 1106 +++++-----
 .../javax/media/j3d/IndexedGeometryStripArray.java |  170 +-
 .../j3d/IndexedGeometryStripArrayRetained.java     |   42 +-
 .../share/javax/media/j3d/IndexedLineArray.java    |  219 +-
 .../javax/media/j3d/IndexedLineArrayRetained.java  |   54 +-
 .../javax/media/j3d/IndexedLineStripArray.java     |  254 ++-
 .../media/j3d/IndexedLineStripArrayRetained.java   |   77 +-
 .../share/javax/media/j3d/IndexedPointArray.java   |  206 +-
 .../javax/media/j3d/IndexedPointArrayRetained.java |   38 +-
 .../share/javax/media/j3d/IndexedQuadArray.java    |  208 +-
 .../javax/media/j3d/IndexedQuadArrayRetained.java  |   73 +-
 .../javax/media/j3d/IndexedTriangleArray.java      |  215 +-
 .../media/j3d/IndexedTriangleArrayRetained.java    |   95 +-
 .../javax/media/j3d/IndexedTriangleFanArray.java   |  248 ++-
 .../media/j3d/IndexedTriangleFanArrayRetained.java |  116 +-
 .../javax/media/j3d/IndexedTriangleStripArray.java |  244 ++-
 .../j3d/IndexedTriangleStripArrayRetained.java     |   97 +-
 .../share/javax/media/j3d/IndexedUnorderSet.java   |    2 +-
 src/classes/share/javax/media/j3d/J3DBuffer.java   |   12 +-
 .../share/javax/media/j3d/J3DGraphics2DImpl.java   |   52 +-
 src/classes/share/javax/media/j3d/J3dMessage.java  |    3 +
 .../share/javax/media/j3d/J3dNotification.java     |   42 +
 src/classes/share/javax/media/j3d/Light.java       |   20 +-
 src/classes/share/javax/media/j3d/LineArray.java   |  183 +-
 .../share/javax/media/j3d/LineArrayRetained.java   |   50 +-
 .../share/javax/media/j3d/LineAttributes.java      |   13 +-
 .../share/javax/media/j3d/LineStripArray.java      |  208 +-
 .../javax/media/j3d/LineStripArrayRetained.java    |   99 +-
 src/classes/share/javax/media/j3d/LinearFog.java   |   39 +-
 .../share/javax/media/j3d/LinearFogRetained.java   |   26 +-
 src/classes/share/javax/media/j3d/Link.java        |   12 +-
 .../share/javax/media/j3d/LinkRetained.java        |    2 +-
 src/classes/share/javax/media/j3d/Locale.java      |  445 +++-
 .../share/javax/media/j3d/MasterControl.java       |  330 ++-
 src/classes/share/javax/media/j3d/Material.java    |   16 +-
 .../share/javax/media/j3d/MediaContainer.java      |   17 +
 .../javax/media/j3d/MediaContainerRetained.java    |    2 +-
 src/classes/share/javax/media/j3d/ModelClip.java   |   20 +-
 src/classes/share/javax/media/j3d/Morph.java       |   40 +-
 .../share/javax/media/j3d/MorphRetained.java       |  132 +-
 src/classes/share/javax/media/j3d/Node.java        |  112 +-
 src/classes/share/javax/media/j3d/NodeData.java    |    4 +-
 .../share/javax/media/j3d/NodeRetained.java        |   36 +-
 .../share/javax/media/j3d/NotificationThread.java  |  120 ++
 .../share/javax/media/j3d/OrderedGroup.java        |    6 +
 .../share/javax/media/j3d/OrientedShape3D.java     |   24 +
 .../share/javax/media/j3d/PhysicalBody.java        |    2 +-
 src/classes/share/javax/media/j3d/PickConeRay.java |    2 +-
 .../share/javax/media/j3d/PickConeSegment.java     |    2 +-
 .../share/javax/media/j3d/PickCylinder.java        |    2 +-
 .../share/javax/media/j3d/PickCylinderRay.java     |    2 +-
 .../share/javax/media/j3d/PickCylinderSegment.java |    2 +-
 src/classes/share/javax/media/j3d/PickInfo.java    | 1056 ++++++++++
 src/classes/share/javax/media/j3d/PickPoint.java   |    4 +
 src/classes/share/javax/media/j3d/Picking.java     |  660 ------
 src/classes/share/javax/media/j3d/PointArray.java  |  182 +-
 .../share/javax/media/j3d/PointArrayRetained.java  |   39 +-
 .../share/javax/media/j3d/PointAttributes.java     |   12 +-
 src/classes/share/javax/media/j3d/PointLight.java  |   16 +
 src/classes/share/javax/media/j3d/PointSound.java  |   34 +
 .../share/javax/media/j3d/PolygonAttributes.java   |   13 +
 src/classes/share/javax/media/j3d/QuadArray.java   |  183 +-
 .../share/javax/media/j3d/QuadArrayRetained.java   |   52 +-
 src/classes/share/javax/media/j3d/Raster.java      |   22 +
 .../share/javax/media/j3d/RasterRetained.java      |    3 +-
 src/classes/share/javax/media/j3d/RenderBin.java   |  539 +++--
 .../share/javax/media/j3d/RenderMolecule.java      |   93 +-
 src/classes/share/javax/media/j3d/Renderer.java    |  192 +-
 .../share/javax/media/j3d/RenderingAttributes.java |  851 +++++++-
 .../media/j3d/RenderingAttributesRetained.java     |  275 ++-
 .../media/j3d/RenderingAttributesStructure.java    |   24 +-
 .../share/javax/media/j3d/SceneGraphObject.java    |   99 +-
 .../share/javax/media/j3d/ScreenViewCache.java     |   32 +-
 src/classes/share/javax/media/j3d/Sensor.java      |  278 +--
 .../share/javax/media/j3d/SetLiveState.java        |    2 +-
 src/classes/share/javax/media/j3d/Shader.java      |  131 ++
 .../share/javax/media/j3d/ShaderAppearance.java    |  285 +++
 .../javax/media/j3d/ShaderAppearanceRetained.java  |  360 ++++
 .../share/javax/media/j3d/ShaderAttribute.java     |   77 +
 .../javax/media/j3d/ShaderAttributeArray.java      |  147 ++
 .../media/j3d/ShaderAttributeArrayRetained.java    |  996 +++++++++
 .../javax/media/j3d/ShaderAttributeBinding.java    |  128 ++
 .../media/j3d/ShaderAttributeBindingRetained.java  |   57 +
 .../javax/media/j3d/ShaderAttributeObject.java     |  130 ++
 .../media/j3d/ShaderAttributeObjectRetained.java   |  311 +++
 .../javax/media/j3d/ShaderAttributeRetained.java   |   82 +
 .../share/javax/media/j3d/ShaderAttributeSet.java  |  263 +++
 .../media/j3d/ShaderAttributeSetRetained.java      |  390 ++++
 .../javax/media/j3d/ShaderAttributeValue.java      |  100 +
 .../media/j3d/ShaderAttributeValueRetained.java    |  499 +++++
 src/classes/share/javax/media/j3d/ShaderBin.java   |  363 ++++
 src/classes/share/javax/media/j3d/ShaderError.java |  410 ++++
 .../share/javax/media/j3d/ShaderErrorListener.java |   33 +
 .../share/javax/media/j3d/ShaderProgram.java       |  194 ++
 .../javax/media/j3d/ShaderProgramRetained.java     | 1196 +++++++++++
 .../share/javax/media/j3d/ShaderRetained.java      |   78 +
 src/classes/share/javax/media/j3d/Shape3D.java     |   20 +-
 .../javax/media/j3d/Shape3DCompileRetained.java    |  162 +-
 .../share/javax/media/j3d/Shape3DRetained.java     |  237 ++-
 src/classes/share/javax/media/j3d/SharedGroup.java |   14 +-
 .../share/javax/media/j3d/SharedGroupRetained.java |    6 +-
 src/classes/share/javax/media/j3d/Sound.java       |   32 +-
 .../share/javax/media/j3d/SoundRetained.java       |   18 +-
 .../share/javax/media/j3d/SoundScheduler.java      |   46 +-
 .../share/javax/media/j3d/SoundSchedulerAtom.java  |   10 +-
 .../share/javax/media/j3d/SoundStructure.java      |   14 +-
 src/classes/share/javax/media/j3d/Soundscape.java  |   12 +-
 .../share/javax/media/j3d/SourceCodeShader.java    |  121 ++
 .../javax/media/j3d/SourceCodeShaderRetained.java  |   85 +
 src/classes/share/javax/media/j3d/SpotLight.java   |   17 +
 src/classes/share/javax/media/j3d/Switch.java      |   17 +-
 .../share/javax/media/j3d/TexCoordGeneration.java  |   35 +-
 src/classes/share/javax/media/j3d/Text3D.java      |   25 +
 .../share/javax/media/j3d/Text3DRetained.java      |   32 +-
 src/classes/share/javax/media/j3d/Texture.java     |   27 +-
 src/classes/share/javax/media/j3d/Texture2D.java   |   14 +-
 .../share/javax/media/j3d/TextureAttributes.java   |   16 +-
 .../javax/media/j3d/TextureAttributesRetained.java |   28 +-
 src/classes/share/javax/media/j3d/TextureBin.java  |  220 +-
 .../share/javax/media/j3d/TextureRetained.java     |   57 +-
 .../share/javax/media/j3d/TextureUnitState.java    |   10 +-
 .../javax/media/j3d/TextureUnitStateRetained.java  |   11 -
 src/classes/share/javax/media/j3d/TimerThread.java |    4 +-
 src/classes/share/javax/media/j3d/Transform3D.java |    4 +-
 .../share/javax/media/j3d/TransformGroup.java      |   14 +-
 .../share/javax/media/j3d/TransformGroupData.java  |    4 +-
 .../javax/media/j3d/TransformGroupRetained.java    |    8 +-
 .../share/javax/media/j3d/TransformStructure.java  |    4 +-
 .../javax/media/j3d/TransparencyAttributes.java    |  223 +-
 .../media/j3d/TransparencyAttributesRetained.java  |   10 -
 .../javax/media/j3d/TransparentRenderingInfo.java  |   96 +-
 .../share/javax/media/j3d/TriangleArray.java       |  185 +-
 .../javax/media/j3d/TriangleArrayRetained.java     |   58 +-
 .../share/javax/media/j3d/TriangleFanArray.java    |  212 +-
 .../javax/media/j3d/TriangleFanArrayRetained.java  |  125 +-
 .../share/javax/media/j3d/TriangleStripArray.java  |  211 +-
 .../media/j3d/TriangleStripArrayRetained.java      |   58 +-
 src/classes/share/javax/media/j3d/View.java        |    2 +-
 src/classes/share/javax/media/j3d/ViewCache.java   |    3 +-
 .../share/javax/media/j3d/ViewPlatform.java        |    7 +
 .../javax/media/j3d/ViewPlatformRetained.java      |    1 -
 .../share/javax/media/j3d/ViewSpecificGroup.java   |    8 +-
 .../javax/media/j3d/ViewSpecificGroupRetained.java |    4 +-
 .../share/javax/media/j3d/VirtualUniverse.java     |  190 +-
 .../share/javax/media/j3d/WakeupIndexedList.java   |    2 +-
 202 files changed, 21516 insertions(+), 5758 deletions(-)
 create mode 100644 src/classes/share/javax/media/j3d/CgShaderProgram.java
 create mode 100644 src/classes/share/javax/media/j3d/CgShaderProgramRetained.java
 create mode 100644 src/classes/share/javax/media/j3d/GLSLShaderProgram.java
 create mode 100644 src/classes/share/javax/media/j3d/GLSLShaderProgramRetained.java
 create mode 100755 src/classes/share/javax/media/j3d/GraphStructureChangeListener.java
 create mode 100644 src/classes/share/javax/media/j3d/GraphicsConfigInfo.java
 create mode 100644 src/classes/share/javax/media/j3d/J3dNotification.java
 create mode 100644 src/classes/share/javax/media/j3d/NotificationThread.java
 create mode 100644 src/classes/share/javax/media/j3d/PickInfo.java
 delete mode 100644 src/classes/share/javax/media/j3d/Picking.java
 create mode 100644 src/classes/share/javax/media/j3d/Shader.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderAppearance.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderAppearanceRetained.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderAttribute.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderAttributeArray.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderAttributeArrayRetained.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderAttributeBinding.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderAttributeBindingRetained.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderAttributeObject.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderAttributeObjectRetained.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderAttributeRetained.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderAttributeSet.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderAttributeSetRetained.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderAttributeValue.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderAttributeValueRetained.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderBin.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderError.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderErrorListener.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderProgram.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderProgramRetained.java
 create mode 100644 src/classes/share/javax/media/j3d/ShaderRetained.java
 create mode 100644 src/classes/share/javax/media/j3d/SourceCodeShader.java
 create mode 100644 src/classes/share/javax/media/j3d/SourceCodeShaderRetained.java

(limited to 'src/classes/share/javax')

diff --git a/src/classes/share/javax/media/j3d/AlternateAppearance.java b/src/classes/share/javax/media/j3d/AlternateAppearance.java
index e6d160c..fccc203 100644
--- a/src/classes/share/javax/media/j3d/AlternateAppearance.java
+++ b/src/classes/share/javax/media/j3d/AlternateAppearance.java
@@ -92,7 +92,13 @@ public class AlternateAppearance extends Leaf {
     public static final int ALLOW_SCOPE_WRITE =
 	CapabilityBits.ALTERNATE_APPEARANCE_ALLOW_SCOPE_WRITE;
 
-
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_INFLUENCING_BOUNDS_READ,
+        ALLOW_APPEARANCE_READ,
+        ALLOW_SCOPE_READ
+    };
+    
     /**
      * Constructs an AlternateAppearance node with default
      * parameters.  The default values are as follows:
@@ -106,28 +112,30 @@ public class AlternateAppearance extends Leaf {
      */
     public AlternateAppearance() {
 	// Just use the defaults
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+        
     }
 
-
-    /**
-     * Creates the retained mode AlternateAppearanceRetained object that this
-     * Alternate Appearance component object will point to.
-     */
-    void createRetained() {
-	this.retained = new AlternateAppearanceRetained();
-	this.retained.setSource(this);
-    }
-
-
     /**
      * Constructs an AlternateAppearance node with the specified appearance.
      * @param appearance the appearance that is used for those nodes affected
      * by this AlternateAppearance node.
      */
     public AlternateAppearance(Appearance appearance) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
 	((AlternateAppearanceRetained)retained).initAppearance(appearance);
     }
 
+    /**
+     * Creates the retained mode AlternateAppearanceRetained object that this
+     * Alternate Appearance component object will point to.
+     */
+    void createRetained() {
+	this.retained = new AlternateAppearanceRetained();
+	this.retained.setSource(this);
+    }
 
     /**
      * Sets the appearance of this AlternateAppearance node.
diff --git a/src/classes/share/javax/media/j3d/AlternateAppearanceRetained.java b/src/classes/share/javax/media/j3d/AlternateAppearanceRetained.java
index 0b88951..b7d17bf 100644
--- a/src/classes/share/javax/media/j3d/AlternateAppearanceRetained.java
+++ b/src/classes/share/javax/media/j3d/AlternateAppearanceRetained.java
@@ -803,7 +803,7 @@ class AlternateAppearanceRetained extends LeafRetained {
 
 //	AlternateAppearance alternate appearance = (AlternateAppearance) originalNode;
 
-//	// TODO: clone appearance
+//	// XXXX: clone appearance
 
 //	setInfluencingBounds(alternate appearance.getInfluencingBounds());
 
diff --git a/src/classes/share/javax/media/j3d/Appearance.java b/src/classes/share/javax/media/j3d/Appearance.java
index cb24eba..88aea7b 100644
--- a/src/classes/share/javax/media/j3d/Appearance.java
+++ b/src/classes/share/javax/media/j3d/Appearance.java
@@ -285,7 +285,21 @@ public class Appearance extends NodeComponent {
   public static final int ALLOW_TEXTURE_UNIT_STATE_WRITE =
     CapabilityBits.APPEARANCE_ALLOW_TEXTURE_UNIT_STATE_WRITE;
 
-
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_COLORING_ATTRIBUTES_READ,
+        ALLOW_LINE_ATTRIBUTES_READ,
+        ALLOW_MATERIAL_READ,
+        ALLOW_POINT_ATTRIBUTES_READ,
+        ALLOW_POLYGON_ATTRIBUTES_READ,
+        ALLOW_RENDERING_ATTRIBUTES_READ,
+        ALLOW_TEXGEN_READ,
+        ALLOW_TEXTURE_ATTRIBUTES_READ,
+        ALLOW_TEXTURE_READ,
+        ALLOW_TEXTURE_UNIT_STATE_READ,
+        ALLOW_TRANSPARENCY_ATTRIBUTES_READ        
+    };
+    
     /**
      * Constructs an Appearance component object using defaults for all
      * state variables. All component object references are initialized 
@@ -293,6 +307,8 @@ public class Appearance extends NodeComponent {
      */
     public Appearance() {
 	// Just use default values
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -919,7 +935,7 @@ public class Appearance extends NodeComponent {
 	if ((nc != null) && nc.getDuplicateOnCloneTree())
 	    return true;
 
-	// TODO: TextureUnitState
+	// XXXX: TextureUnitState
 	
 	return false;
     }
diff --git a/src/classes/share/javax/media/j3d/AppearanceRetained.java b/src/classes/share/javax/media/j3d/AppearanceRetained.java
index cc0489e..215c8eb 100644
--- a/src/classes/share/javax/media/j3d/AppearanceRetained.java
+++ b/src/classes/share/javax/media/j3d/AppearanceRetained.java
@@ -83,9 +83,6 @@ class AppearanceRetained extends NodeComponentRetained {
     static final int POINT              = 0x0200;
     static final int TEXTURE_UNIT_STATE = 0x0400;
 
-    static final int ALL_COMPONENTS = (MATERIAL|TEXTURE|TEXCOORD_GEN|TEXTURE_ATTR|COLOR|TRANSPARENCY|
-				       RENDERING|POLYGON|LINE|POINT|TEXTURE_UNIT_STATE);
-
     static final int ALL_SOLE_USERS = 0;
 
     // A pointer to the scene graph appearance object
@@ -876,11 +873,18 @@ class AppearanceRetained extends NodeComponentRetained {
 
     }
 
+    void setLive(boolean backgroundGroup, int refCount) {
+	// System.out.println("AppearceRetained.setLive()");
+	doSetLive(backgroundGroup, refCount);
+	markAsLive();
+    }
+
     /**
-     * This setLive routine first calls the superclass's method, then
-     * it adds itself to the list of lights
+     * This method calls the setLive method of all appearance bundle
+     * objects.
      */
-    void setLive(boolean backgroundGroup, int refCount) {
+    void doSetLive(boolean backgroundGroup, int refCount) {
+	// System.out.println("AppearceRetained.doSetLive()");
 	
 	if (material != null) {	    
 	
@@ -938,12 +942,11 @@ class AppearanceRetained extends NodeComponentRetained {
 	// Increment the reference count and initialize the appearance
 	// mirror object
         super.doSetLive(backgroundGroup, refCount);
-	super.markAsLive();
     }
 
     /**
-     * This clearLive routine first calls the superclass's method, then
-     * it removes itself to the list of lights
+     * This method calls the clearLive method of all appearance bundle
+     * objects.
      */
     void clearLive(int refCount) {
 	super.clearLive(refCount);
diff --git a/src/classes/share/javax/media/j3d/AttributeBin.java b/src/classes/share/javax/media/j3d/AttributeBin.java
index 8e98708..cc1afd4 100644
--- a/src/classes/share/javax/media/j3d/AttributeBin.java
+++ b/src/classes/share/javax/media/j3d/AttributeBin.java
@@ -27,6 +27,11 @@ class AttributeBin extends Object implements ObjectUpdate {
      */
     RenderingAttributesRetained definingRenderingAttributes = null;
 
+    /**
+     * The RenderBin for this object
+     */
+    RenderBin renderBin = null;
+
     /**
      * The EnvirionmentSet that this AttributeBin resides
      */
@@ -40,21 +45,14 @@ class AttributeBin extends Object implements ObjectUpdate {
     AttributeBin prev = null;
 
     /**
-     * The list of TextureBins in this AttributeBin
+     * The list of ShaderBins in this AttributeBin
      */
-    TextureBin textureBinList = null;
-
+    ShaderBin shaderBinList = null;
 
     /**
-     * The list of TextureBins to be added for the next frame
+     *  List of shaderBins to be added next frame
      */
-    ArrayList addTextureBins = new ArrayList();
-
-    /**
-     *  List of TextureBins to be added next frame
-     */
-    ArrayList addTBs = new ArrayList();
-
+    ArrayList addShaderBins = new ArrayList();
 
     /**
      * If the RenderingAttribute component of the appearance will be changed
@@ -63,11 +61,6 @@ class AttributeBin extends Object implements ObjectUpdate {
     boolean soleUser = false;
     AppearanceRetained app = null;
 
-    /**
-     *  List of TextureBins to be removeded next frame
-     */
-    ArrayList removeTBs = new ArrayList();
-
     int onUpdateList = 0;
     static int ON_OBJ_UPDATE_LIST = 0x1;
     static int ON_CHANGED_FREQUENT_UPDATE_LIST = 0x2;
@@ -76,44 +69,45 @@ class AttributeBin extends Object implements ObjectUpdate {
     // for whether the definingRendering attrs is non-null;
     boolean ignoreVertexColors = false;
 
-    // TODO: use definingMaterial etc. instead of these
+    // XXXX: use definingMaterial etc. instead of these
     // when sole user is completely implement
     RenderingAttributesRetained renderingAttrs;
 
-    int numEditingTextureBins = 0;
-
-
+    int numEditingShaderBins = 0;
 
     AttributeBin(AppearanceRetained app, RenderingAttributesRetained renderingAttributes, RenderBin rBin) {
+
 	reset(app, renderingAttributes, rBin);
     }
 
     void reset(AppearanceRetained app, RenderingAttributesRetained renderingAttributes, RenderBin rBin) {
 	prev = null;
 	next = null;
-	textureBinList = null;
+	shaderBinList = null;
 	onUpdateList = 0;
-	numEditingTextureBins = 0;
+	numEditingShaderBins = 0;
         renderingAttrs = renderingAttributes;
 
+	renderBin = rBin;	
+
 	if (app != null) {
 	    soleUser = ((app.changedFrequent & AppearanceRetained.RENDERING) != 0);
 	}
 	else {
 	    soleUser = false;
 	}
-	//	System.out.println("soleUser = "+soleUser+" renderingAttributes ="+renderingAttributes);
+	//System.out.println("soleUser = "+soleUser+" renderingAttributes ="+renderingAttributes);
 	// Set the appearance only for soleUser case
 	if (soleUser)
 	    this.app = app;
 	else
 	    app = null;
-
+	
 	if (renderingAttributes != null) {
 	    if (renderingAttributes.changedFrequent != 0) {
 		definingRenderingAttributes = renderingAttributes;
 		if ((onUpdateList & ON_CHANGED_FREQUENT_UPDATE_LIST) == 0 ) {
-		    rBin.aBinUpdateList.add(this);
+		    renderBin.aBinUpdateList.add(this);
 		    onUpdateList |= AttributeBin.ON_CHANGED_FREQUENT_UPDATE_LIST;
 		}
 	    }
@@ -138,12 +132,11 @@ class AttributeBin extends Object implements ObjectUpdate {
      */
     boolean equals(RenderingAttributesRetained renderingAttributes, RenderAtom ra) {
 
-	// If the any reference to the appearance components  that is cached renderMolecule
-	// can change frequently, make a separate bin
 	// If the any reference to the appearance components  that is cached renderMolecule
 	// can change frequently, make a separate bin
 	if (soleUser || (ra.geometryAtom.source.appearance != null &&
-			 ((ra.geometryAtom.source.appearance.changedFrequent & AppearanceRetained.RENDERING) != 0))) {
+			 ((ra.geometryAtom.source.appearance.changedFrequent & 
+			   AppearanceRetained.RENDERING) != 0))) {
 	    if (app == (Object)ra.geometryAtom.source.appearance) {
 
 		// if this AttributeBin is currently on a zombie state,
@@ -156,9 +149,9 @@ class AttributeBin extends Object implements ObjectUpdate {
                 // attributes reference change would not have reflected to 
 	        // the AttributeBin
 
-                if (numEditingTextureBins == 0) {
+                if (numEditingShaderBins == 0) {
 		    if ((onUpdateList & ON_CHANGED_FREQUENT_UPDATE_LIST) == 0) {
-			environmentSet.renderBin.aBinUpdateList.add(this);
+			renderBin.aBinUpdateList.add(this);
 			onUpdateList |= 
 				AttributeBin.ON_CHANGED_FREQUENT_UPDATE_LIST;
 		    }
@@ -180,7 +173,7 @@ class AttributeBin extends Object implements ObjectUpdate {
 		if (definingRenderingAttributes == renderingAttributes) {
 		    if (definingRenderingAttributes.compChanged != 0) {
 			if ((onUpdateList & ON_CHANGED_FREQUENT_UPDATE_LIST) == 0 ) {
-			    environmentSet.renderBin.aBinUpdateList.add(this);
+			    renderBin.aBinUpdateList.add(this);
 			    onUpdateList |= AttributeBin.ON_CHANGED_FREQUENT_UPDATE_LIST;
 			}
 		    }
@@ -196,127 +189,56 @@ class AttributeBin extends Object implements ObjectUpdate {
 	    return false;
 	}
 
-
-
 	return (true);
     }
 
-
     public void updateObject() {
+	ShaderBin sb;
 	TextureBin t;
-	int i;
+	int i, size;
 	
-	if (addTBs.size() > 0) {
-	    t = (TextureBin)addTBs.get(0);
-	    if (textureBinList == null) {
-		textureBinList = t;
-
+	size = addShaderBins.size();
+	if (size > 0) {
+	    sb = (ShaderBin)addShaderBins.get(0);
+	    if (shaderBinList == null) {
+		shaderBinList = sb;
 	    }
 	    else {
-		// Look for a TextureBin that has the same texture
-		insertTextureBin(t);	
-	    }	    
-	    for (i = 1; i < addTBs.size() ; i++) {
-		t = (TextureBin)addTBs.get(i);
-		// Look for a TextureBin that has the same texture
-		insertTextureBin(t);
-
+		sb.next = shaderBinList;
+		shaderBinList.prev = sb;
+		shaderBinList = sb;
 	    }
-	}
-	addTBs.clear();
-	onUpdateList &= ~ON_OBJ_UPDATE_LIST;
-    }
-    
-    void insertTextureBin(TextureBin t) {
-	TextureBin tb;
-	int i;
-	TextureRetained texture = null;
-
-	if (t.texUnitState != null && t.texUnitState.length > 0) {
-	    if (t.texUnitState[0] != null) {
-	        texture = t.texUnitState[0].texture;
+	    	    
+	    for (i = 1; i < size ; i++) {
+		sb = (ShaderBin)addShaderBins.get(i);
+		sb.next = shaderBinList;
+		shaderBinList.prev = sb;
+		shaderBinList = sb;
 	    }
 	}
-
-	// use the texture in the first texture unit as the sorting criteria
-	if (texture != null) {
-	    tb = textureBinList; 
-	    while (tb != null) { 
-		if (tb.texUnitState == null || tb.texUnitState[0] == null ||
-			tb.texUnitState[0].texture != texture) {
-		    tb = tb.next;
-		} else {
-		    // put it here  
-		    t.next = tb; 
-		    t.prev = tb.prev; 
-		    if (tb.prev == null) { 
-		        textureBinList = t; 
-		    } 
-		    else { 
-		        tb.prev.next = t; 
-		    } 
-		    tb.prev = t; 
-		    return; 
-	        } 
-	    }
-	} 
-	// Just put it up front
-	t.prev = null;
-	t.next = textureBinList;
-	textureBinList.prev = t;
-	textureBinList = t;
-
-	t.tbFlag &= ~TextureBin.RESORT;
+	addShaderBins.clear();
+	onUpdateList &= ~ON_OBJ_UPDATE_LIST;
     }
 
 
     /**
-     * reInsert textureBin if the first texture is different from
-     * the previous bin and different from the next bin
+     * Adds the given shaderBin to this AttributeBin.
      */
-    void reInsertTextureBin(TextureBin tb) {
-
-        TextureRetained texture = null,
-                        prevTexture = null,
-                        nextTexture = null;
-
-        if (tb.texUnitState != null && tb.texUnitState[0] != null) {
-            texture = tb.texUnitState[0].texture;
-        }
-
-        if (tb.prev != null && tb.prev.texUnitState != null) {
-            prevTexture = tb.prev.texUnitState[0].texture;
-        }
-
-        if (texture != prevTexture) {
-            if (tb.next != null && tb.next.texUnitState != null) {
-                nextTexture = tb.next.texUnitState[0].texture;
-            }
-            if (texture != nextTexture) {
-                if (tb.prev != null && tb.next != null) {
-                    tb.prev.next = tb.next;
-		    tb.next.prev = tb.prev;
-                    insertTextureBin(tb);
-                }
-            }
-        }
-    }
+    void addShaderBin(ShaderBin sb, RenderBin rb, ShaderAppearanceRetained sApp) {
 
+	sb.attributeBin = this;
 
-    /**
-     * Adds the given TextureBin to this AttributeBin.
-     */
-    void addTextureBin(TextureBin t, RenderBin rb, RenderAtom ra) {
-	int i;
-	t.attributeBin = this;
-        AppearanceRetained raApp = ra.geometryAtom.source.appearance;
-	RenderingAttributesRetained rAttrs = 
-		(raApp == null)? null : raApp.renderingAttributes;
-	if (!soleUser && renderingAttrs != rAttrs) {
-	    // no longer sole user
-            renderingAttrs = definingRenderingAttributes;
+	if(sApp != null) {
+	    // ShaderBin should reference to the mirror components. -- JADA.
+	    // System.out.println("AttributeBin : sApp.isMirror = " + sApp.isMirror);
+	    assert(sApp.isMirror);
+	    sb.shaderProgram = sApp.shaderProgram;
+	    sb.shaderAttributeSet = sApp.shaderAttributeSet;
 	}
-	addTBs.add(t);
+	sb.shaderAppearance = sApp;
+	
+	// TODO : JADA - sort by ShaderProgram to avoid state trashing.
+	addShaderBins.add(sb);
 	if ((onUpdateList & ON_OBJ_UPDATE_LIST) == 0) {
 	    onUpdateList |= ON_OBJ_UPDATE_LIST;
 	    rb.objUpdateList.add(this);
@@ -324,41 +246,35 @@ class AttributeBin extends Object implements ObjectUpdate {
 
     }
 
+
     /**
-     * Removes the given TextureBin from this AttributeBin.
+     * Removes the given shaderBin from this AttributeBin.
      */
-    void removeTextureBin(TextureBin t) {
-
-	int i;
-	TextureRetained tex;
+    void removeShaderBin(ShaderBin sb) {
 	
-	t.attributeBin = null;
-	// If the TextureBin being remove is contained in addTBs, then
-	// remove the TextureBin from the addList
-	if (addTBs.contains(t)) {
-	    addTBs.remove(addTBs.indexOf(t));
+	// If the shaderBin being remove is contained in addShaderBins, 
+	// then remove the shadereBin from the addList
+	if (addShaderBins.contains(sb)) {
+	    addShaderBins.remove(addShaderBins.indexOf(sb));
 	}
 	else {
-	    if (t.prev == null) { // At the head of the list
-		textureBinList = t.next;
-		if (t.next != null) {
-		    t.next.prev = null;
+	    if (sb.prev == null) { // At the head of the list
+		shaderBinList = sb.next;
+		if (sb.next != null) {
+		    sb.next.prev = null;
 		}
 	    } else { // In the middle or at the end.
-		t.prev.next = t.next;
-		if (t.next != null) {
-		    t.next.prev = t.prev;
+		sb.prev.next = sb.next;
+		if (sb.next != null) {
+		    sb.next.prev = sb.prev;
 		}
 	    }
 	}
-	t.prev = null;
-	t.next = null;
 
-	t.clear();
+	sb.clear();
+	renderBin.shaderBinFreelist.add(sb);
 
-	environmentSet.renderBin.textureBinFreelist.add(t);
-
-	if (textureBinList == null && addTBs.size() == 0 ) {
+	if (shaderBinList == null && addShaderBins.size() == 0 ) {
 	    // Note: Removal of this attributebin as a user of the rendering
 	    // atttrs is done during removeRenderAtom() in RenderMolecule.java
 	    environmentSet.removeAttributeBin(this);
@@ -370,14 +286,14 @@ class AttributeBin extends Object implements ObjectUpdate {
      */
     void render(Canvas3D cv) {
 
-	TextureBin t;
+	ShaderBin sb;
 	
 	boolean visible = (definingRenderingAttributes == null || 
 	    		       definingRenderingAttributes.visible);
 
-	if ( (environmentSet.renderBin.view.viewCache.visibilityPolicy
+	if ( (renderBin.view.viewCache.visibilityPolicy
 			== View.VISIBILITY_DRAW_VISIBLE && !visible) ||
-	     (environmentSet.renderBin.view.viewCache.visibilityPolicy
+	     (renderBin.view.viewCache.visibilityPolicy
 			== View.VISIBILITY_DRAW_INVISIBLE && visible)) {
 	    return;
 	}
@@ -386,15 +302,16 @@ class AttributeBin extends Object implements ObjectUpdate {
         // include this AttributeBin to the to-be-updated list in Canvas
         cv.setStateToUpdate(Canvas3D.ATTRIBUTEBIN_BIT, this);
 
-	t = textureBinList;
-	while (t != null) {
-	    t.render(cv);
-	    t = t.next;
+	sb = shaderBinList;
+	while (sb != null) {
+	    sb.render(cv);
+	    sb = sb.next;
 	}
     }
 
 
     void updateAttributes(Canvas3D cv) {
+
 	if ((cv.canvasDirty & Canvas3D.ATTRIBUTEBIN_DIRTY) != 0) {
 	    // Update Attribute Bundles
 	    if (definingRenderingAttributes == null) {
@@ -403,7 +320,7 @@ class AttributeBin extends Object implements ObjectUpdate {
 					    cv.depthBufferEnableOverride);
 	    } else {
 	        definingRenderingAttributes.updateNative(
-				    cv.ctx,
+				    cv,
 				    cv.depthBufferWriteEnableOverride,
 				    cv.depthBufferEnableOverride);
 	    }
@@ -420,7 +337,7 @@ class AttributeBin extends Object implements ObjectUpdate {
 					cv.depthBufferEnableOverride);
 	    } else {
 		definingRenderingAttributes.updateNative(
-				        cv.ctx,
+				        cv,
 					cv.depthBufferWriteEnableOverride,
 				    	cv.depthBufferEnableOverride);
 	    }
@@ -465,11 +382,23 @@ class AttributeBin extends Object implements ObjectUpdate {
 	onUpdateList &= ~ON_CHANGED_FREQUENT_UPDATE_LIST;
     }
 
-    void incrActiveTextureBin() {
-	numEditingTextureBins++;
+    void incrActiveShaderBin() {
+	numEditingShaderBins++;
     }
 
-    void decrActiveTextureBin() {
-	numEditingTextureBins--;
+    void decrActiveShaderBin() {
+	numEditingShaderBins--;
+    }
+
+    void updateFromShaderBin(RenderAtom ra) {
+
+	AppearanceRetained raApp = ra.geometryAtom.source.appearance;
+	RenderingAttributesRetained rAttrs = 
+	    (raApp == null)? null : raApp.renderingAttributes;
+
+ 	if (!soleUser && renderingAttrs != rAttrs) {
+	    // no longer sole user
+	    renderingAttrs = definingRenderingAttributes;
+ 	}
     }
 }
diff --git a/src/classes/share/javax/media/j3d/AuralAttributes.java b/src/classes/share/javax/media/j3d/AuralAttributes.java
index 89384e6..289383f 100644
--- a/src/classes/share/javax/media/j3d/AuralAttributes.java
+++ b/src/classes/share/javax/media/j3d/AuralAttributes.java
@@ -473,6 +473,24 @@ public class AuralAttributes extends NodeComponent {
      public static final int
     ALLOW_VELOCITY_SCALE_FACTOR_WRITE = CapabilityBits.AURAL_ATTRIBUTES_ALLOW_VELOCITY_SCALE_FACTOR_WRITE;
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_ATTRIBUTE_GAIN_READ,
+        ALLOW_DECAY_FILTER_READ,
+        ALLOW_DECAY_TIME_READ,
+        ALLOW_DENSITY_READ,
+        ALLOW_DIFFUSION_READ,
+        ALLOW_DISTANCE_FILTER_READ,
+        ALLOW_FREQUENCY_SCALE_FACTOR_READ,
+        ALLOW_REFLECTION_COEFFICIENT_READ,
+        ALLOW_REFLECTION_DELAY_READ,
+        ALLOW_REVERB_COEFFICIENT_READ,
+        ALLOW_REVERB_DELAY_READ,
+        ALLOW_REVERB_ORDER_READ,
+        ALLOW_ROLLOFF_READ,
+        ALLOW_VELOCITY_SCALE_FACTOR_READ        
+    };
+    
     /** *****************
      *   
      *  Constructors
@@ -501,6 +519,8 @@ public class AuralAttributes extends NodeComponent {
      */  
     public AuralAttributes() {
          // Just use default values
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -523,6 +543,9 @@ public class AuralAttributes extends NodeComponent {
                       Point2f[]         distanceFilter,
                       float      	frequencyScaleFactor,
                       float      	velocityScaleFactor) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((AuralAttributesRetained)this.retained).setAttributeGain(gain);
         ((AuralAttributesRetained)this.retained).setRolloff(rolloff);
         ((AuralAttributesRetained)this.retained).setReflectionCoefficient(
@@ -558,6 +581,9 @@ public class AuralAttributes extends NodeComponent {
                       float[]           frequencyCutoff,
                       float      	frequencyScaleFactor,
                       float      	velocityScaleFactor) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((AuralAttributesRetained)this.retained).setAttributeGain(gain);
         ((AuralAttributesRetained)this.retained).setRolloff(rolloff);
         ((AuralAttributesRetained)this.retained).setReflectionCoefficient(
@@ -606,6 +632,9 @@ public class AuralAttributes extends NodeComponent {
 			   float[]	frequencyCutoff,
 			   float	frequencyScaleFactor,
 			   float	velocityScaleFactor) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((AuralAttributesRetained)this.retained).setAttributeGain(gain);
         ((AuralAttributesRetained)this.retained).setRolloff(rolloff);
         ((AuralAttributesRetained)this.retained).setReflectionCoefficient(
diff --git a/src/classes/share/javax/media/j3d/AuralAttributesRetained.java b/src/classes/share/javax/media/j3d/AuralAttributesRetained.java
index 3b23c51..eb88987 100644
--- a/src/classes/share/javax/media/j3d/AuralAttributesRetained.java
+++ b/src/classes/share/javax/media/j3d/AuralAttributesRetained.java
@@ -642,7 +642,7 @@ class AuralAttributesRetained extends NodeComponentRetained {
         else
             if (debugFlag)
                 debugPrint("reset aa; aa.frequencyCutoff = null");
-	// TODO: (Enhancement) Why are these dirtyFlag cleared rather than aa->this
+	// XXXX: (Enhancement) Why are these dirtyFlag cleared rather than aa->this
         this.aaDirty = false;
 	aa.aaDirty = false;
     }
diff --git a/src/classes/share/javax/media/j3d/Background.java b/src/classes/share/javax/media/j3d/Background.java
index f50a9b3..c9a2bdd 100644
--- a/src/classes/share/javax/media/j3d/Background.java
+++ b/src/classes/share/javax/media/j3d/Background.java
@@ -228,6 +228,16 @@ public class Background extends Leaf {
      */
     public static final int SCALE_NONE_CENTER = 5; 
     
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_APPLICATION_BOUNDS_READ,
+        ALLOW_COLOR_READ,
+        ALLOW_GEOMETRY_READ,
+        ALLOW_IMAGE_READ,
+        ALLOW_IMAGE_SCALE_MODE_READ
+    };
+    
+    
     /**
      * Constructs a Background node with default parameters.  The default
      * values are as follows:
@@ -242,6 +252,8 @@ public class Background extends Leaf {
      */
     public Background () {
 	// Just use the defaults
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -250,7 +262,10 @@ public class Background extends Leaf {
      * objects in the scene.
      */
     public Background(Color3f color) {
-	((BackgroundRetained)this.retained).setColor(color);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((BackgroundRetained)this.retained).setColor(color);
     }
 
     /**
@@ -259,7 +274,10 @@ public class Background extends Leaf {
      * objects in the scene.
      */
     public Background(float r, float g, float b) {
-	((BackgroundRetained)this.retained).setColor(r, g, b);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((BackgroundRetained)this.retained).setColor(r, g, b);
     }
 
     /**
@@ -273,7 +291,10 @@ public class Background extends Leaf {
      * @param image pixel array object used as the background image
      */
     public Background(ImageComponent2D image) {
-	((BackgroundRetained)this.retained).setImage(image);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((BackgroundRetained)this.retained).setImage(image);
     }
 
     /**
@@ -290,7 +311,10 @@ public class Background extends Leaf {
      * contains an illegal node.
      */
     public Background(BranchGroup branch) {
-	((BackgroundRetained)this.retained).setGeometry(branch);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((BackgroundRetained)this.retained).setGeometry(branch);
     }
 
     /**
diff --git a/src/classes/share/javax/media/j3d/BehaviorStructure.java b/src/classes/share/javax/media/j3d/BehaviorStructure.java
index a8e9389..24a56c2 100644
--- a/src/classes/share/javax/media/j3d/BehaviorStructure.java
+++ b/src/classes/share/javax/media/j3d/BehaviorStructure.java
@@ -667,7 +667,7 @@ class BehaviorStructure extends J3dStructure {
 
 		if (awtCond.AwtId != 0) {
 		    if (awtCond.AwtId == id) {
-			// TODO: how do we clone this event (do we need to?)
+			// XXXX: how do we clone this event (do we need to?)
 			// Bug: 4181321
 			awtCond.addAWTEvent(evt);
 		    }
diff --git a/src/classes/share/javax/media/j3d/BoundingBox.java b/src/classes/share/javax/media/j3d/BoundingBox.java
index 7e4e88a..3bd5e15 100644
--- a/src/classes/share/javax/media/j3d/BoundingBox.java
+++ b/src/classes/share/javax/media/j3d/BoundingBox.java
@@ -215,7 +215,7 @@ public class BoundingBox extends Bounds {
 	   else if(bounds[i].boundId == BOUNDING_POLYTOPE) {
 	       BoundingPolytope polytope = (BoundingPolytope)bounds[i];
 
-	       for(i=0;i<polytope.nVerts;i++) { // TODO handle polytope with no verts
+	       for(i=0;i<polytope.nVerts;i++) { // XXXX: handle polytope with no verts
 		   if( polytope.verts[i].x < lower.x )
 		       lower.x = polytope.verts[i].x;
 		   if( polytope.verts[i].y < lower.y )
diff --git a/src/classes/share/javax/media/j3d/BoundingLeaf.java b/src/classes/share/javax/media/j3d/BoundingLeaf.java
index 9015202..80c35a1 100644
--- a/src/classes/share/javax/media/j3d/BoundingLeaf.java
+++ b/src/classes/share/javax/media/j3d/BoundingLeaf.java
@@ -50,10 +50,18 @@ public class BoundingLeaf extends Leaf {
     public static final int
     ALLOW_REGION_WRITE = CapabilityBits.BOUNDING_LEAF_ALLOW_REGION_WRITE;
 
-    /**
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_REGION_READ
+    };
+    
+        /**
      * Constructs a BoundingLeaf node with a null (empty) bounding region.
      */
     public BoundingLeaf() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+        
 	((BoundingLeafRetained)this.retained).createBoundingLeaf();
     }
 
@@ -62,6 +70,9 @@ public class BoundingLeaf extends Leaf {
      * @param region the bounding region of this leaf node
      */
     public BoundingLeaf(Bounds region) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+        
 	((BoundingLeafRetained)this.retained).createBoundingLeaf();
 	((BoundingLeafRetained)this.retained).initRegion(region);
     }
diff --git a/src/classes/share/javax/media/j3d/BoundingPolytope.java b/src/classes/share/javax/media/j3d/BoundingPolytope.java
index 491f85a..fbed7f3 100644
--- a/src/classes/share/javax/media/j3d/BoundingPolytope.java
+++ b/src/classes/share/javax/media/j3d/BoundingPolytope.java
@@ -76,7 +76,7 @@ public class BoundingPolytope extends Bounds {
 					   planes[i].z*invMag, planes[i].w*invMag );
 	    
 	}
-	computeAllVerts();  // TODO lazy evaluate
+	computeAllVerts();  // XXXX: lazy evaluate
     }
 
     /**
@@ -111,7 +111,7 @@ public class BoundingPolytope extends Bounds {
 	mag[4] = 1.0;
 	mag[5] = 1.0;
 
-	computeAllVerts(); // TODO lazy evaluate
+	computeAllVerts(); // XXXX: lazy evaluate
     }
 
 
@@ -131,7 +131,7 @@ public class BoundingPolytope extends Bounds {
 	    boundsIsEmpty = true;
 	    boundsIsInfinite = false;
 	    initEmptyPolytope();
-	    computeAllVerts(); // TODO lazy evaluate
+	    computeAllVerts(); // XXXX: lazy evaluate
 	    return;
 	}
        
@@ -156,7 +156,7 @@ public class BoundingPolytope extends Bounds {
 	    mag[3] = 1.0;
 	    mag[4] = 1.0;
 	    mag[5] = 1.0;
-	    computeAllVerts(); // TODO lazy evaluate
+	    computeAllVerts(); // XXXX: lazy evaluate
 	   
 	} else if( boundsObject.boundId == BOUNDING_BOX ){
 	    BoundingBox box = (BoundingBox)boundsObject;
@@ -176,7 +176,7 @@ public class BoundingPolytope extends Bounds {
 	    mag[3] = 1.0;
 	    mag[4] = 1.0;
 	    mag[5] = 1.0;
-	    computeAllVerts(); // TODO lazy evaluate
+	    computeAllVerts(); // XXXX: lazy evaluate
 	   
 	} else if( boundsObject.boundId == BOUNDING_POLYTOPE ) {
 	    BoundingPolytope polytope = (BoundingPolytope)boundsObject;
@@ -215,7 +215,7 @@ public class BoundingPolytope extends Bounds {
 	    boundsIsEmpty = true;
 	    boundsIsInfinite = false;
 	    initEmptyPolytope();
-	    computeAllVerts(); // TODO lazy evaluate
+	    computeAllVerts(); // XXXX: lazy evaluate
 	    return;
 	}
 	// find first non empty bounds object
@@ -227,7 +227,7 @@ public class BoundingPolytope extends Bounds {
 	    boundsIsEmpty = true;
 	    boundsIsInfinite = false;
 	    initEmptyPolytope();
-	    computeAllVerts(); // TODO lazy evaluate
+	    computeAllVerts(); // XXXX: lazy evaluate
 	    return; 
 	}
        
@@ -253,7 +253,7 @@ public class BoundingPolytope extends Bounds {
 	    mag[4] = 1.0;
 	    mag[5] = 1.0;
 
-	    computeAllVerts(); // TODO lazy evaluate
+	    computeAllVerts(); // XXXX: lazy evaluate
 	} else if( boundsObjects[i].boundId == BOUNDING_BOX ){
 	    BoundingBox box = (BoundingBox)boundsObjects[i];
 	    planes = new Vector4d[6];
@@ -273,7 +273,7 @@ public class BoundingPolytope extends Bounds {
 	    mag[4] = 1.0;
 	    mag[5] = 1.0;
 
-	    computeAllVerts(); // TODO lazy evaluate
+	    computeAllVerts(); // XXXX: lazy evaluate
 	} else if( boundsObjects[i].boundId == BOUNDING_POLYTOPE ) {
 	    BoundingPolytope polytope = (BoundingPolytope)boundsObjects[i];
 	    planes = new Vector4d[polytope.planes.length];
@@ -320,7 +320,7 @@ public class BoundingPolytope extends Bounds {
 	    if( planes.length <= 0 ) {
 		boundsIsEmpty = true;
 		boundsIsInfinite = false;
-		computeAllVerts(); // TODO lazy evaluate
+		computeAllVerts(); // XXXX: lazy evaluate
 		return;
 	    }
 
@@ -332,7 +332,7 @@ public class BoundingPolytope extends Bounds {
 		this.planes[i] = new Vector4d( planes[i].x*invMag, planes[i].y*invMag,
 					       planes[i].z*invMag, planes[i].w*invMag );
 	    } 
-	    computeAllVerts();  // TODO lazy evaluate
+	    computeAllVerts();  // XXXX: lazy evaluate
 
 	}
     
@@ -373,7 +373,7 @@ public class BoundingPolytope extends Bounds {
 	if( boundsObject == null )  {
 	    boundsIsEmpty = true;
 	    boundsIsInfinite = false;
-	    computeAllVerts(); // TODO lazy evaluate
+	    computeAllVerts(); // XXXX: lazy evaluate
 	      
 	}else if( boundsObject.boundId == BOUNDING_SPHERE ) {
 	    BoundingSphere sphere = (BoundingSphere)boundsObject;
@@ -391,7 +391,7 @@ public class BoundingPolytope extends Bounds {
 
 	    boundsIsEmpty = boundsObject.boundsIsEmpty;
 	    boundsIsInfinite = boundsObject.boundsIsInfinite;
-	    computeAllVerts(); // TODO lazy evaluate
+	    computeAllVerts(); // XXXX: lazy evaluate
 
 	} else if( boundsObject.boundId == BOUNDING_BOX){
 	    BoundingBox box = (BoundingBox)boundsObject;
@@ -422,7 +422,7 @@ public class BoundingPolytope extends Bounds {
 	    
 	    boundsIsEmpty = boundsObject.boundsIsEmpty;
 	    boundsIsInfinite = boundsObject.boundsIsInfinite;
-	    computeAllVerts(); // TODO lazy evaluate
+	    computeAllVerts(); // XXXX: lazy evaluate
 
 	} else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
 	    BoundingPolytope polytope = (BoundingPolytope)boundsObject;
@@ -1627,7 +1627,7 @@ public class BoundingPolytope extends Bounds {
 		}
 	    }
 	}
-	// TODO correctly compute centroid
+	// XXXX: correctly compute centroid
 	
 	x=y=z=0.0; 
 	Point3d newVerts[] = new Point3d[nVerts];
diff --git a/src/classes/share/javax/media/j3d/BoundingSphere.java b/src/classes/share/javax/media/j3d/BoundingSphere.java
index 79dd8fb..67b9d5c 100644
--- a/src/classes/share/javax/media/j3d/BoundingSphere.java
+++ b/src/classes/share/javax/media/j3d/BoundingSphere.java
@@ -1073,7 +1073,7 @@ public class BoundingSphere extends Bounds {
 	Point3d dir = new Point3d();  // normalized direction of ray
 	Point3d oc  = new Point3d();  // vector from sphere center to ray origin
 
-	oc.x = center.x - origin.x;   // TODO check if this method is still needed
+	oc.x = center.x - origin.x;   // XXXX: check if this method is still needed
 	oc.y = center.y - origin.y;
 	oc.z = center.z - origin.z;
 
diff --git a/src/classes/share/javax/media/j3d/BranchGroup.java b/src/classes/share/javax/media/j3d/BranchGroup.java
index 92004fd..1c58feb 100644
--- a/src/classes/share/javax/media/j3d/BranchGroup.java
+++ b/src/classes/share/javax/media/j3d/BranchGroup.java
@@ -86,22 +86,63 @@ public class BranchGroup extends Group {
    * Detaches this BranchGroup from its parent.
    */
     public void detach() {
-       Group parent;
-
-       if (isLiveOrCompiled()) {
-           if(!this.getCapability(ALLOW_DETACH))
-               throw new CapabilityNotSetException(J3dI18N.getString("BranchGroup1"));
-
-           if (((BranchGroupRetained)this.retained).parent != null) {
-	       parent = (Group)((BranchGroupRetained)this.retained).parent.source;
-               if(!parent.getCapability(Group.ALLOW_CHILDREN_WRITE))
-                   throw new CapabilityNotSetException(J3dI18N.getString("BranchGroup2"));
-	   }
-       }
+	Group parent;
+	
+	if (isLiveOrCompiled()) {
+	    if(!this.getCapability(ALLOW_DETACH))
+		throw new CapabilityNotSetException(J3dI18N.getString("BranchGroup1"));
+	    
+	    if (((BranchGroupRetained)this.retained).parent != null) {
+		parent = (Group)((BranchGroupRetained)this.retained).parent.source;
+		if(!parent.getCapability(Group.ALLOW_CHILDREN_WRITE))
+		    throw new CapabilityNotSetException(J3dI18N.getString("BranchGroup2"));
+	    }
+	}
+	
+	((BranchGroupRetained)this.retained).detach();
+    }
+    
+    
+    void validateModeFlagAndPickShape(int mode, int flags, PickShape pickShape) {
 
-      ((BranchGroupRetained)this.retained).detach();
-  }
+        if(isLive()==false) {
+	    throw new IllegalStateException(J3dI18N.getString("BranchGroup3"));
+        }
+        
+        if((mode != PickInfo.PICK_BOUNDS) && (mode != PickInfo.PICK_GEOMETRY)) {
+          
+          throw new IllegalArgumentException(J3dI18N.getString("BranchGroup4"));
+        }
+        
+        if((pickShape instanceof PickPoint) && (mode == PickInfo.PICK_GEOMETRY)) {
+          throw new IllegalArgumentException(J3dI18N.getString("BranchGroup5"));
+        }
+        
+        if(((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) &&
+                ((flags & PickInfo.ALL_GEOM_INFO) != 0)) {
+            throw new IllegalArgumentException(J3dI18N.getString("BranchGroup6"));
+        }
+        
+        if((mode == PickInfo.PICK_BOUNDS) && 
+                (((flags & (PickInfo.CLOSEST_GEOM_INFO | 
+                            PickInfo.ALL_GEOM_INFO |
+                            PickInfo.CLOSEST_DISTANCE |
+                            PickInfo.CLOSEST_INTERSECTION_POINT)) != 0))) {
+          
+          throw new IllegalArgumentException(J3dI18N.getString("BranchGroup7"));
+        }
+  
+        if((pickShape instanceof PickBounds) && 
+                (((flags & (PickInfo.CLOSEST_GEOM_INFO | 
+                            PickInfo.ALL_GEOM_INFO |
+                            PickInfo.CLOSEST_DISTANCE |
+                            PickInfo.CLOSEST_INTERSECTION_POINT)) != 0))) {
+          
+          throw new IllegalArgumentException(J3dI18N.getString("BranchGroup8"));
+        }        
 
+    }    
+    
   /**
    * Returns an array referencing all the items that are pickable below this
    * <code>BranchGroup</code> that intersect with PickShape.
@@ -116,12 +157,91 @@ public class BranchGroup extends Group {
    *
    */
     public SceneGraphPath[] pickAll( PickShape pickShape ) {
+
         if(isLive()==false)
 	    throw new IllegalStateException(J3dI18N.getString("BranchGroup3"));
+
+        return ((BranchGroupRetained)this.retained).pickAll(pickShape);           
+        
+ }
     
-	return Picking.pickAll( this, pickShape );
+    /**
+     * Returns an array unsorted references to all the PickInfo objects that are 
+     * pickable  below this <code>BranchGroup</code> that intersect with PickShape.
+     * The accuracy of the pick is set by the pick mode. The mode include : 
+     * PickInfo.PICK_BOUNDS and PickInfo.PICK_GEOMETRY. The amount of information returned 
+     * is specified via a masked variable, flags, indicating which components are 
+     * present in each returned PickInfo object. 
+     *
+     * @param mode  picking mode, one of <code>PickInfo.PICK_BOUNDS</code> or <code>PickInfo.PICK_GEOMETRY</code>.
+     *
+     * @param flags a mask indicating which components are present in each PickInfo object.  
+     * This is specified as one or more individual bits that are bitwise "OR"ed together to 
+     * describe the PickInfo data. The flags include :
+     * <ul>
+     * <code>PickInfo.SCENEGRAPHPATH</code> - request for computed SceneGraphPath.<br>    
+     * <code>PickInfo.NODE</code> - request for computed intersected Node.<br>
+     * <code>PickInfo.LOCAL_TO_VWORLD</code> - request for computed local to virtual world transform.<br>
+     * <code>PickInfo.CLOSEST_INTERSECTION_POINT</code> - request for closest intersection point.<br>
+     * <code>PickInfo.CLOSEST_DISTANCE</code> - request for the distance of closest intersection.<br>
+     * <code>PickInfo.CLOSEST_GEOM_INFO</code> - request for only the closest intersection geometry information.<br>
+     * <code>PickInfo.ALL_GEOM_INFO</code> - request for all intersection geometry information.<br>
+     * </ul>
+     *
+     * @param pickShape the description of this picking volume or area.
+     *
+     * @exception IllegalArgumentException if flags contains both CLOSEST_GEOM_INFO and 
+     * ALL_GEOM_INFO.
+     *
+     * @exception IllegalArgumentException if pickShape is a PickPoint and pick mode
+     * is set to PICK_GEOMETRY.
+     *
+     * @exception IllegalArgumentException if pick mode is neither PICK_BOUNDS 
+     * nor PICK_GEOMETRY.
+     *
+     * @exception IllegalArgumentException if pick mode is PICK_BOUNDS 
+     * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE,
+     * CLOSEST_GEOM_INFO or ALL_GEOM_INFO.
+     *
+     * @exception IllegalArgumentException if pickShape is PickBounds 
+     * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE,
+     * CLOSEST_GEOM_INFO or ALL_GEOM_INFO.
+     *
+     * @exception IllegalStateException if BranchGroup is not live.
+     *
+     * @exception CapabilityNotSetException if the mode is
+     * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit
+     * is not set in any Geometry objects referred to by any shape
+     * node whose bounds intersects the PickShape.
+     *   
+     * @exception CapabilityNotSetException if flags contains any of
+     * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO
+     * or ALL_GEOM_INFO, and the capability bits that control reading of
+     * coordinate data are not set in any GeometryArray object referred
+     * to by any shape node that intersects the PickShape.
+     * The capability bits that must be set to avoid this exception are as follows :
+     * <ul> 
+     * <li>By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ</li>
+     * <li>By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ</li>
+     * <li>Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ
+     * (in addition to one of the above)</li>
+     * </ul>
+     *
+     * @see Locale#pickAll(int,int,javax.media.j3d.PickShape)
+     * @see PickInfo
+     * 
+     * @since Java 3D 1.4
+     *
+     */
+
+    public PickInfo[] pickAll( int mode, int flags, PickShape pickShape ) {
+
+        validateModeFlagAndPickShape(mode, flags, pickShape);      
+        return ((BranchGroupRetained)this.retained).pickAll(mode, flags, pickShape);           
+
     }
 
+
   /**
    * Returns a sorted array of references to all the Pickable items that 
    * intersect with the pickShape. Element [0] references the item closest 
@@ -139,10 +259,90 @@ public class BranchGroup extends Group {
    *  
    */
     public SceneGraphPath[] pickAllSorted( PickShape pickShape ) {
+
         if(isLive()==false)
-	    throw new IllegalStateException(J3dI18N.getString("BranchGroup3"));
-    
-	return Picking.pickAllSorted( this, pickShape );
+	    throw new IllegalStateException(J3dI18N.getString("BranchGroup3"));    
+
+        return ((BranchGroupRetained)this.retained).pickAllSorted(pickShape);           
+
+    }
+
+
+    /**
+     * Returns a sorted array of PickInfo references to all the pickable
+     * items that intersect with the pickShape. Element [0] references 
+     * the item closest to <i>origin</i> of PickShape successive array
+     * elements are further from the <i>origin</i>
+     * The accuracy of the pick is set by the pick mode. The mode include : 
+     * PickInfo.PICK_BOUNDS and PickInfo.PICK_GEOMETRY. The amount of information returned 
+     * is specified via a masked variable, flags, indicating which components are 
+     * present in each returned PickInfo object. 
+     *
+     * @param mode  picking mode, one of <code>PickInfo.PICK_BOUNDS</code> or <code>PickInfo.PICK_GEOMETRY</code>.
+     *
+     * @param flags a mask indicating which components are present in each PickInfo object.  
+     * This is specified as one or more individual bits that are bitwise "OR"ed together to 
+     * describe the PickInfo data. The flags include :
+     * <ul>
+     * <code>PickInfo.SCENEGRAPHPATH</code> - request for computed SceneGraphPath.<br>    
+     * <code>PickInfo.NODE</code> - request for computed intersected Node.<br>
+     * <code>PickInfo.LOCAL_TO_VWORLD</code> - request for computed local to virtual world transform.<br>
+     * <code>PickInfo.CLOSEST_INTERSECTION_POINT</code> - request for closest intersection point.<br>
+     * <code>PickInfo.CLOSEST_DISTANCE</code> - request for the distance of closest intersection.<br>
+     * <code>PickInfo.CLOSEST_GEOM_INFO</code> - request for only the closest intersection geometry information.<br>
+     * <code>PickInfo.ALL_GEOM_INFO</code> - request for all intersection geometry information.<br>
+     * </ul>
+     *
+     * @param pickShape the description of this picking volume or area.
+     *
+     * @exception IllegalArgumentException if flags contains both CLOSEST_GEOM_INFO and 
+     * ALL_GEOM_INFO.
+     *
+     * @exception IllegalArgumentException if pickShape is a PickPoint and pick mode
+     * is set to PICK_GEOMETRY.
+     *
+     * @exception IllegalArgumentException if pick mode is neither PICK_BOUNDS 
+     * nor PICK_GEOMETRY.
+     *
+     * @exception IllegalArgumentException if pick mode is PICK_BOUNDS 
+     * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE,
+     * CLOSEST_GEOM_INFO or ALL_GEOM_INFO.
+     *
+     * @exception IllegalArgumentException if pickShape is PickBounds 
+     * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE,
+     * CLOSEST_GEOM_INFO or ALL_GEOM_INFO.
+     *
+     * @exception IllegalStateException if BranchGroup is not live.
+     *
+     * @exception CapabilityNotSetException if the mode is
+     * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit
+     * is not set in any Geometry objects referred to by any shape
+     * node whose bounds intersects the PickShape.
+     *   
+     * @exception CapabilityNotSetException if flags contains any of
+     * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO
+     * or ALL_GEOM_INFO, and the capability bits that control reading of
+     * coordinate data are not set in any GeometryArray object referred
+     * to by any shape node that intersects the PickShape.
+     * The capability bits that must be set to avoid this exception are as follows :
+     * <ul> 
+     * <li>By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ</li>
+     * <li>By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ</li>
+     * <li>Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ
+     * (in addition to one of the above)</li>
+     * </ul>
+     *
+     * @see Locale#pickAllSorted(int,int,javax.media.j3d.PickShape)
+     * @see PickInfo
+     * 
+     * @since Java 3D 1.4
+     *
+     */
+    public PickInfo[] pickAllSorted( int mode, int flags, PickShape pickShape ) {
+
+        validateModeFlagAndPickShape(mode, flags, pickShape);
+        return ((BranchGroupRetained)this.retained).pickAllSorted(mode, flags, pickShape);
+
     }
 
   /**
@@ -160,17 +360,95 @@ public class BranchGroup extends Group {
    *  
    */
     public SceneGraphPath pickClosest( PickShape pickShape ) {
+
         if(isLive()==false)
 	    throw new IllegalStateException(J3dI18N.getString("BranchGroup3"));
 
-	return Picking.pickClosest( this, pickShape );
+        return ((BranchGroupRetained)this.retained).pickClosest(pickShape);           
+
     }
 
+    /**
+     * Returns a PickInfo which references the pickable item
+     * which is closest to the origin of <code>pickShape</code>.
+     * The accuracy of the pick is set by the pick mode. The mode include : 
+     * PickInfo.PICK_BOUNDS and PickInfo.PICK_GEOMETRY. The amount of information returned 
+     * is specified via a masked variable, flags, indicating which components are 
+     * present in each returned PickInfo object. 
+     *
+     * @param mode  picking mode, one of <code>PickInfo.PICK_BOUNDS</code> or <code>PickInfo.PICK_GEOMETRY</code>.
+     *
+     * @param flags a mask indicating which components are present in each PickInfo object.  
+     * This is specified as one or more individual bits that are bitwise "OR"ed together to 
+     * describe the PickInfo data. The flags include :
+     * <ul>
+     * <code>PickInfo.SCENEGRAPHPATH</code> - request for computed SceneGraphPath.<br>    
+     * <code>PickInfo.NODE</code> - request for computed intersected Node.<br>
+     * <code>PickInfo.LOCAL_TO_VWORLD</code> - request for computed local to virtual world transform.<br>
+     * <code>PickInfo.CLOSEST_INTERSECTION_POINT</code> - request for closest intersection point.<br>
+     * <code>PickInfo.CLOSEST_DISTANCE</code> - request for the distance of closest intersection.<br>
+     * <code>PickInfo.CLOSEST_GEOM_INFO</code> - request for only the closest intersection geometry information.<br>
+     * <code>PickInfo.ALL_GEOM_INFO</code> - request for all intersection geometry information.<br>
+     * </ul>
+     *
+     * @param pickShape the description of this picking volume or area.
+     *
+     * @exception IllegalArgumentException if flags contains both CLOSEST_GEOM_INFO and 
+     * ALL_GEOM_INFO.
+     *
+     * @exception IllegalArgumentException if pickShape is a PickPoint and pick mode
+     * is set to PICK_GEOMETRY.
+     *
+     * @exception IllegalArgumentException if pick mode is neither PICK_BOUNDS 
+     * nor PICK_GEOMETRY.
+     *
+     * @exception IllegalArgumentException if pick mode is PICK_BOUNDS 
+     * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE,
+     * CLOSEST_GEOM_INFO or ALL_GEOM_INFO.
+     *
+     * @exception IllegalArgumentException if pickShape is PickBounds 
+     * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE,
+     * CLOSEST_GEOM_INFO or ALL_GEOM_INFO.
+     *
+     * @exception IllegalStateException if BranchGroup is not live.
+     *
+     * @exception CapabilityNotSetException if the mode is
+     * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit
+     * is not set in any Geometry objects referred to by any shape
+     * node whose bounds intersects the PickShape.
+     *   
+     * @exception CapabilityNotSetException if flags contains any of
+     * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO
+     * or ALL_GEOM_INFO, and the capability bits that control reading of
+     * coordinate data are not set in any GeometryArray object referred
+     * to by any shape node that intersects the PickShape.
+     * The capability bits that must be set to avoid this exception are as follows :
+     * <ul> 
+     * <li>By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ</li>
+     * <li>By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ</li>
+     * <li>Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ
+     * (in addition to one of the above)</li>
+     * </ul>
+     *
+     * @see Locale#pickClosest(int,int,javax.media.j3d.PickShape)
+     * @see PickInfo
+     * 
+     * @since Java 3D 1.4
+     *
+     */
+    public PickInfo pickClosest( int mode, int flags, PickShape pickShape ) {
+
+        validateModeFlagAndPickShape(mode, flags, pickShape);
+        return ((BranchGroupRetained)this.retained).pickClosest(mode, flags, pickShape);
+
+    }
+
+
   /**
    * Returns a reference to any item that is Pickable below this BranchGroup that
    * intersects with <code>pickShape</code>.
-   *
    * @param pickShape the PickShape object
+   *
    * @see SceneGraphPath
    * @see Locale#pickAny
    * @see PickShape
@@ -178,12 +456,88 @@ public class BranchGroup extends Group {
    *  
    */
     public SceneGraphPath pickAny( PickShape pickShape ) {
+
         if(isLive()==false)
 	    throw new IllegalStateException(J3dI18N.getString("BranchGroup3"));
+
+        return ((BranchGroupRetained)this.retained).pickAny(pickShape);           
     
-	return Picking.pickAny( this, pickShape );
     }
 
+    /**
+     * Returns a PickInfo which references the pickable item  below this
+     * BranchGroup that intersects with <code>pickShape</code>.
+     * The accuracy of the pick is set by the pick mode. The mode include : 
+     * PickInfo.PICK_BOUNDS and PickInfo.PICK_GEOMETRY. The amount of information returned 
+     * is specified via a masked variable, flags, indicating which components are 
+     * present in each returned PickInfo object. 
+     *
+     * @param mode  picking mode, one of <code>PickInfo.PICK_BOUNDS</code> or <code>PickInfo.PICK_GEOMETRY</code>.
+     *
+     * @param flags a mask indicating which components are present in each PickInfo object.  
+     * This is specified as one or more individual bits that are bitwise "OR"ed together to 
+     * describe the PickInfo data. The flags include :
+     * <ul>
+     * <code>PickInfo.SCENEGRAPHPATH</code> - request for computed SceneGraphPath.<br>    
+     * <code>PickInfo.NODE</code> - request for computed intersected Node.<br>
+     * <code>PickInfo.LOCAL_TO_VWORLD</code> - request for computed local to virtual world transform.<br>
+     * <code>PickInfo.CLOSEST_INTERSECTION_POINT</code> - request for closest intersection point.<br>
+     * <code>PickInfo.CLOSEST_DISTANCE</code> - request for the distance of closest intersection.<br>
+     * <code>PickInfo.CLOSEST_GEOM_INFO</code> - request for only the closest intersection geometry information.<br>
+     * <code>PickInfo.ALL_GEOM_INFO</code> - request for all intersection geometry information.<br>
+     * </ul>
+     *
+     * @param pickShape the description of this picking volume or area.
+     *
+     * @exception IllegalArgumentException if flags contains both CLOSEST_GEOM_INFO and 
+     * ALL_GEOM_INFO.
+     *
+     * @exception IllegalArgumentException if pickShape is a PickPoint and pick mode
+     * is set to PICK_GEOMETRY.
+     *
+     * @exception IllegalArgumentException if pick mode is neither PICK_BOUNDS 
+     * nor PICK_GEOMETRY.
+     *
+     * @exception IllegalArgumentException if pick mode is PICK_BOUNDS 
+     * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE,
+     * CLOSEST_GEOM_INFO or ALL_GEOM_INFO.
+     *
+     * @exception IllegalArgumentException if pickShape is PickBounds 
+     * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE,
+     * CLOSEST_GEOM_INFO or ALL_GEOM_INFO.
+     *
+     * @exception IllegalStateException if BranchGroup is not live.
+     *
+     * @exception CapabilityNotSetException if the mode is
+     * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit
+     * is not set in any Geometry objects referred to by any shape
+     * node whose bounds intersects the PickShape.
+     *   
+     * @exception CapabilityNotSetException if flags contains any of
+     * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO
+     * or ALL_GEOM_INFO, and the capability bits that control reading of
+     * coordinate data are not set in any GeometryArray object referred
+     * to by any shape node that intersects the PickShape.
+     * The capability bits that must be set to avoid this exception are as follows :
+     * <ul> 
+     * <li>By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ</li>
+     * <li>By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ</li>
+     * <li>Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ
+     * (in addition to one of the above)</li>
+     * </ul>
+     *
+     * @see Locale#pickAny(int,int,javax.media.j3d.PickShape)
+     * @see PickInfo
+     * 
+     * @since Java 3D 1.4
+     *
+     */
+    public PickInfo pickAny( int mode, int flags, PickShape pickShape ) {
+
+        validateModeFlagAndPickShape(mode, flags, pickShape);
+        return ((BranchGroupRetained)this.retained).pickAny(mode, flags, pickShape);
+
+    }
 
    /**
     * Creates a new instance of the node.  This routine is called
@@ -200,8 +554,11 @@ public class BranchGroup extends Group {
     * @see NodeComponent#setDuplicateOnCloneTree
     */
     public Node cloneNode(boolean forceDuplicate) {
+
         BranchGroup bg = new BranchGroup();
 	bg.duplicateNode(this, forceDuplicate);
 	return bg;
+
     }
+
 }
diff --git a/src/classes/share/javax/media/j3d/BranchGroupRetained.java b/src/classes/share/javax/media/j3d/BranchGroupRetained.java
index 5d1aa84..696ba49 100644
--- a/src/classes/share/javax/media/j3d/BranchGroupRetained.java
+++ b/src/classes/share/javax/media/j3d/BranchGroupRetained.java
@@ -58,11 +58,18 @@ class BranchGroupRetained extends GroupRetained {
 	if (universe != null) {
 	    universe.resetWaitMCFlag();
 	    synchronized (universe.sceneGraphLock) {
-		if (source.isLive()) {
+                boolean isLive = source.isLive();
+		if (isLive) {
 	            notifySceneGraphChanged(true);
 		}
+                GroupRetained oldParent = (GroupRetained)parent;
 	        do_detach();
 		universe.setLiveState.clear();
+                if (isLive)
+                    if (oldParent==null)
+                        universe.notifyStructureChangeListeners(false,locale,(BranchGroup)this.source);
+                    else
+                        universe.notifyStructureChangeListeners(false,oldParent.source, (BranchGroup)this.source);
 	    }
 	    universe.waitForMC();
 	} else { // Not live yet, just do it.
@@ -106,7 +113,7 @@ class BranchGroupRetained extends GroupRetained {
                     setAuxData(s, j, hkIndex);
 
                 } else {
-		    // TODO: change this to an assertion exception
+		    // XXXX: change this to an assertion exception
                     System.out.println("Can't Find matching hashKey in setNodeData.");
                     System.out.println("We're in TROUBLE!!!");
                 }
@@ -206,4 +213,145 @@ class BranchGroupRetained extends GroupRetained {
 	// without any capabilities set
 	mergeFlag = SceneGraphObjectRetained.DONT_MERGE;
     }
+    
+    SceneGraphPath[] pickAll(PickShape pickShape) {
+        
+        PickInfo[] pickInfoArr = pickAll(PickInfo.PICK_BOUNDS,
+					 PickInfo.SCENEGRAPHPATH, pickShape);
+	
+	if(pickInfoArr == null) {
+            return null;
+	}
+
+        SceneGraphPath[] sgpArr = new SceneGraphPath[pickInfoArr.length];
+        for( int i=0; i<sgpArr.length; i++) {
+            sgpArr[i] = pickInfoArr[i].getSceneGraphPath();
+        }
+
+        return sgpArr;              
+    }
+    
+    PickInfo[] pickAll( int mode, int flags, PickShape pickShape ) {
+        
+	if (inSharedGroup) {
+	    throw new RestrictedAccessException(J3dI18N.getString("BranchGroup9"));
+	}
+        
+	GeometryAtom geomAtoms[] =
+	    locale.universe.geometryStructure.pickAll(locale, pickShape);
+
+        return PickInfo.pick(this, geomAtoms, mode, flags, pickShape, PickInfo.PICK_ALL);
+
+    }
+    
+    SceneGraphPath[] pickAllSorted(PickShape pickShape) {
+        
+        PickInfo[] pickInfoArr = pickAllSorted(PickInfo.PICK_BOUNDS,
+					       PickInfo.SCENEGRAPHPATH, pickShape);
+	
+	if(pickInfoArr == null) {
+            return null;
+	}
+
+        SceneGraphPath[] sgpArr = new SceneGraphPath[pickInfoArr.length];
+        for( int i=0; i<sgpArr.length; i++) {
+            sgpArr[i] = pickInfoArr[i].getSceneGraphPath();
+        }
+	
+        return sgpArr;
+
+    }
+    
+    PickInfo[] pickAllSorted( int mode, int flags, PickShape pickShape ) {
+        
+	if (inSharedGroup) {
+	    throw new RestrictedAccessException(J3dI18N.getString("BranchGroup9"));
+	}
+        
+	GeometryAtom geomAtoms[] =
+	    locale.universe.geometryStructure.pickAll(locale, pickShape);
+
+        PickInfo[] pickInfoArr  = null;
+        
+	
+        if ((geomAtoms == null) || (geomAtoms.length == 0)) {
+            return null;
+        }
+
+	if (mode == PickInfo.PICK_GEOMETRY) {
+            // Need to have closestDistance set
+            flags |= PickInfo.CLOSEST_DISTANCE;
+            pickInfoArr= PickInfo.pick(this, geomAtoms, mode, flags, 
+				       pickShape, PickInfo.PICK_ALL);
+            if (pickInfoArr != null) {
+                PickInfo.sortPickInfoArray(pickInfoArr);
+            }
+        }
+        else {
+            PickInfo.sortGeomAtoms(geomAtoms, pickShape);
+            pickInfoArr= PickInfo.pick(this, geomAtoms, mode, flags, 
+				       pickShape, PickInfo.PICK_ALL);          
+        }
+        
+        return pickInfoArr;
+        
+    }
+
+    SceneGraphPath pickClosest( PickShape pickShape ) {
+
+        PickInfo pickInfo = pickClosest( PickInfo.PICK_BOUNDS,
+					 PickInfo.SCENEGRAPHPATH, pickShape);
+        
+        if(pickInfo == null) {
+            return null;
+        }
+
+        return pickInfo.getSceneGraphPath();
+
+    }
+    
+    PickInfo pickClosest( int mode, int flags, PickShape pickShape ) {
+
+        PickInfo[] pickInfoArr = null;
+
+        pickInfoArr = pickAllSorted( mode, flags, pickShape );
+        
+        if(pickInfoArr == null) {
+            return null;
+        }
+        
+        return pickInfoArr[0];
+        
+    }
+ 
+    SceneGraphPath pickAny( PickShape pickShape ) {
+
+        PickInfo pickInfo = pickAny( PickInfo.PICK_BOUNDS,
+				     PickInfo.SCENEGRAPHPATH, pickShape);
+        
+        if(pickInfo == null) {
+            return null;
+        }
+        return pickInfo.getSceneGraphPath();
+    }
+    
+    PickInfo pickAny( int mode, int flags, PickShape pickShape ) {
+
+	if (inSharedGroup) {
+	    throw new RestrictedAccessException(J3dI18N.getString("BranchGroup9"));
+	}
+
+	GeometryAtom geomAtoms[] =
+	    locale.universe.geometryStructure.pickAll(locale, pickShape);
+        
+        PickInfo[] pickInfoArr = PickInfo.pick(this, geomAtoms, mode, 
+					       flags, pickShape, PickInfo.PICK_ANY);
+        
+        if(pickInfoArr == null) {
+            return null;
+        }
+        
+        return pickInfoArr[0];
+        
+    }    
 }
diff --git a/src/classes/share/javax/media/j3d/Canvas3D.java b/src/classes/share/javax/media/j3d/Canvas3D.java
index 597fbd3..6d887b9 100644
--- a/src/classes/share/javax/media/j3d/Canvas3D.java
+++ b/src/classes/share/javax/media/j3d/Canvas3D.java
@@ -300,6 +300,7 @@ public class Canvas3D extends Canvas {
     static final int FOG_DIRTY                 = 0x2000;
     static final int MODELCLIP_DIRTY           = 0x4000;    
     static final int VIEW_MATRIX_DIRTY         = 0x8000;
+    // static final int SHADER_DIRTY              = 0x10000; Not ready for this yet -- JADA
 
     // Use to notify D3D Canvas when window change
     static final int RESIZE = 1;
@@ -373,6 +374,18 @@ public class Canvas3D extends Canvas {
     //
     int monoscopicViewPolicy = View.CYCLOPEAN_EYE_VIEW;
 
+    // User requested stencil size 
+    int requestedStencilSize;
+
+    // Actual stencil size return for this canvas
+    int actualStencilSize;
+
+    // True if stencil buffer is available for user
+    boolean userStencilAvailable;
+
+    // True if stencil buffer is available for system ( decal )
+    boolean systemStencilAvailable;    
+    
     //
     // Read-only flag that indicates whether double buffering is supported
     // for this canvas.  This is always false for off-screen canvases.
@@ -409,17 +422,11 @@ public class Canvas3D extends Canvas {
     //
     int textureColorTableSize;
 
-
-    boolean multiTexAccelerated = false;
-
-    // number of simultaneous Texture unit support for this canvas.
-    int numTexUnitSupported = 1;
-
-    // number of texture coords unit support for multi-texture.
-    int numTexCoordSupported = 1;
-   
     // a mapping between underlying graphics library texture unit and
     // texture unit state in j3d
+    //
+    // TODO: This mapping is now required to be 1-to-1, and it should be
+    // removed entirely in Java 3D 1.5
     int[] texUnitStateMap = null;
 
     // number of active/enabled texture unit 
@@ -428,6 +435,10 @@ public class Canvas3D extends Canvas {
     // index iof last enabled texture unit 
     int lastActiveTexUnit = -1;
 
+    // True if shadingLanguage is supported, otherwise false.
+    boolean shadingLanguageGLSL = false;
+    boolean shadingLanguageCg = false;
+
     // Query properties
     J3dQueryProps queryProps;
 
@@ -454,7 +465,10 @@ public class Canvas3D extends Canvas {
     // View cache for this canvas and its associated view.
     //
     CanvasViewCache canvasViewCache = null;
-
+    
+    // Issue 109: View cache for this canvas, for computing view frustum planes
+    CanvasViewCache canvasViewCacheFrustum = null;
+    
     // Since multiple renderAtomListInfo, share the same vecBounds
     // we want to do the intersection test only once per renderAtom
     // this flag is set to true after the first intersect and set to
@@ -488,7 +502,14 @@ public class Canvas3D extends Canvas {
 					BACKGROUND_DIRTY |
 					BACKGROUND_IMAGE_DIRTY);    
 
-    int cvDirtyMask = VIEW_INFO_DIRTY;
+    // Issue 163: Array of dirty bits is used because the Renderer and
+    // RenderBin run asynchronously. Now that they each have a separate
+    // instance of CanvasViewCache (due to the fix for Issue 109), they
+    // need separate dirty bits. Array element 0 is used for the Renderer and
+    // element 1 is used for the RenderBin.
+    static final int RENDERER_DIRTY_IDX = 0;
+    static final int RENDER_BIN_DIRTY_IDX = 1;
+    int[] cvDirtyMask = new int[2];
 
     // This boolean informs the J3DGraphics2DImpl that the window is resized
     boolean resizeGraphics2D = true;
@@ -574,7 +595,8 @@ public class Canvas3D extends Canvas {
     // PixelFormat structure ( see also gldef.h ) to allow value such
     // as offScreen's pixelformat, and ARB function pointers to be stored.
     long fbConfig = 0;  
-
+    GraphicsConfigInfo gcInfo = null;
+    
     // offScreenBufferInfo is a pointer to additional information about the
     // offScreenBuffer in this Canvas.
     //
@@ -587,6 +609,7 @@ public class Canvas3D extends Canvas {
     // doesn't exist at the time getBestConfiguration() is called, and
     // X11GraphicsConfig neither maintains this pointer nor provides a public
     // constructor to allow Java 3D to extend it.
+    // static Hashtable fbConfigInfoTable = new Hashtable();   -- Chien
     static Hashtable fbConfigTable = new Hashtable();
 
     // The native graphics version, vendor, and renderer information 
@@ -631,6 +654,7 @@ public class Canvas3D extends Canvas {
     LightBin lightBin = null;
     EnvironmentSet environmentSet = null;
     AttributeBin attributeBin = null;
+    ShaderBin shaderBin = null;
     RenderMolecule renderMolecule = null;
     PolygonAttributesRetained polygonAttributes = null;
     LineAttributesRetained lineAttributes = null;
@@ -641,7 +665,7 @@ public class Canvas3D extends Canvas {
     ColoringAttributesRetained coloringAttributes = null;
     Transform3D modelMatrix = null;
     TextureBin textureBin = null;
-
+    
 
     /**
      * cached RenderBin states for lazy native states update
@@ -652,7 +676,7 @@ public class Canvas3D extends Canvas {
     FogRetained fog = null;
     ModelClipRetained modelClip = null;
     Color3f sceneAmbient = new Color3f();
-    TextureUnitStateRetained texUnitState[] = null;
+    TextureUnitStateRetained[] texUnitState = null;
     
     /**
      * cached View states for lazy native states update
@@ -669,6 +693,8 @@ public class Canvas3D extends Canvas {
     TexCoordGenerationRetained texCoordGeneration = null;
     RenderingAttributesRetained renderingAttrs = null;
     AppearanceRetained appearance = null;
+    
+    ShaderProgramRetained  shaderProgram = null;
 
     // only used in Mixed Mode rendering
     Object appHandle = null;
@@ -678,6 +704,8 @@ public class Canvas3D extends Canvas {
      * Texture Generation linear mode. This is used for D3D
      * temporary turn displayList off and do its own coordinate
      * generation since D3D don't support it.
+     *
+     * TODO aces : is this still true in DX9?
      */
     boolean texLinearMode = false; 
 
@@ -701,7 +729,9 @@ public class Canvas3D extends Canvas {
 
     // an unique bit to identify this canvas
     int canvasBit = 0;
-
+    // an unique number to identify this canvas : ( canvasBit = 1 << canvasId)
+    int canvasId = 0;
+    
     // Avoid using this as lock, it cause deadlock 
     Object cvLock = new Object();
     Object evaluateLock = new Object();
@@ -757,10 +787,10 @@ public class Canvas3D extends Canvas {
     static final int ARB_MULTISAMPLE             = 0x200;
     static final int EXT_COMPILED_VERTEX_ARRAYS  = 0x400;
     static final int SUN_VIDEO_RESIZE            = 0x800;
-    static final int STENCIL_BUFFER              = 0x1000;
 
-    // The following three variables are set by
-    // createNewContext()/createQueryContext() callback
+    // The following 10 variables are set by the native
+    // createNewContext()/createQueryContext() methods
+
     // Supported Extensions
     int extensionsSupported = 0;
 
@@ -770,6 +800,33 @@ public class Canvas3D extends Canvas {
     // Texture Boundary Width Max
     int   textureBoundaryWidthMax = 0;
 
+    boolean multiTexAccelerated = false;
+
+    // Max number of texture coordinate sets
+    int maxTexCoordSets = 1;
+
+    // Max number of fixed-function texture units
+    int maxTextureUnits = 1;
+
+    // Max number of fragment shader texture units
+    int maxTextureImageUnits = 0;
+
+    // Max number of vertex shader texture units
+    int maxVertexTextureImageUnits = 0;
+
+    // Max number of combined shader texture units
+    int maxCombinedTextureImageUnits = 0;
+    
+    // Max number of vertex attrs (not counting coord, etc.)
+    int maxVertexAttrs = 0;
+
+    // End of variables set by createNewContext()/createQueryContext()
+
+    // The total available number of texture units used by either the
+    // fixed-function or programmable shader pipeline.
+    // This is computed as: max(maxTextureUnits, maxTextureImageUnits)
+    int maxAvailableTextureUnits;
+
     // Texture Width, Height Max
     int   textureWidthMax = 0;
     int   textureHeightMax = 0;
@@ -802,29 +859,31 @@ public class Canvas3D extends Canvas {
     static final int TEXTUREBIN_BIT	= 0x3;
     static final int RENDERMOLECULE_BIT	= 0x4;
     static final int TRANSPARENCY_BIT	= 0x5;
+    static final int SHADERBIN_BIT	= 0x6;
 
     // bitmask to specify if the corresponding "bin" needs to be updated
     int stateUpdateMask = 0;   
 
     // the set of current "bins" that is to be updated, the stateUpdateMask
     // specifies if each bin in this set is updated or not.
-    Object curStateToUpdate[] = new Object[6];
-
+    Object curStateToUpdate[] = new Object[7];
 
-    // Native method for determining the number of texture unit supported
-    native int getTextureUnitCount(long ctx);
 
     // Native method for determining the texture color table size
     // in the underlying API for this Canvas3D.
     /* native int getTextureColorTableSize(long ctx); */
 
     // This is the native method for creating the underlying graphics context.
-    native long createNewContext(long display, int window, int vid, long fbConfig,
-				 long shareCtx, boolean isSharedCtx,
-				 boolean offScreen);
+    private native long createNewContext(long display, int window, int vid,
+            long fbConfig, long shareCtx, boolean isSharedCtx,
+            boolean offScreen,
+            boolean glslLibraryAvailable,
+            boolean cgLibraryAvailable);
 
-    native void createQueryContext(long display, int window, int vid, long fbConfig, 
-				   boolean offScreen, int width, int height);
+    private native void createQueryContext(long display, int window, int vid,
+            long fbConfig, boolean offScreen, int width, int height,
+            boolean glslLibraryAvailable,
+            boolean cgLibraryAvailable);
 
     native static void destroyContext(long display, int window, long context);
 
@@ -863,9 +922,6 @@ public class Canvas3D extends Canvas {
 
     // The following three methods are used in multi-pass case
 
-    // Native method for setting the depth func
-    native void setDepthFunc(long ctx, int func);
-
     // native method for setting blend color
     native void setBlendColor(long ctx, float red, float green, 
 			      float blue, float alpha);
@@ -1001,7 +1057,7 @@ public class Canvas3D extends Canvas {
 
 
     // native method for updating the texture unit state map
-    native void updateTexUnitStateMap(long ctx, int numActiveTexUnit,
+    private native void updateTexUnitStateMap(long ctx, int numActiveTexUnit,
 					int[] texUnitStateMap);
 
     /**
@@ -1082,7 +1138,7 @@ public class Canvas3D extends Canvas {
     public Canvas3D(GraphicsConfiguration graphicsConfiguration) {
 	this(checkForValidGraphicsConfig(graphicsConfiguration), false);
 
-	// TODO 1.4: remove call to checkForValidGraphicsConfig.
+	// XXXX: ENHANCEMENT -- remove call to checkForValidGraphicsConfig.
 	// Call should then be:
 	// this(graphicsConfiguration, false);
     }
@@ -1135,14 +1191,22 @@ public class Canvas3D extends Canvas {
 	// Needed for Win32-D3D only.
 	vid = nativeWSobj.getCanvasVid(graphicsConfiguration);
 
+        // Issue 163 : Set dirty bits for both Renderer and RenderBin
+        cvDirtyMask[0] = VIEW_INFO_DIRTY;
+        cvDirtyMask[1] = VIEW_INFO_DIRTY;
+
 	// Fix for issue 20.
 	// Needed for Linux and Solaris.
-	Object fbConfigObject;  
-	fbConfigObject = fbConfigTable.get(graphicsConfiguration);
-	if ((fbConfigObject != null) && 
-	    (fbConfigObject instanceof Long)) {
-	    fbConfig = ((Long)fbConfigObject).longValue();
-	    /* System.out.println("Canvas3D creation FBConfig = " + fbConfig + 
+    	GraphicsConfigInfo gcInfo;
+	gcInfo = (GraphicsConfigInfo) fbConfigTable.get(graphicsConfiguration);
+	if (gcInfo != null) {
+            fbConfig = gcInfo.getFBConfig();
+            requestedStencilSize = gcInfo.getRequestedStencilSize();
+            
+	    /*
+	      System.out.println("Canvas3D : requestedStencilSize is " +
+	      requestedStencilSize);
+	      System.out.println("Canvas3D creation FBConfig = " + fbConfig + 
 	       " offScreen is " + offScreen );
 	    */
 	    // This check is needed for Unix and Win-ogl only. fbConfig should 
@@ -1151,8 +1215,8 @@ public class Canvas3D extends Canvas {
 		throw new IllegalArgumentException
 		    (J3dI18N.getString("Canvas3D23"));
 	    }
-	}
-
+	}        
+        
 	if (offScreen) {
 	    screen = new Screen3D(graphicsConfiguration, offScreen);
 
@@ -1164,7 +1228,8 @@ public class Canvas3D extends Canvas {
             // callback from AWT, set the added flag here
             added = true;
 	    synchronized(dirtyMaskLock) {
-	        cvDirtyMask |= Canvas3D.MOVED_OR_RESIZED_DIRTY;
+	        cvDirtyMask[0] |= MOVED_OR_RESIZED_DIRTY;
+	        cvDirtyMask[1] |= MOVED_OR_RESIZED_DIRTY;
 	    }
 
 	    // this canvas will not receive the paint callback either,
@@ -1343,11 +1408,14 @@ public class Canvas3D extends Canvas {
 	}
 
 	synchronized(dirtyMaskLock) {
-	    cvDirtyMask |= Canvas3D.MOVED_OR_RESIZED_DIRTY;
+	    cvDirtyMask[0] |= MOVED_OR_RESIZED_DIRTY;
+	    cvDirtyMask[1] |= MOVED_OR_RESIZED_DIRTY;
 	}
 	
-        canvasBit = VirtualUniverse.mc.getCanvasBit();
-        validCanvas = true;
+        canvasId = VirtualUniverse.mc.getCanvasId();
+        canvasBit = 1 << canvasId;
+ 
+	validCanvas = true;
 	added = true;
 
 	// In case the same canvas is removed and add back,
@@ -1436,8 +1504,9 @@ public class Canvas3D extends Canvas {
 	screen.removeUser(this);
 	evaluateActive();	
 
-        VirtualUniverse.mc.freeCanvasBit(canvasBit);
+        VirtualUniverse.mc.freeCanvasId(canvasId);
 	canvasBit = 0;
+	canvasId = 0;
 
 	ra = null;
 	graphicsContext3D = null;
@@ -1812,7 +1881,7 @@ public class Canvas3D extends Canvas {
 	    width = height = 0;
 	}
 
-	// TODO: illegalSharing
+	// XXXX: illegalSharing
 	
 	if ((offScreenCanvasSize.width != width) ||
 	    (offScreenCanvasSize.height != height)) {
@@ -1842,7 +1911,8 @@ public class Canvas3D extends Canvas {
         offScreenBuffer = buffer;
 
         synchronized(dirtyMaskLock) {
-            cvDirtyMask |= Canvas3D.MOVED_OR_RESIZED_DIRTY;
+            cvDirtyMask[0] |= MOVED_OR_RESIZED_DIRTY;
+            cvDirtyMask[1] |= MOVED_OR_RESIZED_DIRTY;
         }
     }
 
@@ -2033,7 +2103,7 @@ public class Canvas3D extends Canvas {
 		screen.renderer.doWork(0);
 	    } else {
 
-		// TODO: 
+		// XXXX: 
 		// Now we are in trouble, this will cause deadlock if
 		// waitForOffScreenRendering() is invoked
 		  J3dMessage createMessage = VirtualUniverse.mc.getMessage();
@@ -2346,6 +2416,23 @@ public class Canvas3D extends Canvas {
 	graphicsContext3D.runMonitor(J3dThread.NOTIFY);
     }
 
+    /**
+     * Wrapper for native createNewContext method.
+     */
+    long createNewContext(long shareCtx, boolean isSharedCtx) {
+        long retVal = createNewContext(this.screen.display,
+                this.window,
+                this.vid,
+                this.fbConfig,
+                shareCtx, isSharedCtx,
+                this.offScreen,
+                VirtualUniverse.mc.glslLibraryAvailable,
+                VirtualUniverse.mc.cgLibraryAvailable);
+        // compute the max available texture units
+        maxAvailableTextureUnits = Math.max(maxTextureUnits, maxTextureImageUnits);
+        return retVal;
+    }
+    
     /**
      * Make the context associated with the specified canvas current.
      */
@@ -2447,7 +2534,8 @@ public class Canvas3D extends Canvas {
 
 	this.leftManualEyeInImagePlate.set(position);
 	synchronized(dirtyMaskLock) {
-	    cvDirtyMask |= Canvas3D.EYE_IN_IMAGE_PLATE_DIRTY;
+	    cvDirtyMask[0] |= EYE_IN_IMAGE_PLATE_DIRTY;
+            cvDirtyMask[1] |= EYE_IN_IMAGE_PLATE_DIRTY;
 	}
 	redraw();
     }
@@ -2468,7 +2556,8 @@ public class Canvas3D extends Canvas {
 
 	this.rightManualEyeInImagePlate.set(position);
 	synchronized(dirtyMaskLock) {
-	    cvDirtyMask |= Canvas3D.EYE_IN_IMAGE_PLATE_DIRTY;
+	    cvDirtyMask[0] |= EYE_IN_IMAGE_PLATE_DIRTY;
+            cvDirtyMask[1] |= EYE_IN_IMAGE_PLATE_DIRTY;
 	}
 	redraw();
     }
@@ -2542,7 +2631,7 @@ public class Canvas3D extends Canvas {
      * @param position the object that will receive the position
      * @see #setMonoscopicViewPolicy
      */
-    // TODO: This might not make sense for field-sequential HMD. 
+    // XXXX: This might not make sense for field-sequential HMD. 
     public void getCenterEyeInImagePlate(Point3d position) {
 	if (canvasViewCache != null) {
 	    synchronized(canvasViewCache) {
@@ -2851,17 +2940,22 @@ public class Canvas3D extends Canvas {
 	synchronized(cvLock) {
 	    if (view == null) {
 		canvasViewCache = null;
+                canvasViewCacheFrustum = null;
 	    } else {
 		
 		canvasViewCache = new CanvasViewCache(this,
 						      screen.screenViewCache,
-						  view.viewCache);
+                                                      view.viewCache);
+                // Issue 109 : construct a separate canvasViewCache for
+                // computing view frustum
+		canvasViewCacheFrustum = new CanvasViewCache(this,
+						      screen.screenViewCache,
+                                                      view.viewCache);
 		synchronized (dirtyMaskLock) {
-		    cvDirtyMask = (STEREO_DIRTY | MONOSCOPIC_VIEW_POLICY_DIRTY
-				   | EYE_IN_IMAGE_PLATE_DIRTY |
-				   MOVED_OR_RESIZED_DIRTY);	
+                    cvDirtyMask[0] = VIEW_INFO_DIRTY;
+                    cvDirtyMask[1] = VIEW_INFO_DIRTY;
 		}
-	    }	    
+	    }
 	}
     }
 
@@ -2904,7 +2998,8 @@ public class Canvas3D extends Canvas {
 	stereoEnable = flag;
         useStereo = stereoEnable && stereoAvailable;
 	synchronized(dirtyMaskLock) {
-	    cvDirtyMask |= Canvas3D.STEREO_DIRTY;
+	    cvDirtyMask[0] |= STEREO_DIRTY;
+            cvDirtyMask[1] |= STEREO_DIRTY;
 	}
 	redraw();
     }
@@ -2956,7 +3051,8 @@ public class Canvas3D extends Canvas {
 	
 	monoscopicViewPolicy = policy;
 	synchronized(dirtyMaskLock) {
-	    cvDirtyMask |= Canvas3D.MONOSCOPIC_VIEW_POLICY_DIRTY;    
+            cvDirtyMask[0] |= MONOSCOPIC_VIEW_POLICY_DIRTY;
+            cvDirtyMask[1] |= MONOSCOPIC_VIEW_POLICY_DIRTY;
 	}
 	redraw();
     }
@@ -3043,6 +3139,36 @@ public class Canvas3D extends Canvas {
     }
 
 
+    /**
+     * Returns a flag indicating whether or not the specified shading
+     * language is supported. A ShaderError will be generated if an
+     * unsupported shading language is used.
+     *
+     * @param shadingLanguage the shading language being queried, one of:
+     * <code>Shader.SHADING_LANGUAGE_GLSL</code> or
+     * <code>Shader.SHADING_LANGUAGE_CG</code>.
+     *
+     * @return true if the specified shading language is supported,
+     * false otherwise.
+     *
+     * @since Java 3D 1.4
+     */
+    public boolean isShadingLanguageSupported(int shadingLanguage) {
+        // Call queryProperties to ensure that the shading language flags are valid
+        queryProperties();
+        
+        // Return flag for specified shading language
+        switch (shadingLanguage) {
+        case Shader.SHADING_LANGUAGE_GLSL:
+            return shadingLanguageGLSL;
+        case Shader.SHADING_LANGUAGE_CG:
+            return shadingLanguageCg;
+        }
+
+        return false;
+    }
+
+
     /**
      * Returns a read-only Map object containing key-value pairs that define
      * various properties for this Canvas3D.  All of the keys are
@@ -3060,6 +3186,14 @@ public class Canvas3D extends Canvas {
      * <td><b>Value Type</b></td>
      * </tr>
      * <tr>
+     * <td><code>shadingLanguageCg</code></td>
+     * <td>Boolean</td>
+     * </tr>
+     * <tr>
+     * <td><code>shadingLanguageGLSL</code></td>
+     * <td>Boolean</td>
+     * </tr>
+     * <tr>
      * <td><code>doubleBufferAvailable</code></td>
      * <td>Boolean</td>
      * </tr>
@@ -3076,6 +3210,10 @@ public class Canvas3D extends Canvas {
      * <td>Integer</td>
      * </tr>
      * <tr>
+     * <td><code>stencilSize</code></td>
+     * <td>Integer</td>
+     * </tr>
+     * <tr>
      * <td><code>texture3DAvailable</code></td>
      * <td>Boolean</td>
      * </tr>
@@ -3116,10 +3254,26 @@ public class Canvas3D extends Canvas {
      * <td>Boolean</td>
      * </tr>
      * <tr>
+     * <td><code>textureCoordSetsMax</code></td>
+     * <td>Integer</td>
+     * </tr>
+     * <tr>
      * <td><code>textureUnitStateMax</code></td>
      * <td>Integer</td>
      * </tr>
      * <tr>
+     * <td><code>textureImageUnitsMax</code></td>
+     * <td>Integer</td>
+     * </tr>
+     * <tr>
+     * <td><code>textureImageUnitsVertexMax</code></td>
+     * <td>Integer</td>
+     * </tr>
+     * <tr>
+     * <td><code>textureImageUnitsCombinedMax</code></td>
+     * <td>Integer</td>
+     * </tr>
+     * <tr>
      * <td><code>textureCubeMapAvailable</code></td>
      * <td>Boolean</td>
      * </tr>
@@ -3140,6 +3294,10 @@ public class Canvas3D extends Canvas {
      * <td>Float</td>
      * </tr>
      * <tr>
+     * <td><code>vertexAttrsMax</code></td>
+     * <td>Integer</td>
+     * </tr>
+     * <tr>
      * <td><code>compressedGeometry.majorVersionNumber</code></td>
      * <td>Integer</td>
      * </tr>
@@ -3164,6 +3322,22 @@ public class Canvas3D extends Canvas {
      * <p>
      * <ul>
      * <li>
+     * <code>shadingLanguageCg</code>
+     * <ul>
+     * A Boolean indicating whether or not Cg shading Language
+     * is available for this Canvas3D. 
+     * </ul>
+     * </li>
+     *
+     * <li>
+     * <code>shadingLanguageGLSL</code>
+     * <ul>
+     * A Boolean indicating whether or not GLSL shading Language
+     * is available for this Canvas3D.     
+     * </ul>
+     * </li>
+     *
+     * <li>
      * <code>doubleBufferAvailable</code>
      * <ul>
      * A Boolean indicating whether or not double buffering
@@ -3195,7 +3369,6 @@ public class Canvas3D extends Canvas {
      * </ul>
      * </li>
      *
-     *
      * <li>
      * <code>sceneAntialiasingNumPasses</code>
      * <ul>
@@ -3209,6 +3382,14 @@ public class Canvas3D extends Canvas {
      * </li>
      *
      * <li>
+     * <code>stencilSize</code>
+     * <ul>
+     * An Integer indicating the number of stencil bits that are available
+     * for this Canvas3D. 
+     * </ul>
+     * </li>
+     *
+     * <li>
      * <code>texture3DAvailable</code>
      * <ul>
      * A Boolean indicating whether or not 3D Texture mapping
@@ -3329,14 +3510,48 @@ public class Canvas3D extends Canvas {
      * </li>
      *
      * <li>
+     * <code>textureCoordSetsMax</code>
+     * <ul>
+     * An Integer indicating the maximum number of texture coordinate sets
+     * supported by the underlying rendering layer.
+     * </ul>
+     * </li>
+     *
+     * <li>
      * <code>textureUnitStateMax</code>
      * <ul>
-     * An Integer indicating the maximum number of texture unit states
-     * supported by the underlying rendering layer. Java3D allows an
-     * application to specify number of texture unit states more than
-     * what the underlying rendering layer supports; in this case, Java3D
-     * will use multi-pass to support the specified number of texture
-     * unit states.
+     * An Integer indicating the maximum number of fixed-function texture units
+     * supported by the underlying rendering layer. If the number of
+     * application-sepcified texture unit states exceeds the maximum number
+     * for a Canvas3D, and the fixed-function rendering pipeline is used, then
+     * the texture will be effectively disabled for that Canvas3D.
+     * </ul>
+     * </li>
+     *
+     * <li>
+     * <code>textureImageUnitsMax</code>
+     * <ul>
+     * An Integer indicating the maximum number of texture image units
+     * that can be accessed by the fragment shader when programmable shaders
+     * are used.
+     * </ul>
+     * </li>
+     *
+     * <li>
+     * <code>textureImageUnitsVertexMax</code>
+     * <ul>
+     * An Integer indicating the maximum number of texture image units
+     * that can be accessed by the vertex shader when programmable shaders
+     * are used.
+     * </ul>
+     * </li>
+     *
+     * <li>
+     * <code>textureImageUnitsCombinedMax</code>
+     * <ul>
+     * An Integer indicating the combined maximum number of texture image units
+     * that can be accessed by the vertex shader and the fragment shader when
+     * programmable shaders are used.
      * </ul>
      * </li>
      *
@@ -3401,6 +3616,15 @@ public class Canvas3D extends Canvas {
      * </li>
      *
      * <li>
+     * <code>vertexAttrsMax</code>
+     * <ul>
+     * An Integer indicating the maximum number of vertex attributes
+     * supported by the underlying rendering layer. This is in addition to
+     * the vertex coordinate (position), color, normal, and so forth.
+     * </ul>
+     * </li>
+     *
+     * <li>
      * <code>compressedGeometry.majorVersionNumber</code><br>
      * <code>compressedGeometry.minorVersionNumber</code><br>
      * <code>compressedGeometry.minorMinorVersionNumber</code>
@@ -3451,7 +3675,11 @@ public class Canvas3D extends Canvas {
 	// inside the native code after setting the various 
 	// fields in this object
 	createQueryContext(screen.display, window, vid,
-			   fbConfig, offScreen, 1, 1);
+			   fbConfig, offScreen, 1, 1,
+                           VirtualUniverse.mc.glslLibraryAvailable,
+                           VirtualUniverse.mc.cgLibraryAvailable);
+        // compute the max available texture units
+        maxAvailableTextureUnits = Math.max(maxTextureUnits, maxTextureImageUnits);
     }
 
     /**
@@ -3480,8 +3708,12 @@ public class Canvas3D extends Canvas {
 		    1: Renderer.NUM_ACCUMULATION_SAMPLES);
 	}
 	values.add(new Integer(pass));
-	
-	keys.add("compressedGeometry.majorVersionNumber");
+
+	keys.add("stencilSize");
+	// Return the actual stencil size.
+        values.add(new Integer(actualStencilSize));
+
+        keys.add("compressedGeometry.majorVersionNumber");
 	values.add(new Integer(GeometryDecompressor.majorVersionNumber));
 	keys.add("compressedGeometry.minorVersionNumber");
 	values.add(new Integer(GeometryDecompressor.minorVersionNumber));
@@ -3552,8 +3784,29 @@ public class Canvas3D extends Canvas {
         values.add(new Boolean(
 		(textureExtendedFeatures & TEXTURE_LOD_OFFSET) != 0));
 
+        keys.add("textureCoordSetsMax");
+        values.add(new Integer(maxTexCoordSets));
+
         keys.add("textureUnitStateMax");
-        values.add(new Integer(numTexUnitSupported));
+        values.add(new Integer(maxTextureUnits));
+
+        keys.add("textureImageUnitsMax");
+        values.add(new Integer(maxTextureImageUnits));
+
+        keys.add("textureImageUnitsVertexMax");
+        values.add(new Integer(maxVertexTextureImageUnits));
+
+        keys.add("textureImageUnitsCombinedMax");
+        values.add(new Integer(maxCombinedTextureImageUnits));
+
+        keys.add("vertexAttrsMax");
+        values.add(new Integer(maxVertexAttrs));
+
+	keys.add("shadingLanguageGLSL");
+	values.add(new Boolean(shadingLanguageGLSL));
+
+	keys.add("shadingLanguageCg");
+	values.add(new Boolean(shadingLanguageCg));
 
 	keys.add("native.version");
 	values.add(nativeGraphicsVersion);
@@ -3587,12 +3840,21 @@ public class Canvas3D extends Canvas {
     void updateViewCache(boolean flag, CanvasViewCache cvc, 
 		BoundingBox frustumBBox, boolean doInfinite) {
 
+        assert cvc == null;
 	synchronized(cvLock) {	
-	    if (firstPaintCalled && (canvasViewCache != null)) {
-		canvasViewCache.snapshot();
-		canvasViewCache.computeDerivedData(flag, cvc, frustumBBox,
-						   doInfinite);
-	    }	   
+            if (firstPaintCalled && (canvasViewCache != null)) {
+                assert canvasViewCacheFrustum != null;
+                // Issue 109 : choose the appropriate cvCache
+                if (frustumBBox != null) {
+                    canvasViewCacheFrustum.snapshot(true);
+                    canvasViewCacheFrustum.computeDerivedData(flag, null,
+                            frustumBBox, doInfinite);
+                } else {
+                    canvasViewCache.snapshot(false);
+                    canvasViewCache.computeDerivedData(flag, null,
+                            null, doInfinite);
+                }
+            }
 	}
     }
     
@@ -3736,7 +3998,10 @@ public class Canvas3D extends Canvas {
 
 	reset();
 
-	cvDirtyMask |= VIEW_INFO_DIRTY;
+        synchronized (dirtyMaskLock) {
+            cvDirtyMask[0] |= VIEW_INFO_DIRTY;
+            cvDirtyMask[1] |= VIEW_INFO_DIRTY;
+        }
 	needToRebuildDisplayList = true;
 
 	ctxTimeStamp = VirtualUniverse.mc.getContextTimeStamp();
@@ -3754,6 +4019,7 @@ public class Canvas3D extends Canvas {
 	lightBin = null;
 	environmentSet = null;
 	attributeBin = null;
+        shaderBin = null;
 	textureBin = null;
 	renderMolecule = null;
 	polygonAttributes = null;
@@ -3763,6 +4029,7 @@ public class Canvas3D extends Canvas {
 	enableLighting = false;
 	transparency = null;
 	coloringAttributes = null;
+	shaderProgram = null;
 	texture = null;
 	texAttrs = null;
 	if (texUnitState != null) {
@@ -3849,7 +4116,10 @@ public class Canvas3D extends Canvas {
 	updateMaterial(ctx, 1.0f, 1.0f, 1.0f, 1.0f);
 	resetRendering(NOCHANGE);
 	makeCtxCurrent();
-	cvDirtyMask |= VIEW_INFO_DIRTY;
+        synchronized (dirtyMaskLock) {
+            cvDirtyMask[0] |= VIEW_INFO_DIRTY;
+            cvDirtyMask[1] |= VIEW_INFO_DIRTY;
+        }
 	needToRebuildDisplayList = true;
 
 	ctxTimeStamp = VirtualUniverse.mc.getContextTimeStamp();	    
@@ -3978,7 +4248,7 @@ public class Canvas3D extends Canvas {
 	} else {
 	    if (rightStereoPass) {
 		//  Only set cache in right stereo pass, otherwise
-		//  if the left stero pass set the cache value, 
+		//  if the left stereo pass set the cache value, 
 		//  setModelViewMatrix() in right stereo pass will not 
 		//  perform in RenderMolecules.
 		this.modelMatrix = mTrans;
@@ -3991,10 +4261,6 @@ public class Canvas3D extends Canvas {
         setDepthBufferWriteEnable(ctx, mode);
     }
 
-    void setTexUnitStateMap(int texUnitStateIndex, int texUnitIndex) {
-	texUnitStateMap[texUnitIndex] = texUnitStateIndex;
-    }
-
     void setNumActiveTexUnit(int n) {
 	numActiveTexUnit = n;
     }
@@ -4011,6 +4277,29 @@ public class Canvas3D extends Canvas {
 	return lastActiveTexUnit;
     }
 
+    // Create the texture state array
+    void createTexUnitState() {
+        texUnitState = new TextureUnitStateRetained[maxAvailableTextureUnits];
+        for (int t = 0; t < maxAvailableTextureUnits; t++) {
+            texUnitState[t] = new TextureUnitStateRetained();
+            texUnitState[t].texture = null;
+            texUnitState[t].mirror = null;
+        }
+    }
+
+    // Create the texture unit state map
+    void createTexUnitStateMap() {
+        // Create the texture unit state map array, which is a mapping from
+        // texture unit state to the actual underlying texture unit
+        // NOTE: since this is now required to be a 1-to-1 mapping, we will
+        // initialize it as such
+
+        texUnitStateMap = new int[maxAvailableTextureUnits];
+        for (int t = 0; t < maxAvailableTextureUnits; t++) {
+            texUnitStateMap[t] = t;
+        }
+    }
+
     // update the underlying layer of the current texture unit state map
     void updateTexUnitStateMap() {
 	updateTexUnitStateMap(ctx, numActiveTexUnit, texUnitStateMap);
@@ -4164,12 +4453,13 @@ public class Canvas3D extends Canvas {
 	curStateToUpdate[bit] = bin;
     }
 
-    // update LightBin, EnvironmentSet, & AttributeBin if neccessary
+    // update LightBin, EnvironmentSet, AttributeBin & ShaderBin if neccessary
     // according to the stateUpdateMask
 
     static int ENV_STATE_MASK = (1 << LIGHTBIN_BIT) | 
-				(1 << ENVIRONMENTSET_BIT) |
-				(1 << ATTRIBUTEBIN_BIT);
+				(1 << ENVIRONMENTSET_BIT) |	
+	                        (1 << ATTRIBUTEBIN_BIT) |
+                                (1 << SHADERBIN_BIT);
 
     void updateEnvState() {
 
@@ -4190,6 +4480,12 @@ public class Canvas3D extends Canvas {
 		curStateToUpdate[ATTRIBUTEBIN_BIT]).updateAttributes(this);
 	}
 
+	if ((stateUpdateMask & (1 << SHADERBIN_BIT)) != 0) {
+	    ((ShaderBin)
+		curStateToUpdate[SHADERBIN_BIT]).updateAttributes(this);
+	}
+
+
 	// reset the state update mask for those environment state bits
 	stateUpdateMask &= ~ENV_STATE_MASK;
     }
@@ -4317,7 +4613,7 @@ public class Canvas3D extends Canvas {
 	    // it so there is no need to do so in 
 	    // Renderer.freeContextResources()
 	    if (rdr.objectId > 0) {
-		Canvas3D.freeTexture(ctx, rdr.objectId);
+		freeTexture(ctx, rdr.objectId);
 		VirtualUniverse.mc.freeTexture2DId(rdr.objectId);	
 		rdr.objectId = -1;
 
@@ -4325,7 +4621,7 @@ public class Canvas3D extends Canvas {
 	    // Free Graphics2D Texture
 	    if ((graphics2D != null) &&
 		(graphics2D.objectId != -1)) {
-		Canvas3D.freeTexture(ctx, graphics2D.objectId);
+		freeTexture(ctx, graphics2D.objectId);
 		VirtualUniverse.mc.freeTexture2DId(graphics2D.objectId);
 		graphics2D.objectId = -1;
 	    }
diff --git a/src/classes/share/javax/media/j3d/CanvasViewCache.java b/src/classes/share/javax/media/j3d/CanvasViewCache.java
index 7e0ba88..db55ebc 100644
--- a/src/classes/share/javax/media/j3d/CanvasViewCache.java
+++ b/src/classes/share/javax/media/j3d/CanvasViewCache.java
@@ -342,10 +342,19 @@ class CanvasViewCache extends Object {
      * NOTE: This is probably not needed, but we'll do it for symmetry
      * with the ScreenViewCache and ViewCache objects.
      */
-    synchronized void snapshot() {
-	cvcDirtyMask = canvas.cvDirtyMask;
-	canvas.cvDirtyMask = 0;
-	useStereo = canvas.useStereo;
+    synchronized void snapshot(boolean computeFrustum) {
+        // Issue 109 : determine the the correct index to use -- either the
+        // Renderer or RenderBin
+        int dirtyIndex = computeFrustum ?
+            Canvas3D.RENDER_BIN_DIRTY_IDX : Canvas3D.RENDERER_DIRTY_IDX;
+
+        synchronized (canvas.dirtyMaskLock) {
+            // Issue 109 : read/clear the dirty bits for the correct index
+            cvcDirtyMask = canvas.cvDirtyMask[dirtyIndex];
+            canvas.cvDirtyMask[dirtyIndex] = 0;
+        }
+
+        useStereo = canvas.useStereo;
 	monoscopicViewPolicy = canvas.monoscopicViewPolicy;
 	leftManualEyeInImagePlate.set(canvas.leftManualEyeInImagePlate);
 	rightManualEyeInImagePlate.set(canvas.rightManualEyeInImagePlate);
@@ -384,13 +393,29 @@ class CanvasViewCache extends Object {
     private void doComputeDerivedData(boolean currentFlag,
 	CanvasViewCache cvc, BoundingBox frustumBBox, boolean doInfinite) {
 
-	if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
+        // Issue 109 : determine the the correct index to use -- either the
+        // Renderer or RenderBin
+        int dirtyIndex = (frustumBBox != null) ?
+            Canvas3D.RENDER_BIN_DIRTY_IDX : Canvas3D.RENDERER_DIRTY_IDX;
+        int scrvcDirtyMask;
+        
+        // Issue 109 : read/clear the dirty bits for the correct index
+        synchronized (screenViewCache) {
+            scrvcDirtyMask = screenViewCache.scrvcDirtyMask[dirtyIndex];
+            // reset screen view dirty mask if canvas is offScreen. Note:
+            // there is only one canvas per offscreen, so it is ok to
+            // do the reset here.
+            if (canvas.offScreen) {
+                screenViewCache.scrvcDirtyMask[dirtyIndex] = 0;
+            }
+        }
+
+        if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) {
 	    if(cvcDirtyMask != 0)
 		System.out.println("cvcDirtyMask : " +  cvcDirtyMask);
 
-	    if(screenViewCache.scrvcDirtyMask != 0)
-		System.out.println("scrvcDirtyMask : "+
-				   screenViewCache.scrvcDirtyMask);
+	    if(scrvcDirtyMask != 0)
+		System.out.println("scrvcDirtyMask : "+ scrvcDirtyMask);
 
 	    if(viewCache.vcDirtyMask != 0)
 		System.out.println("vcDirtyMask : " +  viewCache.vcDirtyMask);
@@ -406,13 +431,13 @@ class CanvasViewCache extends Object {
 
 	// This flag is use to force a computation when a ViewPlatformTransform
 	// is detected. No sync. needed. We're doing a read of t/f.
-	// TODO: Peeking at the dirty flag is a hack. Need to revisit this.
+	// XXXX: Peeking at the dirty flag is a hack. Need to revisit this.
 	boolean vprNotDirty = (viewCache.vpRetained.vprDirtyMask == 0);
 
 	if(!canvas.offScreen &&
 	   (vprNotDirty) &&
 	   (cvcDirtyMask == 0) &&
-	   (screenViewCache.scrvcDirtyMask == 0) &&
+	   (scrvcDirtyMask == 0) &&
 	   (viewCache.vcDirtyMask == 0) &&
 	    !(updateLastTime && (doInfinite != lastDoInfinite))) {
 	    if(frustumBBox != null)
@@ -439,7 +464,7 @@ class CanvasViewCache extends Object {
 
 	// System.out.println("vpcToVworld is \n" + vpcToVworld);
 
-	try {
+        try {
 	    vworldToVpc.invert(vpcToVworld);
 	}
 	catch (SingularMatrixException e) {
@@ -484,19 +509,13 @@ class CanvasViewCache extends Object {
 	if (frustumBBox != null)
 	    computefrustumBBox(frustumBBox);
 
-	// Copy the computed data into cvc.
+	// Issue 109: cvc should *always* be null
+        assert cvc == null;
 	if(cvc != null)
 	    copyComputedCanvasViewCache(cvc, doInfinite);
 
 	canvas.canvasDirty |= Canvas3D.VIEW_MATRIX_DIRTY;
 
-        // reset screen view dirty mask if canvas is offScreen. Note:
-	// there is only one canvas per offscreen, so it is ok to
-	// do the reset here.
-	if (canvas.offScreen) {
-	    screenViewCache.scrvcDirtyMask = 0;
-	}
-
 	if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1)) {
 	    // Print some data :
 	    System.out.println("useStereo = " + useStereo);
@@ -908,7 +927,7 @@ class CanvasViewCache extends Object {
      */
     private void cacheEyePosition() {
 	if (viewCache.compatibilityModeEnable) {
-	    // TODO: Compute compatibility mode eye position in ImagePlate???
+	    // XXXX: Compute compatibility mode eye position in ImagePlate???
 	    cacheEyePosScreenRelative(leftManualEyeInImagePlate,
 				      rightManualEyeInImagePlate);
 	}
@@ -965,7 +984,7 @@ class CanvasViewCache extends Object {
 
     private void computePlateToVworld() {
 	if (viewCache.compatibilityModeEnable) {
-	    // TODO: implement this correctly for compat mode
+	    // XXXX: implement this correctly for compat mode
 	    leftPlateToVworld.setIdentity();
 	    vworldToLeftPlate.setIdentity();
 	}
@@ -1027,7 +1046,7 @@ class CanvasViewCache extends Object {
         // Concatenate headToLeftImagePlate with leftPlateToVworld
 
 	if (viewCache.compatibilityModeEnable) {
-	    // TODO: implement this correctly for compat mode
+	    // XXXX: implement this correctly for compat mode
 	    headToVworld.setIdentity();
 	}
 	else {
@@ -1054,7 +1073,7 @@ class CanvasViewCache extends Object {
 	// Create a transform with the view platform to coexistence scale
 	tMat1.set(viewPlatformScale);
 
-	// TODO: Is this really correct to ignore HMD?
+	// XXXX: Is this really correct to ignore HMD?
 
 	if (viewCache.viewPolicy != View.HMD_VIEW) {
 	    switch (viewCache.coexistenceCenterInPworldPolicy) {
@@ -1161,7 +1180,7 @@ class CanvasViewCache extends Object {
 
     private void computeCoexistenceToPlate() {
 	if (viewCache.compatibilityModeEnable) {
-	    // TODO: implement this correctly
+	    // XXXX: implement this correctly
 	    coexistenceToLeftPlate.setIdentity();
 	    return;
 	}
@@ -1334,7 +1353,7 @@ class CanvasViewCache extends Object {
 	    B = scale * -backClipDistance;
 	}
 
-	// TODO: Can optimize for HMD case.
+	// XXXX: Can optimize for HMD case.
 	if (true /*viewCache.viewPolicy != View.HMD_VIEW*/) {
 
 	    // Call buildProjView to build the projection and view matrices.
@@ -1392,7 +1411,7 @@ class CanvasViewCache extends Object {
 		}
 	    }
 	}
-	// TODO: The following code has never been ported
+	// XXXX: The following code has never been ported
 //      else {
 //	    Point3d cen_eye;
 //
@@ -1467,14 +1486,14 @@ class CanvasViewCache extends Object {
 	ecToCc.setIdentity();
 
 
-	// TODO: we have no concept of glass correction in the Java 3D API
+	// XXXX: we have no concept of glass correction in the Java 3D API
 	//
 	// Correction in apparent 3D position of window due to glass/CRT
 	// and spherical/cylinderical curvarure of CRT.
 	// This boils down to producing modified values of Lx Ly Hx Hy
 	// and is different for hot spot vs. window center corrections.
 	//
-	/* TODO:
+	/* XXXX:
 	double		cx, cy;
 	if(viewPolicy != HMD_VIEW && enable_crt_glass_correction) {
 	    if (correction_point == CORRECTION_POINT_WINDOW_CENTER) {
@@ -1812,20 +1831,20 @@ class CanvasViewCache extends Object {
     }
 
     Transform3D getImagePlateToVworld() {
-	// TODO: Document -- This will return the transform of left plate.
+	// XXXX: Document -- This will return the transform of left plate.
 	return leftPlateToVworld;
     }
 
 
 
     Transform3D getLastVworldToImagePlate() {
-	// TODO: Document -- This will return the transform of left plate.
+	// XXXX: Document -- This will return the transform of left plate.
 	return lastVworldToLeftPlate;
 
     }
 
     Transform3D getVworldToImagePlate() {
-	// TODO: Document -- This will return the transform of left plate.
+	// XXXX: Document -- This will return the transform of left plate.
 	return vworldToLeftPlate;
     }
 
diff --git a/src/classes/share/javax/media/j3d/CanvasViewEventCatcher.java b/src/classes/share/javax/media/j3d/CanvasViewEventCatcher.java
index 74dc617..2b723e0 100644
--- a/src/classes/share/javax/media/j3d/CanvasViewEventCatcher.java
+++ b/src/classes/share/javax/media/j3d/CanvasViewEventCatcher.java
@@ -42,7 +42,10 @@ class CanvasViewEventCatcher extends ComponentAdapter {
 		System.out.println("It is canvas!");
 	    }
 	    synchronized(canvas) {
-		canvas.cvDirtyMask |= Canvas3D.MOVED_OR_RESIZED_DIRTY; 
+                synchronized (canvas.dirtyMaskLock) {
+                    canvas.cvDirtyMask[0] |= Canvas3D.MOVED_OR_RESIZED_DIRTY;
+                    canvas.cvDirtyMask[1] |= Canvas3D.MOVED_OR_RESIZED_DIRTY;
+                }
 		canvas.resizeGraphics2D = true;
 	    }
 	    
@@ -60,9 +63,12 @@ class CanvasViewEventCatcher extends ComponentAdapter {
 	    System.out.println("Component moved " + e);
 	}
 
-	synchronized(canvas) {	    
-	    canvas.cvDirtyMask |= Canvas3D.MOVED_OR_RESIZED_DIRTY;
-	}
+        synchronized(canvas) {
+            synchronized (canvas.dirtyMaskLock) {
+                canvas.cvDirtyMask[0] |= Canvas3D.MOVED_OR_RESIZED_DIRTY;
+                canvas.cvDirtyMask[1] |= Canvas3D.MOVED_OR_RESIZED_DIRTY;
+            }
+        }
 	// Can't sync. with canvas lock since canvas.getLocationOnScreen()
 	// required Component lock. The order is reverse of 
 	// removeNotify() lock sequence which required Component lock
diff --git a/src/classes/share/javax/media/j3d/CapabilityBits.java b/src/classes/share/javax/media/j3d/CapabilityBits.java
index c7844ae..0b7b41b 100644
--- a/src/classes/share/javax/media/j3d/CapabilityBits.java
+++ b/src/classes/share/javax/media/j3d/CapabilityBits.java
@@ -23,7 +23,7 @@ class CapabilityBits extends Object {
     // Node extends SceneGraphObject
     static final int NODE_ENABLE_COLLISION_REPORTING			= 0;
     static final int NODE_ENABLE_PICK_REPORTING				= 1;
-    static final int NODE_ALLOW_PICK					= 2;
+    private static final int NODE_UNUSED_BIT				= 2;
     static final int NODE_ALLOW_BOUNDS_READ				= 3;
     static final int NODE_ALLOW_BOUNDS_WRITE				= 4;
     static final int NODE_ALLOW_PICKABLE_READ				= 5;
@@ -241,6 +241,11 @@ class CapabilityBits extends Object {
     static final int ALTERNATE_APPEARANCE_ALLOW_SCOPE_READ		= 16;
     static final int ALTERNATE_APPEARANCE_ALLOW_SCOPE_WRITE		= 17;
 
+    // Additional Node bits (must go after all existing Node subclass bits)
+    static final int NODE_ALLOW_PARENT_READ			        = 46;
+    static final int NODE_ALLOW_LOCALE_READ			        = 47;
+
+
     // NodeComponent extends SceneGraphObject
 
     // Appearance extends NodeComponent 
@@ -267,6 +272,12 @@ class CapabilityBits extends Object {
     static final int APPEARANCE_ALLOW_TEXTURE_UNIT_STATE_READ		= 20;
     static final int APPEARANCE_ALLOW_TEXTURE_UNIT_STATE_WRITE		= 21;
 
+    // ShaderAppearance extends Appearance 
+    static final int SHADER_APPEARANCE_ALLOW_SHADER_PROGRAM_READ	= 22;
+    static final int SHADER_APPEARANCE_ALLOW_SHADER_PROGRAM_WRITE	= 23;
+    static final int SHADER_APPEARANCE_ALLOW_SHADER_ATTRIBUTE_SET_READ	= 24;
+    static final int SHADER_APPEARANCE_ALLOW_SHADER_ATTRIBUTE_SET_WRITE	= 25;
+
     // AuralAttributes extends NodeComponent 
     static final int AURAL_ATTRIBUTES_ALLOW_ATTRIBUTE_GAIN_READ		= 0;
     static final int AURAL_ATTRIBUTES_ALLOW_ATTRIBUTE_GAIN_WRITE	= 1;
@@ -362,6 +373,10 @@ class CapabilityBits extends Object {
     static final int
 	RENDERING_ATTRIBUTES_ALLOW_IGNORE_VERTEX_COLORS_WRITE		= 10;
     static final int RENDERING_ATTRIBUTES_ALLOW_DEPTH_ENABLE_WRITE	= 11;
+    static final int RENDERING_ATTRIBUTES_ALLOW_DEPTH_TEST_FUNCTION_READ = 12;
+    static final int RENDERING_ATTRIBUTES_ALLOW_DEPTH_TEST_FUNCTION_WRITE = 13;
+    static final int RENDERING_ATTRIBUTES_ALLOW_STENCIL_ATTRIBUTES_READ = 14;
+    static final int RENDERING_ATTRIBUTES_ALLOW_STENCIL_ATTRIBUTES_WRITE = 15;
 
     // TexCoordGeneration extends NodeComponent 
     static final int TEX_COORD_GENERATION_ALLOW_ENABLE_READ		= 0;
@@ -415,6 +430,20 @@ class CapabilityBits extends Object {
     static final int TEXTURE_UNIT_STATE_ALLOW_STATE_READ		= 0;
     static final int TEXTURE_UNIT_STATE_ALLOW_STATE_WRITE		= 1;
 
+    // ShaderProgram extends NodeComponent 
+    static final int SHADER_PROGRAM_ALLOW_SHADERS_READ			= 0;
+    static final int SHADER_PROGRAM_ALLOW_NAMES_READ			= 1;
+
+    // ShaderAttributeSet extends NodeComponent 
+    static final int SHADER_ATTRIBUTE_SET_ALLOW_ATTRIBUTES_READ		= 0;
+    static final int SHADER_ATTRIBUTE_SET_ALLOW_ATTRIBUTES_WRITE	= 1;
+
+    // ShaderAttribute extends NodeComponent 
+
+    // ShaderAttributeObject extends ShaderAttribute
+    static final int SHADER_ATTRIBUTE_OBJECT_ALLOW_VALUE_READ		= 0;
+    static final int SHADER_ATTRIBUTE_OBJECT_ALLOW_VALUE_WRITE		= 1;
+
     // Geometry extends NodeComponent
     // NOTE: additional bits are below the subclasses
 
@@ -445,6 +474,12 @@ class CapabilityBits extends Object {
     static final int GEOMETRY_ARRAY_ALLOW_REF_DATA_WRITE		= 19;
     static final int GEOMETRY_ARRAY_ALLOW_COUNT_WRITE			= 20;
     static final int GEOMETRY_ARRAY_ALLOW_REF_DATA_READ			= 21;
+    static final int GEOMETRY_ARRAY_ALLOW_VERTEX_ATTR_READ		= 22;
+    static final int GEOMETRY_ARRAY_ALLOW_VERTEX_ATTR_WRITE		= 23;
+
+    // Additional GeometryArray bits (must go after IndexedGeometryArray bits)
+    static final int INDEXED_GEOMETRY_ARRAY_ALLOW_VERTEX_ATTR_INDEX_READ = 24;
+    static final int INDEXED_GEOMETRY_ARRAY_ALLOW_VERTEX_ATTR_INDEX_WRITE = 25;
 
     // CompressedGeometry extends Geometry 
     static final int COMPRESSED_GEOMETRY_ALLOW_COUNT_READ		= 0;
diff --git a/src/classes/share/javax/media/j3d/CgShaderProgram.java b/src/classes/share/javax/media/j3d/CgShaderProgram.java
new file mode 100644
index 0000000..c531073
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/CgShaderProgram.java
@@ -0,0 +1,173 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The CgShaderProgram object is a concrete implementation of a
+ * ShaderProgram node component for NVIDIA's Cg shader language.
+ *
+ * @see SourceCodeShader
+ *
+ * @since Java 3D 1.4
+ */
+
+public class CgShaderProgram extends ShaderProgram {
+
+    /**
+     * Constructs a Cg shader program node component.
+     *
+     * <br>
+     * TODO: ADD MORE DOCUMENTATION HERE.
+     */
+    public CgShaderProgram() {
+    }
+
+    // Implement abstract setVertexAttrNames method (inherit javadoc from parent class)
+    public void setVertexAttrNames(String[] vertexAttrNames) {
+	checkForLiveOrCompiled();
+
+        if (vertexAttrNames != null) {
+            for (int i = 0; i < vertexAttrNames.length; i++) {
+                if (vertexAttrNames[i] == null) {
+                    throw new NullPointerException();
+                }
+            }
+        }
+
+        ((CgShaderProgramRetained)this.retained).setVertexAttrNames(vertexAttrNames);
+    }
+
+    // Implement abstract getVertexAttrNames method (inherit javadoc from parent class)
+    public String[] getVertexAttrNames() {
+
+	if (isLiveOrCompiled()) {
+	    if(!this.getCapability(ALLOW_NAMES_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("CgShaderProgram0"));
+	    }
+	}
+
+ 	return ((CgShaderProgramRetained)this.retained).getVertexAttrNames();
+    }
+
+    // Implement abstract setShaderAttrNames method (inherit javadoc from parent class)
+    public void setShaderAttrNames(String[] shaderAttrNames) {
+	checkForLiveOrCompiled();
+
+        if (shaderAttrNames != null) {
+            for (int i = 0; i < shaderAttrNames.length; i++) {
+                if (shaderAttrNames[i] == null) {
+                    throw new NullPointerException();
+                }
+            }
+        }
+
+        ((CgShaderProgramRetained)this.retained).setShaderAttrNames(shaderAttrNames);
+    }
+
+    // Implement abstract getShaderAttrNames method (inherit javadoc from parent class)
+    public String[] getShaderAttrNames() {
+
+	if (isLiveOrCompiled()) {
+	    if(!this.getCapability(ALLOW_NAMES_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("CgShaderProgram0"));
+	    }
+	}
+
+ 	return ((CgShaderProgramRetained)this.retained).getShaderAttrNames();
+    }
+
+    /**
+     * Copies the specified array of shaders into this shader
+     * program. This method makes a shallow copy of the array. The
+     * array of shaders may be null or empty (0 length), but the
+     * elements of the array must be non-null. The shading language of
+     * each shader in the array must be
+     * <code>SHADING_LANGUAGE_CG</code>. Each shader in the array must
+     * be a SourceCodeShader. There must be no more than one vertex shader
+     * and one fragment shader in the array.
+     *
+     * @param shaders array of Shader objects to be copied into this
+     * ShaderProgram
+     *
+     * @exception CapabilityNotSetException if appropriate capability is
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @exception IllegalArgumentException if the shading language of
+     * any shader in the shaders array is <em>not</em>
+     * <code>SHADING_LANGUAGE_CG</code>.
+     *
+     * @exception IllegalArgumentException if there are more than one
+     * vertex shader or more than one fragment shader in the shaders
+     * array.
+     *
+     * @exception ClassCastException if any shader in the shaders
+     * array is <em>not</em> a SourceCodeShader.
+     */
+    public void setShaders(Shader[] shaders) {
+	checkForLiveOrCompiled();
+
+	if (shaders != null) {
+            // Check shaders for valid shading language, class type, etc.
+            for (int i = 0; i < shaders.length; i++) {
+                boolean hasVertexShader = false;
+                boolean hasFragmentShader = false;
+
+                // Check shading language
+                if (shaders[i].getShadingLanguage() != Shader.SHADING_LANGUAGE_CG) {
+                    throw new IllegalArgumentException(J3dI18N.getString("CgShaderProgram2"));
+                }
+
+                // Check for more than one vertex shader or fragment shader
+                if (shaders[i].getShaderType() == Shader.SHADER_TYPE_VERTEX) {
+                    if (hasVertexShader) {
+                        throw new IllegalArgumentException(J3dI18N.getString("CgShaderProgram3"));
+                    }
+                    hasVertexShader = true;
+                }
+                else { // Shader.SHADER_TYPE_FRAGMENT
+                    if (hasFragmentShader) {
+                        throw new IllegalArgumentException(J3dI18N.getString("CgShaderProgram4"));
+                    }
+                    hasFragmentShader = true;
+                }
+
+                // Try to cast shader to SourceCodeShader; it will throw
+                // ClassCastException if it isn't.
+                SourceCodeShader shad = (SourceCodeShader)shaders[i];
+            }
+        }
+
+ 	((CgShaderProgramRetained)this.retained).setShaders(shaders);
+    }
+
+    // Implement abstract getShaders method (inherit javadoc from parent class)
+    public Shader[] getShaders() {
+	if (isLiveOrCompiled()) {
+	    if(!this.getCapability(ALLOW_SHADERS_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("CgShaderProgram1"));
+	    }
+	}
+
+ 	return ((CgShaderProgramRetained)this.retained).getShaders();
+    }
+
+    /**
+     * Creates a retained mode CgShaderProgramRetained object that this
+     * CgShaderProgram component object will point to.
+     */
+    void createRetained() {
+	this.retained = new CgShaderProgramRetained();
+	this.retained.setSource(this);
+    }
+
+}
diff --git a/src/classes/share/javax/media/j3d/CgShaderProgramRetained.java b/src/classes/share/javax/media/j3d/CgShaderProgramRetained.java
new file mode 100644
index 0000000..eed2484
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/CgShaderProgramRetained.java
@@ -0,0 +1,283 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The CgShaderProgram object is a concrete implementation of a
+ * ShaderProgram node component for NVIDIA's Cg shader language.
+ *
+ * @see SourceCodeShader
+ *
+ * @since Java 3D 1.4
+ */
+
+class CgShaderProgramRetained extends ShaderProgramRetained {
+    
+    /**
+     * Constructs a Cg shader program node component.
+     *
+     * <br>
+     * TODO: ADD MORE DOCUMENTATION HERE.
+     */
+    CgShaderProgramRetained() {
+    }
+
+    synchronized void createMirrorObject() {
+	// System.out.println("CgShaderProgramRetained : createMirrorObject");
+        // This method should only call by setLive().
+	if (mirror == null) {
+	    CgShaderProgramRetained  mirrorCgSP = new CgShaderProgramRetained();	    
+	    mirror = mirrorCgSP;
+	}
+	initMirrorObject();
+    }
+
+    // ShaderAttributeValue methods    
+
+    native ShaderError setUniform1i(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    int value);
+    
+    native ShaderError setUniform1f(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    float value);
+    
+    native ShaderError setUniform2i(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    int[] value);
+    
+    native ShaderError setUniform2f(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    float[] value);
+    
+    native ShaderError setUniform3i(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    int[] value);
+    
+    native ShaderError setUniform3f(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    float[] value);    
+    
+    native ShaderError setUniform4i(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    int[] value);
+    
+    native ShaderError setUniform4f(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    float[] value);    
+    
+    native ShaderError setUniformMatrix3f(long ctx,
+					   long shaderProgramId,
+				           long uniformLocation,
+					   float[] value);
+
+    native ShaderError setUniformMatrix4f(long ctx,
+					   long shaderProgramId,
+			         	   long uniformLocation,
+					   float[] value);
+
+    // ShaderAttributeArray methods
+
+    native ShaderError setUniform1iArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      int[] value);
+    
+    native ShaderError setUniform1fArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      float[] value);
+    
+    native ShaderError setUniform2iArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      int[] value);
+    
+    native ShaderError setUniform2fArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      float[] value);
+    
+    native ShaderError setUniform3iArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      int[] value);
+    
+    native ShaderError setUniform3fArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      float[] value);    
+    
+    native ShaderError setUniform4iArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      int[] value);
+    
+    native ShaderError setUniform4fArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      float[] value);    
+    
+    native ShaderError setUniformMatrix3fArray(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    int numElements,
+					    float[] value);
+
+    native ShaderError setUniformMatrix4fArray(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    int numElements,
+					    float[] value);
+
+
+    
+    /* New native interfaces */
+    private native ShaderError createNativeShader(long ctx, int shaderType, long[] shaderId);
+    private native ShaderError destroyNativeShader(long ctx, long shaderId);
+    private native ShaderError compileNativeShader(long ctx, long shaderId, String program);
+
+    private native ShaderError createNativeShaderProgram(long ctx, long[] shaderProgramId);
+    private native ShaderError destroyNativeShaderProgram(long ctx, long shaderProgramId);
+    private native ShaderError linkNativeShaderProgram(long ctx, long shaderProgramId,
+						       long[] shaderId);
+    private native void lookupNativeVertexAttrNames(long ctx, long shaderProgramId,
+            int numAttrNames, String[] attrNames, boolean[] errArr);
+    private native void lookupNativeShaderAttrNames(long ctx, long shaderProgramId,
+            int numAttrNames, String[] attrNames, long[] locArr,
+            int[] typeArr, int[] sizeArr, boolean[] isArrayArr);
+
+    private native ShaderError useShaderProgram(long ctx, long shaderProgramId);
+
+    /**
+     * Method to return a flag indicating whether this
+     * ShaderProgram is supported on the specified Canvas.
+     */
+    boolean isSupported(Canvas3D cv) {
+        return cv.shadingLanguageCg;
+    }
+
+    /**
+     * Method to create the native shader.
+     */
+    ShaderError createShader(long ctx, ShaderRetained shader, long[] shaderIdArr) {	
+	  return  createNativeShader(ctx, shader.shaderType, shaderIdArr);
+    }
+    
+    /**
+     * Method to destroy the native shader.
+     */
+    ShaderError destroyShader(long ctx, long shaderId) {
+	return destroyNativeShader(ctx, shaderId);
+    }
+    
+    /**
+     * Method to compile the native shader.
+     */
+    ShaderError compileShader(long ctx, long shaderId, String source) {
+        return compileNativeShader(ctx, shaderId, source );
+    }
+
+    /**
+     * Method to create the native shader program.
+     */
+    ShaderError createShaderProgram(long ctx, long[] shaderProgramIdArr) {
+	    return createNativeShaderProgram(ctx, shaderProgramIdArr);  
+    }
+
+    /**
+     * Method to destroy the native shader program.
+     */
+    ShaderError destroyShaderProgram(long ctx, long shaderProgramId) {
+        return destroyNativeShaderProgram(ctx, shaderProgramId);
+    }
+
+    /**
+     * Method to link the native shader program.
+     */
+    ShaderError linkShaderProgram(long ctx, long shaderProgramId, long[] shaderIds) {
+        return linkNativeShaderProgram(ctx, shaderProgramId, shaderIds);
+    }
+ 
+    ShaderError bindVertexAttrName(long ctx, long shaderProgramId, String attrName, int attrIndex) {
+        // This is a no-op for Cg
+        return null;
+    }
+
+    void lookupVertexAttrNames(long ctx, long shaderProgramId, String[] attrNames, boolean[] errArr) {
+        lookupNativeVertexAttrNames(ctx, shaderProgramId, attrNames.length, attrNames, errArr);
+    }
+
+    void lookupShaderAttrNames(long ctx, long shaderProgramId,
+            String[] attrNames, AttrNameInfo[] attrNameInfoArr) {
+
+        int numAttrNames = attrNames.length;
+        
+        long[] locArr = new long[numAttrNames];
+        int[] typeArr = new int[numAttrNames];
+        int[] sizeArr = new int[numAttrNames]; // currently unused
+        boolean[] isArrayArr = new boolean[numAttrNames];
+
+        // Initialize loc array to -1 (indicating no location)
+        for (int i = 0; i < numAttrNames; i++) {
+            locArr[i] = -1;
+        }
+
+        lookupNativeShaderAttrNames(ctx, shaderProgramId,
+                numAttrNames, attrNames, locArr, typeArr, sizeArr, isArrayArr);
+
+        for (int i = 0; i < numAttrNames; i++) {
+            attrNameInfoArr[i] = new AttrNameInfo();
+            attrNameInfoArr[i].setLocation(locArr[i]);
+            attrNameInfoArr[i].setArray(isArrayArr[i]);
+            attrNameInfoArr[i].setType(typeArr[i]);
+            System.err.println(attrNames[i] +
+                    " : loc = " + locArr[i] +
+                    ", type = " + typeArr[i] +
+                    ", isArray = " + isArrayArr[i] +
+                    ", size = " + sizeArr[i]);
+        }
+    }
+
+    /**
+     * Method to enable the native shader program.
+     */
+    ShaderError enableShaderProgram(long ctx, long shaderProgramId) {
+	return useShaderProgram(ctx, shaderProgramId);
+    }
+	
+    /**
+     * Method to disable the native shader program.
+     */
+    ShaderError disableShaderProgram(long ctx) {
+	return useShaderProgram(ctx, 0);
+    }
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/Clip.java b/src/classes/share/javax/media/j3d/Clip.java
index b32342b..e374ec3 100644
--- a/src/classes/share/javax/media/j3d/Clip.java
+++ b/src/classes/share/javax/media/j3d/Clip.java
@@ -61,6 +61,12 @@ public class Clip extends Leaf {
      public static final int
     ALLOW_BACK_DISTANCE_WRITE = CapabilityBits.CLIP_ALLOW_BACK_DISTANCE_WRITE;
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_APPLICATION_BOUNDS_READ,
+        ALLOW_BACK_DISTANCE_READ
+    };
+    
     /**
      * Constructs a Clip node with default parameters.  The default
      * values are as follows:
@@ -72,12 +78,17 @@ public class Clip extends Leaf {
      */
     public Clip () {
 	// Just use the defaults
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
      * Constructs a Clip node with the specified back clip distance.
      */
     public Clip(double backDistance) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((ClipRetained)this.retained).initBackDistance(backDistance);
     }
 
diff --git a/src/classes/share/javax/media/j3d/ColoringAttributes.java b/src/classes/share/javax/media/j3d/ColoringAttributes.java
index 996edf1..6858829 100644
--- a/src/classes/share/javax/media/j3d/ColoringAttributes.java
+++ b/src/classes/share/javax/media/j3d/ColoringAttributes.java
@@ -115,6 +115,12 @@ public class ColoringAttributes extends NodeComponent {
      */
     public static final int SHADE_GOURAUD = 3;
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_COLOR_READ,
+        ALLOW_SHADE_MODEL_READ
+    };
+    
     /**
      * Constructs a ColoringAttributes node with default parameters.
      * The default values are as follows:
@@ -125,6 +131,8 @@ public class ColoringAttributes extends NodeComponent {
      */
     public ColoringAttributes() {
 	// Just use default attributes
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -134,7 +142,10 @@ public class ColoringAttributes extends NodeComponent {
      * SHADE_FLAT, or SHADE_GOURAUD
      */
     public ColoringAttributes(Color3f color, int shadeModel) {
-	((ColoringAttributesRetained)this.retained).initColor(color);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((ColoringAttributesRetained)this.retained).initColor(color);
 	((ColoringAttributesRetained)this.retained).initShadeModel(shadeModel);
   
     }
@@ -149,7 +160,10 @@ public class ColoringAttributes extends NodeComponent {
      */
     public ColoringAttributes(float red, float green, float blue,
 			      int shadeModel) {
-	((ColoringAttributesRetained)this.retained).initColor(red, green,blue);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((ColoringAttributesRetained)this.retained).initColor(red, green,blue);
 	((ColoringAttributesRetained)this.retained).initShadeModel(shadeModel);
     }
 
@@ -315,7 +329,8 @@ public class ColoringAttributes extends NodeComponent {
      * Capability read bit set will be displayed.
      */
     public String toString() {
-        StringBuffer str=new StringBuffer("ColoringAttributes:");
+        StringBuffer str = new StringBuffer(getNamePrefix());
+        str.append("javax.media.j3d.ColoringAttributes: ");
         String shadingModes[] = { "FASTEST", "NICEST", "SHADE_FLAT",
                                                      "SHADE_GOURAUD" };
 
diff --git a/src/classes/share/javax/media/j3d/CompressedGeometry.java b/src/classes/share/javax/media/j3d/CompressedGeometry.java
index 60541f9..8609349 100644
--- a/src/classes/share/javax/media/j3d/CompressedGeometry.java
+++ b/src/classes/share/javax/media/j3d/CompressedGeometry.java
@@ -14,11 +14,10 @@ package javax.media.j3d;
 
 /**
  * The compressed geometry object is used to store geometry in a
- * compressed format.  Using compressed geometry reduces the amount
- * of memory needed by a Java 3D application and increases the speed
- * objects can be sent over the network.  Once geometry decompression
- * hardware support becomes available, increased rendering performance
- * will also result from the use of compressed geometry.
+ * compressed format. Using compressed geometry may increase the speed
+ * objects can be sent over the network. Note that the geometry will
+ * be decompressed in memory, so the application will not see any
+ * memory savings.
  * <p>
  * Compressed geometry may be passed to this CompressedGeometry object
  * in one of two ways: by copying the data into this object using the
@@ -49,6 +48,8 @@ package javax.media.j3d;
  * the results are undefined.
  * </li>
  * </ul>
+ *
+ * @deprecated As of Java 3D version 1.4.
  */
 public class CompressedGeometry extends Geometry {
 
@@ -87,10 +88,21 @@ public class CompressedGeometry extends Geometry {
     ALLOW_REF_DATA_READ =
 	CapabilityBits.COMPRESSED_GEOMETRY_ALLOW_REF_DATA_READ;
 
+
+    // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+	ALLOW_COUNT_READ,
+	ALLOW_HEADER_READ,
+	ALLOW_GEOMETRY_READ,
+	ALLOW_REF_DATA_READ
+    };
+
     /**
      * Package scoped default constructor for use by cloneNodeComponent.
      */
     CompressedGeometry() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -156,6 +168,9 @@ public class CompressedGeometry extends Geometry {
 	    throw new IllegalArgumentException
 		(J3dI18N.getString("CompressedGeometry0")) ;
 
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
 	// Create a separate copy of the given header.
 	cgHeader = new CompressedGeometryHeader() ;
 	hdr.copy(cgHeader) ;
@@ -174,33 +189,10 @@ public class CompressedGeometry extends Geometry {
     }
 
     /**
-     * Creates a new CompressedGeometry NodeComponent.  The
-     * specified compressed geometry data is accessed by reference
-     * from the specified buffer.
-     * If the version number of compressed geometry, as specified by
-     * the CompressedGeometryHeader, is incompatible with the
-     * supported version of compressed geometry in the current version
-     * of Java 3D, the compressed geometry object will not be
-     * rendered.
-     *
-     * @param hdr the compressed geometry header.  This is copied
-     * into the CompressedGeometry NodeComponent.
-     *
-     * @param compressedGeometry a buffer containing an NIO byte buffer
-     * of compressed geometry data.  The
-     * geometry must conform to the format described in Appendix B of
-     * the <i>Java 3D API Specification</i>.
+     * This constructor is not implemented.
      *
-     * @exception UnsupportedOperationException this method is not
-     * yet implemented
-     *
-     * @exception IllegalArgumentException if a problem is detected with the
-     * header,
-     * or if the java.nio.Buffer contained in the specified J3DBuffer
-     * is not a java.nio.ByteBuffer object.
-     *
-     * @see CompressedGeometryHeader
-     * @see Canvas3D#queryProperties
+     * @exception UnsupportedOperationException this constructor is not
+     * implemented
      *
      * @since Java 3D 1.3
      */
@@ -323,7 +315,6 @@ public class CompressedGeometry extends Geometry {
 	// the same number of Shape3D objects as TriangleArray using 1/2
 	// to 2/3 of the vertices, with only a marginal performance penalty.
 	//
-	// TODO revisit this
 	return decompressor.toTriangleStripArrays(cgr) ;
     }
 
@@ -345,9 +336,7 @@ public class CompressedGeometry extends Geometry {
     /**
      * Gets the compressed geometry data reference.
      *
-     * @return the current compressed geometry data reference;
-     * null is returned if this compressed geometry object was created
-     * with a J3DBuffer reference rather than a byte array.
+     * @return the current compressed geometry data reference.
      *
      * @exception IllegalStateException if the data access mode for this
      * object is not by-reference.
@@ -372,14 +361,11 @@ public class CompressedGeometry extends Geometry {
 
 
     /**
-     * Gets the compressed geometry data buffer reference.
+     * Gets the compressed geometry data buffer reference, which is
+     * always null since NIO buffers are not supported for
+     * CompressedGeometry objects.
      *
-     * @return the current compressed geometry data buffer reference;
-     * null is returned if this compressed geometry object was created
-     * with a byte array reference rather than a J3DBuffer.
-     *
-     * @exception IllegalStateException if the data access mode for this
-     * object is not by-reference.
+     * @return null
      *
      * @exception CapabilityNotSetException if appropriate capability is
      *  not set and this object is part of live or compiled scene graph
@@ -392,11 +378,6 @@ public class CompressedGeometry extends Geometry {
 		throw new CapabilityNotSetException
 		    (J3dI18N.getString("CompressedGeometry6")) ;
 
-	if (!isByReference())
-	    throw new IllegalStateException
-		(J3dI18N.getString("CompressedGeometry8")) ;
-
-	// TODO: implement this when NIO buffer support is added
 	return null;
     }
 
diff --git a/src/classes/share/javax/media/j3d/CompressedGeometryHeader.java b/src/classes/share/javax/media/j3d/CompressedGeometryHeader.java
index 39f409a..38876a9 100644
--- a/src/classes/share/javax/media/j3d/CompressedGeometryHeader.java
+++ b/src/classes/share/javax/media/j3d/CompressedGeometryHeader.java
@@ -25,6 +25,8 @@ import javax.vecmath.*;
  * provided.
  *
  * @see CompressedGeometry
+ *
+ * @deprecated As of Java 3D version 1.4.
  */
 public class CompressedGeometryHeader extends Object {
 
diff --git a/src/classes/share/javax/media/j3d/CompressedGeometryRetained.java b/src/classes/share/javax/media/j3d/CompressedGeometryRetained.java
index 6bd7164..737fa4d 100644
--- a/src/classes/share/javax/media/j3d/CompressedGeometryRetained.java
+++ b/src/classes/share/javax/media/j3d/CompressedGeometryRetained.java
@@ -81,23 +81,31 @@ class CompressedGeometryRetained extends GeometryRetained {
     private GeometryRetained pickGeometry = null ;
 
     /**
-     * Native method that returns availability of a native by-reference
+     * Formerly native method that returns availability of a native by-reference
      * rendering API for compressed geometry.
      */
-    native boolean decompressByRef(long ctx) ;
+    private boolean decompressByRef(long ctx) {
+	return false;
+    }
 
     /**
-     * Native method that returns availability of hardware acceleration for
-     * compressed geometry of the given version.
+     * Formerly native method that returns availability of hardware
+     * rendering (and acceleration) for compressed geometry of the
+     * given version.
      */
-    native boolean decompressHW(long ctx, int majorVersion, int minorVersion) ;
+    private boolean decompressHW(long ctx, int majorVersion, int minorVersion) {
+	return false;
+    }
 
     /**
-     * Native method that does the rendering
+     * Formerly native method that does HW compressed geometry rendering
      */
-    native void execute(long  ctx, int version, int bufferType,
-			int bufferContents, int renderFlags,
-			int offset, int size, byte[] geometry) ;
+    private void execute(long  ctx, int version, int bufferType,
+			 int bufferContents, int renderFlags,
+			 int offset, int size, byte[] geometry) {
+
+	assert false : "This method should never be called!";
+    }
 
     /**
      * Method for calling native execute() method on behalf of the J3D renderer.
@@ -107,7 +115,7 @@ class CompressedGeometryRetained extends GeometryRetained {
 		 boolean multiScreen, int screen,
 		 boolean ignoreVertexColors, int pass) {
 
-	// TODO: alpha udpate
+	// XXXX: alpha udpate
 	execute(cv.ctx, packedVersion, bufferType, bufferContents,
 		renderFlags, offset, size, compressedGeometry) ;
     }
@@ -313,12 +321,12 @@ class CompressedGeometryRetained extends GeometryRetained {
     // The following intersect() methods are used to implement geometry-based
     // picking and collision.
     //
-    boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {
+    boolean intersect(PickShape pickShape, PickInfo.IntersectionInfo iInfo,  int flags, Point3d iPnt) {
 	GeometryRetained geom = getPickGeometry() ;
 	return (geom != null ?
-		geom.intersect(pickShape, dist, iPnt) : false);
+		geom.intersect(pickShape, iInfo, flags, iPnt) : false);
     }
-
+    
     boolean intersect(Bounds targetBound) {
 	GeometryRetained geom = getPickGeometry() ;
 	return (geom != null ? geom.intersect(targetBound) : false);
diff --git a/src/classes/share/javax/media/j3d/ConeSound.java b/src/classes/share/javax/media/j3d/ConeSound.java
index 8b57d27..424195b 100644
--- a/src/classes/share/javax/media/j3d/ConeSound.java
+++ b/src/classes/share/javax/media/j3d/ConeSound.java
@@ -147,6 +147,12 @@ public class ConeSound extends PointSound {
   public static final int
     ALLOW_ANGULAR_ATTENUATION_WRITE = CapabilityBits.CONE_SOUND_ALLOW_ANGULAR_ATTENUATION_WRITE;
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+	ALLOW_DIRECTION_READ,
+	ALLOW_ANGULAR_ATTENUATION_READ
+    };
+
     /**
      * Constructs and initializes a new ConeSound node using default
      * parameters.  The following default values are used:
@@ -159,6 +165,8 @@ public class ConeSound extends PointSound {
     public ConeSound() {
         // Uses default values defined in ConeSoundRetained.java
        super();
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -177,6 +185,10 @@ public class ConeSound extends PointSound {
                       Vector3f direction) {
 
         super(soundData, initialGain, position );
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((ConeSoundRetained)this.retained).setDirection(direction);
     }
 
@@ -201,6 +213,10 @@ public class ConeSound extends PointSound {
                       float dirX, float dirY, float dirZ) {
 
         super(soundData, initialGain, posX, posY, posZ );
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((ConeSoundRetained)this.retained).setDirection(dirX, dirY, dirZ);
     }
 
@@ -248,6 +264,10 @@ public class ConeSound extends PointSound {
 
         super(soundData, initialGain, loopCount, release, continuous, enable,
                       region, priority, position, frontDistanceAttenuation );
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((ConeSoundRetained)this.retained).setBackDistanceGain(
                       backDistanceAttenuation);
         ((ConeSoundRetained)this.retained).setDirection(direction);
@@ -301,6 +321,10 @@ public class ConeSound extends PointSound {
         super(soundData, initialGain, loopCount, release, continuous, enable,
                      region, priority, posX, posY, posZ, 
                      frontDistance, frontDistanceGain );
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((ConeSoundRetained)this.retained).setDirection(dirX, dirY, dirZ);
         ((ConeSoundRetained)this.retained).setBackDistanceGain(
                      backDistance, backDistanceGain );
@@ -346,6 +370,10 @@ public class ConeSound extends PointSound {
 
         super(soundData, initialGain, loopCount, release, continuous, enable,
                       region, priority, position, distanceAttenuation );
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((ConeSoundRetained)this.retained).setDirection(direction);
         ((ConeSoundRetained)this.retained).setAngularAttenuation(
                       angularAttenuation);
@@ -402,6 +430,10 @@ public class ConeSound extends PointSound {
         super(soundData, initialGain, loopCount, release, continuous, enable,
                      region, priority, posX, posY, posZ, 
                      distance, distanceGain );
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((ConeSoundRetained)this.retained).setDirection(dirX, dirY, dirZ);
         ((ConeSoundRetained)this.retained).setAngularAttenuation(angle,
                      angularGain, frequencyCutoff);
@@ -444,6 +476,10 @@ public class ConeSound extends PointSound {
 
         super(soundData, initialGain, loopCount, release, continuous, enable,
                       region, priority, position, frontDistanceAttenuation );
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((ConeSoundRetained)this.retained).setBackDistanceGain(
                       backDistanceAttenuation);
         ((ConeSoundRetained)this.retained).setDirection(direction);
@@ -497,6 +533,10 @@ public class ConeSound extends PointSound {
         super(soundData, initialGain, loopCount, release, continuous, enable,
                      region, priority, posX, posY, posZ, 
                      frontDistance, frontDistanceGain );
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((ConeSoundRetained)this.retained).setBackDistanceGain(
                      backDistance, backDistanceGain );
         ((ConeSoundRetained)this.retained).setDirection(dirX, dirY, dirZ);
diff --git a/src/classes/share/javax/media/j3d/DepthComponent.java b/src/classes/share/javax/media/j3d/DepthComponent.java
index 97f6af1..ad3212d 100644
--- a/src/classes/share/javax/media/j3d/DepthComponent.java
+++ b/src/classes/share/javax/media/j3d/DepthComponent.java
@@ -31,10 +31,18 @@ public abstract class DepthComponent extends NodeComponent {
     public static final int
     ALLOW_DATA_READ = CapabilityBits.DEPTH_COMPONENT_ALLOW_DATA_READ;
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_SIZE_READ,
+        ALLOW_DATA_READ
+    };
+    
     /**
      * default constructor
      */
     DepthComponent() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
diff --git a/src/classes/share/javax/media/j3d/DirectionalLight.java b/src/classes/share/javax/media/j3d/DirectionalLight.java
index 987e014..a858cd1 100644
--- a/src/classes/share/javax/media/j3d/DirectionalLight.java
+++ b/src/classes/share/javax/media/j3d/DirectionalLight.java
@@ -41,6 +41,11 @@ public class DirectionalLight extends Light {
   public static final int
     ALLOW_DIRECTION_WRITE = CapabilityBits.DIRECTIONAL_LIGHT_ALLOW_DIRECTION_WRITE;
 
+    // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+	ALLOW_DIRECTION_READ
+    };
+    
     /**
      * Constructs a DirectionalLight node with default parameters.
      * The default values are as follows:
@@ -49,6 +54,8 @@ public class DirectionalLight extends Light {
      * </ul>
      */
     public DirectionalLight() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -59,6 +66,10 @@ public class DirectionalLight extends Light {
      */
     public DirectionalLight(Color3f color, Vector3f direction) {
 	super(color);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
 	((DirectionalLightRetained)this.retained).initDirection(direction);
     }
 
@@ -71,6 +82,10 @@ public class DirectionalLight extends Light {
      */
     public DirectionalLight(boolean lightOn, Color3f color, Vector3f direction) {
 	super(lightOn, color);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
 	((DirectionalLightRetained)this.retained).initDirection(direction);
     }
 
diff --git a/src/classes/share/javax/media/j3d/DisplayListRenderMethod.java b/src/classes/share/javax/media/j3d/DisplayListRenderMethod.java
index 012f30a..b3f8625 100644
--- a/src/classes/share/javax/media/j3d/DisplayListRenderMethod.java
+++ b/src/classes/share/javax/media/j3d/DisplayListRenderMethod.java
@@ -155,7 +155,7 @@ class DisplayListRenderMethod implements RenderMethod {
 	Transform3D staticNormalTransform;
 
 	if ((rm.primaryRenderAtomList != null) &&
-                (rm.texCoordSetMapLen <= cv.numTexCoordSupported)) {
+                (rm.texCoordSetMapLen <= cv.maxTexCoordSets)) {
 
 	    cv.newDisplayList(cv.ctx, rm.displayListId);
 
@@ -195,7 +195,7 @@ class DisplayListRenderMethod implements RenderMethod {
 
 	geo = (GeometryArrayRetained)ra.geometry();
 	if ((geo.texCoordSetMap != null) && 
-		(geo.texCoordSetMap.length > cv.numTexCoordSupported)) {
+		(geo.texCoordSetMap.length > cv.maxTexCoordSets)) {
 	    return;
         }
 
@@ -224,7 +224,7 @@ class DisplayListRenderMethod implements RenderMethod {
 	
 	geo = (GeometryArrayRetained)ra.geometry();
 	if ((rm.primaryRenderAtomList != null) &&
-                (rm.texCoordSetMapLen <= cv.numTexCoordSupported)) {
+                (rm.texCoordSetMapLen <= cv.maxTexCoordSets)) {
 
 	    id = ra.renderAtom.dlistIds[ra.index];
 	    cv.newDisplayList(cv.ctx, id);
diff --git a/src/classes/share/javax/media/j3d/ExceptionStrings.properties b/src/classes/share/javax/media/j3d/ExceptionStrings.properties
index c3e8674..b1541ba 100644
--- a/src/classes/share/javax/media/j3d/ExceptionStrings.properties
+++ b/src/classes/share/javax/media/j3d/ExceptionStrings.properties
@@ -153,6 +153,12 @@ BranchGroup0=Cannot compile a live BranchGroup
 BranchGroup1=BranchGroup: no capability to detach
 BranchGroup2=Group: no capability to write children
 BranchGroup3=Picking can only work if BranchGroup is alive
+BranchGroup4=BranchGroup: Mode has to be either PickInfo.PICK_BOUNDS or PickInfo.PICK_GEOMETRY
+BranchGroup5=BranchGroup: Mode can't be PickInfo.PICK_GEOMETRY if PickShape is PickPoint
+BranchGroup6=BranchGroup: CLOSEST_GEOM_INFO and ALL_GEOM_INFO can't be set together.
+BranchGroup7=BranchGroup: Mode can't be PICK_BOUNDS if geometry information is needed
+BranchGroup8=BranchGroup: PickShape can't be PickBounds if geometry information is needed
+BranchGroup9=BranchGroup: Cannot call picking under a SharedGroup node
 CachedFrustum0=Frustum must have aleast 6 planes
 CachedFrustum1=Frustum must have  6 planes
 Clip0=Clip: no capability to set back distance
@@ -172,7 +178,7 @@ CompressedGeometry5=CompressedGeometry: no capability to get geometry
 CompressedGeometry6=CompressedGeometry: no capability to get data reference
 CompressedGeometry7=CompressedGeometry: cannot directly access data in byReference mode
 CompressedGeometry8=CompressedGeometry: must be in byReference mode to use this method
-CompressedGeometry9=CompressedGeometry: NIO buffer support is not currently implemented
+CompressedGeometry9=CompressedGeometry: NIO buffer support is not implemented
 ClipRetained0=Clip: Immediate mode clip may not be in scene graph
 ClipRetained1=Clip: illegal node under Background geometry Branch
 ClipRetained2=Clip: illegal node under SharedGroup Branch
@@ -268,7 +274,7 @@ GeometryArray103=GeometryArray: initial tex coord index + valid vertex count > v
 GeometryArray104=GeometryArray: initial coord index + valid vertex count > vertex count
 GeometryArray105=GeometryArray: must not be in BY_REFERENCE mode to use this method
 GeometryArray106=GeometryArray: texCoord set mapping is not specified
-GeometryArray107=GeometryArray: must specify at least one set of tex coord
+GeometryArray107=GeometryArray: must specify at least one set of texture coordinates
 GeometryArray108=GeometryArray: invalid texCoord set mapping
 GeometryArray109=GeometryArray: must be in TEXTURE_COORDINATE_4 mode to use this method
 GeometryArray110=GeometryArray: validVertexCount should be greater than or equal to zero
@@ -285,6 +291,18 @@ GeometryArray120=GeometryArray: must be direct nio buffer
 GeometryArray121=GeometryArray: None of the TEXTURE_COORDINATE bits are set in vertexFormat 
 GeometryArray122=GeometryArray: NORMALS bit is not set in vertexFormat 
 GeometryArray123=GeometryArray: None of the COLOR bits are set in vertexFormat 
+GeometryArray124=GeometryArray: texCoordSetCount < 0
+GeometryArray125=GeometryArray: vertexAttrCount < 0
+GeometryArray126=GeometryArray: no capability to set vertex attributes
+GeometryArray127=GeometryArray: no capability to read vertex attributes
+GeometryArray128=GeometryArray: VERTEX_ATTRIBUTES flag must not be set in INTERLEAVED mode
+GeometryArray129=GeometryArray: vertex attr array length is incorrect
+GeometryArray130=GeometryArray: initial vertex attr index + valid vertex count > vertex count
+GeometryArray131=GeometryArray: vertexAttrCount > 0, but VERTEX_ATTRIBUTES flag is not set
+GeometryArray132=GeometryArray: vertexAttrCount != vertexAttrSizes.length
+GeometryArray133=GeometryArray: vertexAttrSize value out of range
+GeometryArray134=GeometryArray: vertexAttrSize invalid for this method
+GeometryArray135=GeometryArray: USE_COORD_INDEX_ONLY bit cannot be set for non-indexed geometry
 GeometryDecompressor0=GeometryDecompressor: start+length > data array size
 GeometryDecompressor1=GeometryDecompressor: bad delta normal in compressed buffer
 GeometryDecompressorRetained0=GeometryDecompressorRetained: bad buffer data type
@@ -318,6 +336,7 @@ GeometryStripArray4=GeometryStripArray: initial color index + valid vertex count
 GeometryStripArray5=GeometryStripArray: initial normal index + valid vertex count > vertex count
 GeometryStripArray6=GeometryStripArray: initial tex coord index + valid vertex count > vertex count
 GeometryStripArray7=GeometryStripArray: initial coord index + valid vertex count > vertex count
+GeometryStripArray8=GeometryStripArray: initial vertex attr index + valid vertex count > vertex count
 GraphicsContext3D11=Background: Scene Graph background may not be in immediate mode
 GraphicsContext3D12=Fog: Scene Graph fog may not be in immediate mode
 GraphicsContext3D13=GraphicsContext3D: Light object is null
@@ -358,6 +377,11 @@ Locale0=Locale.addBranchGraph: Branch Group already has a parent
 Locale1=Locale: no capability to detach BranchGroup
 Locale3=Locale.replaceBranchGraph: Branch Group already has a parent
 Locale4=Locale has been removed from its VirtualUniverse
+Locale5=Locale: Mode has to be either PickInfo.PICK_BOUNDS or PickInfo.PICK_GEOMETRY
+Locale6=Locale: Mode can't be PickInfo.PICK_GEOMETRY if PickShape is PickPoint
+Locale7=Locale: CLOSEST_GEOM_INFO and ALL_GEOM_INFO can't be set together.
+Locale8=Locale: Mode can't be PICK_BOUNDS if geometry information is needed
+Locale9=Locale: PickShape can't be PickBounds if geometry information is needed
 IndexedLineStripArray0=IndexedLineStripArray: illegal vertexCount
 IndexedLineStripArray1=IndexedLineStripArray: illegal indexCount
 IndexedGeometryArray0=IndexedGeometryArray: no capability to get index count
@@ -381,12 +405,11 @@ IndexedGeometryArray24=IndexedGeometryArray: index color value greater than the
 IndexedGeometryArray25=IndexedGeometryArray: index texcoord value greater than the array length
 IndexedGeometryArray26=IndexedGeometryArray: index normal value greater than the array length
 IndexedGeometryArray27=IndexedGeometryArray: index value less than zero
+IndexedGeometryArray28=IndexedGeometryArray: no capability to set vertex attribute index
+IndexedGeometryArray29=IndexedGeometryArray: no capability to get vertex attribute index
+IndexedGeometryArray30=IndexedGeometryArray: index vertexAttr value greater than the array length
 IndexedLineArray0=IndexedLineArray: illegal vertexCount
 IndexedLineArray1=IndexedLineArray: illegal indexCount
-IndexedGeometryArrayRetained0=execute() called on indexed geometry
-IndexedGeometryArrayRetained1=WARNING: Memory redundantly allocated for Color Indices array since the USE_COORD_INDEX_ONLY flag has been specified. This will throw a NPE in a subsequent version
-IndexedGeometryArrayRetained2=WARNING: Memory redundantly allocated for Normal Indices array since the USE_COORD_INDEX_ONLY flag has been specified. This will throw a NPE in a subsequent version
-IndexedGeometryArrayRetained3=WARNING: Memory redundantly allocated for TextureCoordinate Indices array since the USE_COORD_INDEX_ONLY flag has been specified. This will throw a NPE in a subsequent version
 IndexedGeometryStripArray0=IndexedGeometryStripArray: no capability to get number of strips
 IndexedGeometryStripArray1=IndexedGeometryStripArray: no capability to get strip index counts
 IndexedGeometryStripArray2=IndexedGeometryStripArray: no capability to set strip index counts
@@ -466,14 +489,15 @@ MorphRetained2=Morph: All GeometryArrays must be of same type
 MorphRetained5=Invalid SceneGraphPath encountered : localToVworld is null.
 MorphRetained7=Morph: number of weights not same as number of GeometryArrays
 MorphRetained8=Morph: sum of all weights is NOT 1.0
-Node0=Cannot get the parent of a live or compiled node
+MorphRetained9=Morph: vertex attributes are not supported
+Node0=Node: no capability to read parent
 Node1=Node: no capability to set bounds
 Node2=Node: no capability to read user bounds
 Node3=Node: no capability to read Pickable
 Node4=Node: no capability to set Collidable
 Node5=Node: no capability to set user auto compute bounds
 Node6=Node: no capability to read user auto compute bounds
-Node7=Node: local to vworld transform is undefined for a node that is not part of a live scene graph
+Node7=Node: local to vworld transform is undefined for a node that is compiled but not live
 Node8=Node: no capability to read local to vworld transform
 Node9=Node: Invalid geometric bounds
 Node11=cloneTree: should be overridden in child
@@ -482,6 +506,14 @@ Node13=Node: Cannot clone a live or compiled scenegraph
 Node14=Node: no capability to set Pickable
 Node15=Node: Cannot compile, clone or getBounds on a scene graph that contains a cycle.
 Node16=Node: no capability to read Collidable
+Node17=Node: no capability to read locale
+PickInfo0=PickInfo: PICK_GEOMETRY mode - no capability to ALLOW_GEOMETRY_READ
+PickInfo1=PickInfo: PICK_GEOMETRY mode - no capability to ALLOW_INTERSECT
+PickInfo2=PickInfo: PICK_GEOMETRY mode - no capability to ALLOW_COORDINATE_READ
+PickInfo3=PickInfo: PICK_GEOMETRY mode - no capability to ALLOW_COUNT_READ
+PickInfo4=PickInfo: PICK_GEOMETRY mode - no capability to ALLOW_FORMAT_READ
+PickInfo5=PickInfo: PICK_GEOMETRY mode - no capability to ALLOW_COORDINATE_INDEX_READ
+PickInfo6=PickInfo: PICK_GEOMETRY mode - no capability to ALLOW_GEOMETRY_ARRAY_READ
 Picking0=Cannot call picking under a SharedGroup node
 Picking2=Picking: Node has no parent and locale. This is illegal!
 NodeComponent0=NodeComponent:cloneNodeComponent must be defined in subclass
@@ -572,6 +604,10 @@ RenderingAttributes10=RenderingAttributes: no capability to set raster op
 RenderingAttributes11=RenderingAttributes: no capability to get raster op
 RenderingAttributes12=RenderingAttributes: no capability to set ignore vertex colors flag
 RenderingAttributes13=RenderingAttributes: no capability to get ignore vertex colors flag
+RenderingAttributes14=RenderingAttributes: no capability to set depth test function
+RenderingAttributes15=RenderingAttributes: no capability to get depth test function
+RenderingAttributes16=RenderingAttributes: no capability to set stencil attributes
+RenderingAttributes17=RenderingAttributes: no capability to get stencil attributes
 TriangleStripArrayRetained0=PickPoint doesn't make sense for geometry-based picking. Java 3D doesn't have spatial information of the surface. Should use PickBounds with BoundingSphere and set radius to a epsilon tolerance.
 TriangleStripArrayRetained1=stripVertexCounts element less than 3
 RotationPathInterpolator0=RotationPathInterpolator: length of knots and quats must be of the same length
@@ -891,6 +927,26 @@ ModelClip14=ModelClip: no capability to read influencing bounding leaf
 ModelClipRetained1=ModelClip: illegal node under SharedGroup Branch
 MasterControl0=OpenGL is not MT safe
 MasterControl1=Green threads are not supported
+MasterControl2=NOTE: simulated multi-texture will not work for programmable shaders
+MasterControl3=and will be removed entirely in the next release of Java 3D
 J3DBuffer0=Native access to NIO buffer not supported
 J3DBuffer1=NIO buffer must be a direct buffer
 J3DBuffer2=NIO buffer must match native byte order of underlying platform
+GLSLShaderProgram0=GLSLShaderProgram: no capability to read names
+GLSLShaderProgram1=GLSLShaderProgram: no capability to read shaders
+GLSLShaderProgram2=GLSLShaderProgram: Shader has incompatible shading language
+CgShaderProgram0=CgShaderProgram: no capability to read names
+CgShaderProgram1=CgShaderProgram: no capability to read shaders
+CgShaderProgram2=CgShaderProgram: Shader has incompatible shading language
+CgShaderProgram3=CgShaderProgram: must not specify more than one vertex shader
+CgShaderProgram4=CgShaderProgram: must not specify more than one fragment shader
+ShaderAppearance0=ShaderAppearance: no capability to set shader program
+ShaderAppearance1=ShaderAppearance: no capability to get shader program
+ShaderAppearance2=ShaderAppearance: no capability to set shader attribute set
+ShaderAppearance3=ShaderAppearance: no capability to get shader attribute set
+ShaderAttributeBinding0=ShaderAttributeBinding is not supported
+ShaderProgramRetained0=Shading language not supported
+ShaderAttributeObject0=ShaderAttributeObject: no capability to get value
+ShaderAttributeObject1=ShaderAttributeObject: no capability to set value
+ShaderAttributeSet0=ShaderAttributeSet: no capability to get attribute
+ShaderAttributeSet1=ShaderAttributeSet: no capability to set attribute
diff --git a/src/classes/share/javax/media/j3d/ExponentialFog.java b/src/classes/share/javax/media/j3d/ExponentialFog.java
index 75b17e1..9d95a32 100644
--- a/src/classes/share/javax/media/j3d/ExponentialFog.java
+++ b/src/classes/share/javax/media/j3d/ExponentialFog.java
@@ -46,6 +46,11 @@ public class ExponentialFog extends Fog {
     public static final int
     ALLOW_DENSITY_WRITE = CapabilityBits.EXPONENTIAL_FOG_ALLOW_DENSITY_WRITE;
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+	ALLOW_DENSITY_READ
+    };
+
     /**
      * Constructs an ExponentialFog node with default parameters.
      * The default values are as follows:
@@ -55,6 +60,8 @@ public class ExponentialFog extends Fog {
      */
     public ExponentialFog() {
 	// Just use the defaults
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -63,6 +70,9 @@ public class ExponentialFog extends Fog {
      */
     public ExponentialFog(Color3f color) {
 	super(color);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -73,6 +83,10 @@ public class ExponentialFog extends Fog {
      */
     public ExponentialFog(Color3f color, float density) {
 	super(color);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
 	((ExponentialFogRetained)this.retained).initDensity(density);
     }
 
@@ -84,6 +98,9 @@ public class ExponentialFog extends Fog {
      */
     public ExponentialFog(float r, float g, float b) {
 	super(r, g, b);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -96,6 +113,10 @@ public class ExponentialFog extends Fog {
      */
     public ExponentialFog(float r, float g, float b, float density) {
 	super(r, g, b);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
 	((ExponentialFogRetained)this.retained).initDensity(density);
     }
 
diff --git a/src/classes/share/javax/media/j3d/ExponentialFogRetained.java b/src/classes/share/javax/media/j3d/ExponentialFogRetained.java
index 512e521..b82efd9 100644
--- a/src/classes/share/javax/media/j3d/ExponentialFogRetained.java
+++ b/src/classes/share/javax/media/j3d/ExponentialFogRetained.java
@@ -21,7 +21,10 @@ import java.util.ArrayList;
  */
 class ExponentialFogRetained extends FogRetained {
     // Fog density
-    float density = 1.0f;
+    private float density = 1.0f;
+
+    // Issue 144: density in Eye Coordinates (EC)
+    private float densityInEc;
 
     // dirty bits for ExponentialFog
     static final int DENSITY_CHANGED	= FogRetained.LAST_DEFINED_BIT << 1;
@@ -108,7 +111,9 @@ class ExponentialFogRetained extends FogRetained {
     native void update(long ctx, float red, float green, float blue, float density);
 
     void update(long ctx, double scale) {
-	update(ctx, color.x, color.y, color.z, density);
+        // Issue 144: recompute the density in EC, and send it to native code
+	validateDistancesInEc(scale);
+	update(ctx, color.x, color.y, color.z, densityInEc);
     }
 
 
@@ -128,6 +133,8 @@ class ExponentialFogRetained extends FogRetained {
 	    ((ExponentialFogRetained)mirrorFog).density = ((Float)((Object[])objs[4])[4]).floatValue();
 	    
 	}
+        // Issue 144: store the local to vworld scale used to transform the density
+	((ExponentialFogRetained)mirrorFog).setLocalToVworldScale(getLastLocalToVworld().getDistanceScale());	
 
 	super.updateMirrorObject(objs);
     }
@@ -142,6 +149,18 @@ class ExponentialFogRetained extends FogRetained {
  
          return efr;
     }
-  
+
+    // Issue 144: method to recompute the density in EC by multiplying the specified
+    // density by the inverse of the local to EC scale
+    /**
+     * Scale distances from local to eye coordinate.
+     */
+    protected void validateDistancesInEc(double vworldToCoexistenceScale) {
+        // vworldToCoexistenceScale can be used here since
+        // CoexistenceToEc has a unit scale
+        double localToEcScale = getLocalToVworldScale() * vworldToCoexistenceScale;
+
+        densityInEc = (float)(density / localToEcScale);
+    }
 
 }
diff --git a/src/classes/share/javax/media/j3d/Fog.java b/src/classes/share/javax/media/j3d/Fog.java
index cb5e517..ffd2292 100644
--- a/src/classes/share/javax/media/j3d/Fog.java
+++ b/src/classes/share/javax/media/j3d/Fog.java
@@ -77,6 +77,13 @@ public abstract class Fog extends Leaf {
     public static final int
     ALLOW_SCOPE_WRITE = CapabilityBits.FOG_ALLOW_SCOPE_WRITE;
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_INFLUENCING_BOUNDS_READ,
+        ALLOW_COLOR_READ,
+        ALLOW_SCOPE_READ        
+    };
+    
     /**
      * Constructs a Fog node with default parameters.  The default
      * values are as follows:
@@ -89,6 +96,8 @@ public abstract class Fog extends Leaf {
      */
     public Fog() {
 	// Just use the defaults
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -96,7 +105,10 @@ public abstract class Fog extends Leaf {
      * @param color the fog color
      */
     public Fog(Color3f color) {
-	((FogRetained)this.retained).initColor(color);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((FogRetained)this.retained).initColor(color);
     }
 
     /**
@@ -106,7 +118,10 @@ public abstract class Fog extends Leaf {
      * @param b the blue component of the fog color
      */
     public Fog(float r, float g, float b) {
-	((FogRetained)this.retained).initColor(r, g, b);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((FogRetained)this.retained).initColor(r, g, b);
     }
 
     /**
diff --git a/src/classes/share/javax/media/j3d/FogRetained.java b/src/classes/share/javax/media/j3d/FogRetained.java
index 9c84fd5..9ab5ec6 100644
--- a/src/classes/share/javax/media/j3d/FogRetained.java
+++ b/src/classes/share/javax/media/j3d/FogRetained.java
@@ -90,6 +90,9 @@ abstract class FogRetained extends LeafRetained{
     // Is true, if the mirror fog is viewScoped
     boolean isViewScoped = false;
 
+    // Scale value extracted from localToVworld transform
+    private double localToVworldScale = 1.0;
+
     FogRetained() {
 	localBounds = new BoundingBox();
 	((BoundingBox)localBounds).setLower( 1.0, 1.0, 1.0);
@@ -750,6 +753,8 @@ abstract class FogRetained extends LeafRetained{
      }
 
     void updateTransformChange() {
+	super.updateTransformChange();
+        setLocalToVworldScale(sgFog.getLastLocalToVworld().getDistanceScale());
     }
 
     // Called on mirror object
@@ -785,5 +790,19 @@ abstract class FogRetained extends LeafRetained{
     void getMirrorObjects(ArrayList leafList, HashKey key) {
 	leafList.add(mirrorFog);
     }
-    
+
+    /**
+     * Scale distances from local to eye coordinate
+     */
+    protected void validateDistancesInEc(double vworldToCoexistenceScale) {
+        assert false : "subclasses should override this method";
+    }
+
+    double getLocalToVworldScale() {
+        return localToVworldScale;
+    }
+
+    void setLocalToVworldScale(double localToVworldScale) {
+        this.localToVworldScale = localToVworldScale;
+    }
 }
diff --git a/src/classes/share/javax/media/j3d/Font3D.java b/src/classes/share/javax/media/j3d/Font3D.java
index 5e440f8..d706abe 100644
--- a/src/classes/share/javax/media/j3d/Font3D.java
+++ b/src/classes/share/javax/media/j3d/Font3D.java
@@ -178,6 +178,49 @@ public class Font3D extends NodeComponent {
       bounds.setUpper(upper);
     }
 
+    // BY MIK OF CLASSX
+    /**
+     * Returns a GeometryArray of a glyph in this Font3D.
+     *
+     * @param c character from which to generate a tessellated glyph.
+     *
+     * @return a GeometryArray
+     *
+     * @since Java 3D 1.4
+     */
+    public GeometryArray getGlyphGeometry(char c) {
+        char code[] = { c };
+        GlyphVector gv = font.createGlyphVector(frc, code);
+
+        // triangulate the glyph
+        GeometryArrayRetained glyph_gar = triangulateGlyphs(gv, code[0]);
+
+        // Assume that triangulateGlyphs returns a triangle array with only coords & normals
+        // (and without by-ref, interleaved, etc.)
+        assert glyph_gar instanceof TriangleArrayRetained :
+            "Font3D: GeometryArray is not an instance of TrangleArray";
+        assert glyph_gar.getVertexFormat() == (GeometryArray.COORDINATES | GeometryArray.NORMALS) :
+            "Font3D: Illegal GeometryArray format -- only coordinates and normals expected";
+
+        // create a correctly sized TriangleArray
+        TriangleArray ga = new TriangleArray(glyph_gar.getVertexCount(),glyph_gar.getVertexFormat());
+        
+        // temp storage for coords, normals
+        float tmp[] = new float[3];
+
+        int vertexCount = ga.getVertexCount();
+        for(int i=0; i<vertexCount; i++) {
+            // copy the glyph geometry to the TriangleArray
+            glyph_gar.getCoordinate(i,tmp);
+            ga.setCoordinate(i,tmp);
+
+            glyph_gar.getNormal(i,tmp);
+            ga.setNormal(i,tmp);
+        }
+
+        return ga;
+    }
+
 
   // Triangulate glyph with 'unicode' if not already done.
     GeometryArrayRetained triangulateGlyphs(GlyphVector gv, char c) {
@@ -415,7 +458,7 @@ public class Font3D extends NodeComponent {
 	      }
 	    }
 
-	    // TODO: Should use IndexedTriangleArray to avoid 
+	    // XXXX: Should use IndexedTriangleArray to avoid 
 	    // duplication of vertices. To create triangles for
 	    // side faces, every vertex is duplicated currently.
 	    TriangleArray triAry = new TriangleArray(vertCnt,
diff --git a/src/classes/share/javax/media/j3d/FreeListManager.java b/src/classes/share/javax/media/j3d/FreeListManager.java
index f5c10c5..0233669 100644
--- a/src/classes/share/javax/media/j3d/FreeListManager.java
+++ b/src/classes/share/javax/media/j3d/FreeListManager.java
@@ -25,10 +25,9 @@ class FreeListManager {
     static final int DISPLAYLIST = 4;
     static final int TEXTURE2D = 5;
     static final int TEXTURE3D = 6;
-    static final int CANVASBIT = 7;
-    static final int VECTOR3D = 8;
-    static final int POINT3D = 9;
-    static int MAXINT = 9;
+    static final int VECTOR3D = 7;
+    static final int POINT3D = 8;
+    static int MAXINT = 8;
     
     // what list we are going to shrink next
     private static int currlist = 0;
@@ -44,7 +43,6 @@ class FreeListManager {
 	freelist[DISPLAYLIST] = new IntegerFreeList();
 	freelist[TEXTURE2D] = new IntegerFreeList();
 	freelist[TEXTURE3D] = new IntegerFreeList();
-	freelist[CANVASBIT] = new IntegerFreeList();
 	freelist[POINT3D] = new MemoryFreeList("javax.vecmath.Point3d");
 	freelist[VECTOR3D] = new MemoryFreeList("javax.vecmath.Vector3d");
     }
diff --git a/src/classes/share/javax/media/j3d/GLSLShaderProgram.java b/src/classes/share/javax/media/j3d/GLSLShaderProgram.java
new file mode 100644
index 0000000..5608cb8
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GLSLShaderProgram.java
@@ -0,0 +1,158 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The GLSLShaderProgram object is a concrete implementation of a
+ * ShaderProgram node component for the OpenGL GLSL shading language.
+ *
+ * @see SourceCodeShader
+ *
+ * @since Java 3D 1.4
+ */
+
+public class GLSLShaderProgram extends ShaderProgram {
+
+    /**
+     * Constructs a GLSL shader program node component.
+     *
+     * <br>
+     * TODO: ADD MORE DOCUMENTATION HERE.
+     */
+    public GLSLShaderProgram() {
+    }
+
+    // Implement abstract setVertexAttrNames method (inherit javadoc from parent class)
+    public void setVertexAttrNames(String[] vertexAttrNames) {
+	checkForLiveOrCompiled();
+
+        if (vertexAttrNames != null) {
+            for (int i = 0; i < vertexAttrNames.length; i++) {
+                if (vertexAttrNames[i] == null) {
+                    throw new NullPointerException();
+                }
+            }
+        }
+
+        ((GLSLShaderProgramRetained)this.retained).setVertexAttrNames(vertexAttrNames);
+    }
+
+    // Implement abstract getVertexAttrNames method (inherit javadoc from parent class)
+    public String[] getVertexAttrNames() {
+
+	if (isLiveOrCompiled()) {
+	    if(!this.getCapability(ALLOW_NAMES_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GLSLShaderProgram0"));
+	    }
+	}
+
+ 	return ((GLSLShaderProgramRetained)this.retained).getVertexAttrNames();
+
+    }
+
+    // Implement abstract setShaderAttrNames method (inherit javadoc from parent class)
+    public void setShaderAttrNames(String[] shaderAttrNames) {
+	checkForLiveOrCompiled();
+
+        if (shaderAttrNames != null) {
+            for (int i = 0; i < shaderAttrNames.length; i++) {
+                if (shaderAttrNames[i] == null) {
+                    throw new NullPointerException();
+                }
+            }
+        }
+
+        ((GLSLShaderProgramRetained)this.retained).setShaderAttrNames(shaderAttrNames);
+    }
+
+    // Implement abstract getShaderAttrNames method (inherit javadoc from parent class)
+    public String[] getShaderAttrNames() {
+
+	if (isLiveOrCompiled()) {
+	    if(!this.getCapability(ALLOW_NAMES_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GLSLShaderProgram0"));
+	    }
+	}
+
+ 	return ((GLSLShaderProgramRetained)this.retained).getShaderAttrNames();
+
+    }
+
+    /**
+     * Copies the specified array of shaders into this shader
+     * program. This method makes a shallow copy of the array. The
+     * array of shaders may be null or empty (0 length), but the
+     * elements of the array must be non-null. The shading language of
+     * each shader in the array must be
+     * <code>SHADING_LANGUAGE_GLSL</code>. Each shader in the array must
+     * be a SourceCodeShader.
+     *
+     * @param shaders array of Shader objects to be copied into this
+     * ShaderProgram
+     *
+     * @exception CapabilityNotSetException if appropriate capability is
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @exception IllegalArgumentException if the shading language of
+     * any shader in the shaders array is <em>not</em>
+     * <code>SHADING_LANGUAGE_GLSL</code>.
+     *
+     * @exception ClassCastException if any shader in the shaders
+     * array is <em>not</em> a SourceCodeShader.
+     *
+     * @exception NullPointerException if any element in the
+     * shaders array is null.
+     */
+    public void setShaders(Shader[] shaders) {
+	checkForLiveOrCompiled();
+        
+        if(shaders != null) {
+            // Check shaders for valid shading language and class type
+            for (int i = 0; i < shaders.length; i++) {
+                if (shaders[i].getShadingLanguage() != Shader.SHADING_LANGUAGE_GLSL) {
+                    throw new IllegalArgumentException(J3dI18N.getString("GLSLShaderProgram2"));
+                }
+                
+                // Try to cast shader to SourceCodeShader; it will throw
+                // ClassCastException if it isn't.
+                SourceCodeShader shad = (SourceCodeShader)shaders[i];
+            }
+            
+        }
+        
+ 	((GLSLShaderProgramRetained)this.retained).setShaders(shaders);
+    }
+
+    // Implement abstract getShaders method (inherit javadoc from parent class)
+    public Shader[] getShaders() {
+
+	if (isLiveOrCompiled()) {
+	    if(!this.getCapability(ALLOW_SHADERS_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GLSLShaderProgram1"));
+	    }
+	}
+
+ 	return ((GLSLShaderProgramRetained)this.retained).getShaders();
+    }
+
+    /**
+     * Creates a retained mode GLSLShaderProgramRetained object that this
+     * GLSLShaderProgram component object will point to.
+     */
+    void createRetained() {
+	this.retained = new GLSLShaderProgramRetained();
+	this.retained.setSource(this);
+    }
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/GLSLShaderProgramRetained.java b/src/classes/share/javax/media/j3d/GLSLShaderProgramRetained.java
new file mode 100644
index 0000000..7d4e10b
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GLSLShaderProgramRetained.java
@@ -0,0 +1,281 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The GLSLShaderProgram object is a concrete implementation of a
+ * ShaderProgram node component for the OpenGL GLSL shading language.
+ *
+ * @see SourceCodeShader
+ *
+ * @since Java 3D 1.4
+ */
+
+class GLSLShaderProgramRetained extends ShaderProgramRetained {
+    
+    /**
+     * Constructs a GLSL shader program node component.
+     *
+     * <br>
+     * TODO: ADD MORE DOCUMENTATION HERE.
+     */
+    GLSLShaderProgramRetained() {
+    }
+
+    synchronized void createMirrorObject() {
+	// System.out.println("GLSLShaderProgramRetained : createMirrorObject");
+        // This method should only call by setLive().
+	if (mirror == null) {
+	    GLSLShaderProgramRetained  mirrorGLSLSP = new GLSLShaderProgramRetained();	    
+	    mirror = mirrorGLSLSP;
+	    mirror.source = source;
+	}
+	initMirrorObject();
+    }
+
+    // ShaderAttributeValue methods
+
+    native ShaderError setUniform1i(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    int value);
+    
+    native ShaderError setUniform1f(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    float value);
+    
+    native ShaderError setUniform2i(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    int[] value);
+    
+    native ShaderError setUniform2f(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    float[] value);
+    
+    native ShaderError setUniform3i(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    int[] value);
+    
+    native ShaderError setUniform3f(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    float[] value);    
+    
+    native ShaderError setUniform4i(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    int[] value);
+    
+    native ShaderError setUniform4f(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    float[] value);    
+    
+    native ShaderError setUniformMatrix3f(long ctx,
+					   long shaderProgramId,
+				           long uniformLocation,
+					   float[] value);
+
+    native ShaderError setUniformMatrix4f(long ctx,
+					   long shaderProgramId,
+			         	   long uniformLocation,
+					   float[] value);
+    
+    // ShaderAttributeArray methods
+
+    native ShaderError setUniform1iArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      int[] value);
+    
+    native ShaderError setUniform1fArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      float[] value);
+    
+    native ShaderError setUniform2iArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      int[] value);
+    
+    native ShaderError setUniform2fArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      float[] value);
+    
+    native ShaderError setUniform3iArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      int[] value);
+    
+    native ShaderError setUniform3fArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      float[] value);    
+    
+    native ShaderError setUniform4iArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      int[] value);
+    
+    native ShaderError setUniform4fArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      float[] value);    
+    
+    native ShaderError setUniformMatrix3fArray(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    int numElements,
+					    float[] value);
+
+    native ShaderError setUniformMatrix4fArray(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    int numElements,
+					    float[] value);
+    
+    /* New native interfaces */
+    private native ShaderError createNativeShader(long ctx, int shaderType, long[] shaderId);
+    private native ShaderError destroyNativeShader(long ctx, long shaderId);
+    private native ShaderError compileNativeShader(long ctx, long shaderId, String program);
+
+    private native ShaderError createNativeShaderProgram(long ctx, long[] shaderProgramId);
+    private native ShaderError destroyNativeShaderProgram(long ctx, long shaderProgramId);
+    private native ShaderError linkNativeShaderProgram(long ctx, long shaderProgramId,
+						       long[] shaderId);
+    private native ShaderError bindNativeVertexAttrName(long ctx, long shaderProgramId,
+                                                        String attrName, int attrIndex);
+    private native void lookupNativeShaderAttrNames(long ctx, long shaderProgramId,
+            int numAttrNames, String[] attrNames, long[] locArr,
+            int[] typeArr, int[] sizeArr, boolean[] isArrayArr);
+    
+    private native ShaderError useShaderProgram(long ctx, long shaderProgramId);
+ 
+    /**
+     * Method to return a flag indicating whether this
+     * ShaderProgram is supported on the specified Canvas.
+     */
+    boolean isSupported(Canvas3D cv) {
+        return cv.shadingLanguageGLSL;
+    }
+
+    /**
+     * Method to create the native shader.
+     */
+    ShaderError createShader(long ctx, ShaderRetained shader, long[] shaderIdArr) {	
+	  return  createNativeShader(ctx, shader.shaderType, shaderIdArr);
+    }
+    
+    /**
+     * Method to destroy the native shader.
+     */
+    ShaderError destroyShader(long ctx, long shaderId) {
+	return destroyNativeShader(ctx, shaderId);
+    }
+    
+    /**
+     * Method to compile the native shader.
+     */
+    ShaderError compileShader(long ctx, long shaderId, String source) {
+        return compileNativeShader(ctx, shaderId, source );
+    }
+
+    /**
+     * Method to create the native shader program.
+     */
+    ShaderError createShaderProgram(long ctx, long[] shaderProgramIdArr) {
+	    return createNativeShaderProgram(ctx, shaderProgramIdArr);  
+    }
+
+    /**
+     * Method to destroy the native shader program.
+     */
+    ShaderError destroyShaderProgram(long ctx, long shaderProgramId) {
+        return destroyNativeShaderProgram(ctx, shaderProgramId);
+    }
+
+    /**
+     * Method to link the native shader program.
+     */
+    ShaderError linkShaderProgram(long ctx, long shaderProgramId, long[] shaderIds) {
+        return linkNativeShaderProgram(ctx, shaderProgramId, shaderIds);
+    }
+ 
+    ShaderError bindVertexAttrName(long ctx, long shaderProgramId, String attrName, int attrIndex) {
+        return bindNativeVertexAttrName(ctx, shaderProgramId, attrName, attrIndex);
+    }
+
+    void lookupVertexAttrNames(long ctx, long shaderProgramId, String[] attrNames, boolean[] errArr) {
+        // This method is a no-op for GLSL
+    }
+
+    void lookupShaderAttrNames(long ctx, long shaderProgramId,
+            String[] attrNames, AttrNameInfo[] attrNameInfoArr) {
+
+        int numAttrNames = attrNames.length;
+        
+        long[] locArr = new long[numAttrNames];
+        int[] typeArr = new int[numAttrNames];
+        int[] sizeArr = new int[numAttrNames]; // currently unused
+        boolean[] isArrayArr = new boolean[numAttrNames];
+
+        // Initialize loc array to -1 (indicating no location)
+        for (int i = 0; i < numAttrNames; i++) {
+            locArr[i] = -1;
+        }
+
+        lookupNativeShaderAttrNames(ctx, shaderProgramId,
+                numAttrNames, attrNames, locArr, typeArr, sizeArr, isArrayArr);
+
+        for (int i = 0; i < numAttrNames; i++) {
+            attrNameInfoArr[i] = new AttrNameInfo();
+            attrNameInfoArr[i].setLocation(locArr[i]);
+            attrNameInfoArr[i].setArray(isArrayArr[i]);
+            attrNameInfoArr[i].setType(typeArr[i]);
+//            System.err.println(attrNames[i] +
+//                    " : loc = " + locArr[i] +
+//                    ", type = " + typeArr[i] +
+//                    ", isArray = " + isArrayArr[i] +
+//                    ", size = " + sizeArr[i]);
+        }
+    }
+    
+    /**
+     * Method to enable the native shader program.
+     */
+    ShaderError enableShaderProgram(long ctx, long shaderProgramId) {
+	return useShaderProgram(ctx, shaderProgramId);
+    }
+	
+    /**
+     * Method to disable the native shader program.
+     */
+    ShaderError disableShaderProgram(long ctx) {
+	return useShaderProgram(ctx, 0);
+    }
+
+
+}
diff --git a/src/classes/share/javax/media/j3d/Geometry.java b/src/classes/share/javax/media/j3d/Geometry.java
index 042b366..89391b1 100644
--- a/src/classes/share/javax/media/j3d/Geometry.java
+++ b/src/classes/share/javax/media/j3d/Geometry.java
@@ -32,14 +32,22 @@ package javax.media.j3d;
 public abstract class Geometry extends NodeComponent {
 
     /**
-     * Specifies that this Geometry allows intersect operation. 
+     * Specifies that this Geometry allows intersect operation. This
+     * capability bit is set (true) by default for all Geometry objects.
      */
     public static final int
     ALLOW_INTERSECT = CapabilityBits.GEOMETRY_ALLOW_INTERSECT;
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_INTERSECT
+    };
+    
     /**
      * Constructs a new Geometry object.
      */
     public Geometry() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 }
diff --git a/src/classes/share/javax/media/j3d/GeometryArray.java b/src/classes/share/javax/media/j3d/GeometryArray.java
index 8851569..b784a11 100644
--- a/src/classes/share/javax/media/j3d/GeometryArray.java
+++ b/src/classes/share/javax/media/j3d/GeometryArray.java
@@ -17,7 +17,8 @@ import javax.vecmath.*;
 
 /**
  * The GeometryArray object contains separate arrays of positional
- * coordinates, colors, normals, and texture coordinates that
+ * coordinates, colors, normals, texture coordinates, and vertex
+ * attributes that
  * describe point, line, or polygon geometry.  This class is extended
  * to create the various primitive types (such as lines,
  * triangle strips, etc.).
@@ -29,7 +30,8 @@ import javax.vecmath.*;
  * <li>
  * <b>By Copying:</b>
  * The existing methods for setting positional coordinates, colors,
- * normals, and texture coordinates (such as <code>setCoordinate</code>,
+ * normals, texture coordinates, and vertex attributes
+ * (such as <code>setCoordinate</code>,
  * <code>setColors</code>, etc.)  copy the data into this
  * GeometryArray.  This is appropriate for many applications and
  * offers an application much flexibility in organizing its data.
@@ -41,9 +43,11 @@ import javax.vecmath.*;
  * this feature, set the <code>BY_REFERENCE</code> bit in the
  * <code>vertexFormat</code> field of the constructor for this
  * GeometryArray.  In this mode, the various set methods for
- * coordinates, normals, colors, and texture coordinates are not used.
+ * coordinates, normals, colors, texture coordinates, and vertex attributes
+ * are not used.
  * Instead, new methods are used to set a reference to user-supplied
- * coordinate, color, normal, and texture coordinate arrays (such as
+ * coordinate, color, normal, texture coordinate, and vertex attribute
+ * arrays (such as
  * <code>setCoordRefFloat</code>, <code>setColorRefFloat</code>,
  * etc.).  Data in any array that is referenced by a live or compiled
  * GeometryArray object may only be modified via the
@@ -132,6 +136,24 @@ public abstract class GeometryArray extends Geometry {
   public static final int
     ALLOW_TEXCOORD_WRITE = CapabilityBits.GEOMETRY_ARRAY_ALLOW_TEXCOORD_WRITE;
 
+  /**
+   * Specifies that this GeometryArray allows reading the array of
+   * vertex attributes.
+   *
+   * @since Java 3D 1.4
+   */
+  public static final int
+    ALLOW_VERTEX_ATTR_READ = CapabilityBits.GEOMETRY_ARRAY_ALLOW_VERTEX_ATTR_READ;
+
+  /**
+   * Specifies that this GeometryArray allows writing the array of
+   * vertex attributes.
+   *
+   * @since Java 3D 1.4
+   */
+  public static final int
+    ALLOW_VERTEX_ATTR_WRITE = CapabilityBits.GEOMETRY_ARRAY_ALLOW_VERTEX_ATTR_WRITE;
+
   /**
    * Specifies that this GeometryArray allows reading the count or
    * initial index information for this object.
@@ -268,15 +290,12 @@ public abstract class GeometryArray extends Geometry {
      * is only valid in conjunction with the <code>BY_REFERENCE</code>
      * flag.
      *
-     * <p>
-     * NOTE: Use of this class requires version 1.4 of the
-     * Java<sup><font size="-2">TM</font></sup>&nbsp;2 Platform.
-     *
      * @see J3DBuffer
      * @see #setCoordRefBuffer(J3DBuffer)
      * @see #setColorRefBuffer(J3DBuffer)
      * @see #setNormalRefBuffer(J3DBuffer)
      * @see #setTexCoordRefBuffer(int,J3DBuffer)
+     * @see #setVertexAttrRefBuffer(int,J3DBuffer)
      * @see #setInterleavedVertexBuffer(J3DBuffer)
      *
      * @since Java 3D 1.3
@@ -287,17 +306,30 @@ public abstract class GeometryArray extends Geometry {
      * Specifies that only the coordinate indices are used for indexed
      * geometry arrays.  In this mode, the values from the coordinate
      * index array are used as a single set of index values to access
-     * the vertex data for all four vertex components (coord, color,
-     * normal, and texCoord).  The color, normal, and texCoord index arrays
-     * are ignored.  This flag is only valid for indexed geometry arrays
+     * the vertex data for all five vertex components (coord, color,
+     * normal, texCoord, and vertexAttr).  The color, normal, texCoord,
+     * and vertexAttr index arrays are neither allocated nor used. Any
+     * attempt to access the color, normal, texCoord,
+     * or vertexAttr index arrays will result in a NullPointerException.
+     * This flag is only valid for indexed geometry arrays
      * (subclasses of IndexedGeometryArray).
      *
      * @since Java 3D 1.3
      */
     public static final int USE_COORD_INDEX_ONLY = 0x200;
 
+    /**
+     * Specifies that this GeometryArray contains one or more arrays of
+     * vertex attributes. These attributes are used in programmable
+     * shading.
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int VERTEX_ATTRIBUTES = 0x1000;
+
+
     // Used to keep track of the last bit (for adding new bits only)
-    private static final int LAST_FORMAT_BIT = 0x800;
+    private static final int LAST_FORMAT_BIT = 0x1000;
 
 
     // Scratch arrays for converting Point[234]f to TexCoord[234]f
@@ -306,11 +338,26 @@ public abstract class GeometryArray extends Geometry {
     private TexCoord4f [] texCoord4fArray = null;
     private TexCoord2f texCoord2fScratch = null;
     private TexCoord3f texCoord3fScratch = null;
-
- 
+    
+    private static final int[] defTexCoordMap = { 0 };
+
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_COLOR_READ,
+        ALLOW_COORDINATE_READ,
+        ALLOW_COUNT_READ,
+        ALLOW_FORMAT_READ,
+        ALLOW_NORMAL_READ,
+        ALLOW_REF_DATA_READ,
+        ALLOW_TEXCOORD_READ,
+        ALLOW_VERTEX_ATTR_READ        
+    };
+    
 
     // non-public, no parameter constructor
     GeometryArray() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
 
@@ -321,12 +368,15 @@ public abstract class GeometryArray extends Geometry {
      * <ul>
      * texCoordSetCount : 1<br>
      * texCoordSetMap : { 0 }<br>
+     * vertexAttrCount : 0<br>
+     * vertexAttrSizes : null<br>
      * validVertexCount : vertexCount<br>
      * initialVertexIndex : 0<br>
      * initialCoordIndex : 0<br>
      * initialColorIndex : 0<br>
      * initialNormalIndex : 0<br>
      * initialTexCoordIndex : 0<br>
+     * initialVertexAttrIndex : 0<br>
      * all data array values : 0.0<br>
      * all data array references : null<br>
      * </ul>
@@ -355,46 +405,29 @@ public abstract class GeometryArray extends Geometry {
      * <code>USE_COORD_INDEX_ONLY</code>,
      * to indicate that only the coordinate indices are used for indexed
      * geometry arrays.
-     * @exception IllegalArgumentException if vertexCount < 0, if
-     * vertexFormat does NOT include <code>COORDINATES</code>,
-     * if the <code>USE_COORD_INDEX_ONLY</code> bit is set for non-indexed
-     * geometry arrays (that is, GeometryArray objects that are not a
-     * subclass of IndexedGeometryArray),
-     * if the <code>INTERLEAVED</code> bit is set without the
-     * <code>BY_REFERENCE</code> bit being set,
-     * or if the <code>USE_NIO_BUFFER</code> bit is set without the
-     * <code>BY_REFERENCE</code> bit being set.
+     *
+     * @exception IllegalArgumentException if vertexCount &lt; 0
+     *
+     * @exception IllegalArgumentException if vertexFormat does <b>not</b>
+     * include <code>COORDINATES</code>
+     *
+     * @exception IllegalArgumentException if the <code>USE_COORD_INDEX_ONLY</code>
+     * bit is set for non-indexed geometry arrays (that is, GeometryArray objects
+     * that are not a subclass of IndexedGeometryArray)
+     *
+     * @exception IllegalArgumentException if the <code>INTERLEAVED</code>
+     * bit is set without the <code>BY_REFERENCE</code> bit being set
+     *
+     * @exception IllegalArgumentException if the <code>USE_NIO_BUFFER</code>
+     * bit is set without the <code>BY_REFERENCE</code> bit being set
+     *
+     * @exception IllegalArgumentException if the <code>INTERLEAVED</code>
+     * bit and the <code>VERTEX_ATTRIBUTES</code> bit are both set
      */
     public GeometryArray(int vertexCount, int vertexFormat) {
-
-        if (vertexCount < 0)
-	    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray96"));
-
-        if ((vertexFormat & COORDINATES) == 0)
-	  throw new IllegalArgumentException(J3dI18N.getString("GeometryArray0"));
-
-        if ((vertexFormat & INTERLEAVED) != 0 &&
-	    (vertexFormat & BY_REFERENCE) == 0)
-	    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray80"));
-
-        if ((vertexFormat & USE_NIO_BUFFER) != 0 &&
-	    (vertexFormat & BY_REFERENCE) == 0)
-	    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray117"));
-
-        if ((vertexFormat & TEXTURE_COORDINATE) != 0) {
-	    if ((vertexFormat & TEXTURE_COORDINATE_2) != 0) {
-	        texCoord2fArray = new TexCoord2f[1];
-	        texCoord2fScratch = new TexCoord2f();
-	    }
-	    else if ((vertexFormat & TEXTURE_COORDINATE_3) != 0) {
-	        texCoord3fArray = new TexCoord3f[1];
-	        texCoord3fScratch = new TexCoord3f();
-	    } else if ((vertexFormat & TEXTURE_COORDINATE_4) != 0) {
-	        texCoord4fArray = new TexCoord4f[1];
-	    }
-	}
-
-	((GeometryArrayRetained)this.retained).createGeometryArrayData(vertexCount, vertexFormat);
+        this(vertexCount, vertexFormat,
+            ((vertexFormat & TEXTURE_COORDINATE) != 0 ? 1 : 0),
+            ((vertexFormat & TEXTURE_COORDINATE) != 0 ? defTexCoordMap : null));
     }
 
 
@@ -467,48 +500,59 @@ public abstract class GeometryArray extends Geometry {
      *
      * <p>
      * <ul>
-     * <table BORDER=1 CELLSPACING=1 CELLPADDING=1>
+     * <table BORDER=1 CELLSPACING=2 CELLPADDING=2>
      * <tr>
      * <td><center><b>Index</b></center></td>
      * <td><center><b>Element</b></center></td>
-     * <td><center><b>Description</b></center></td>
+     * <td><b>Description</b></td>
      * </tr>
      * <tr>
      * <td><center>0</center></td>
      * <td><center>1</center></td>
-     * <td><center>Use tex coord set 1 for tex unit 0</center></td>
+     * <td>Use tex coord set 1 for tex unit 0</td>
      * </tr>
      * <tr>
      * <td><center>1</center></td>
      * <td><center>-1</center></td>
-     * <td><center>Use no tex coord set for tex unit 1</center></td>
+     * <td>Use no tex coord set for tex unit 1</td>
      * </tr>
      * <tr>
      * <td><center>2</center></td>
      * <td><center>0</center></td>
-     * <td><center>Use tex coord set 0 for tex unit 2</center></td>
+     * <td>Use tex coord set 0 for tex unit 2</td>
      * </tr>
      * <tr>
      * <td><center>3</center></td>
      * <td><center>1</center></td>
-     * <td><center>Reuse tex coord set 1 for tex unit 3</center></td>
+     * <td>Reuse tex coord set 1 for tex unit 3</td>
      * </tr>
      * </table>
      * </ul>
      * <p>
      *
+     * @exception IllegalArgumentException if vertexCount &lt; 0
+     *
+     * @exception IllegalArgumentException if vertexFormat does <b>not</b>
+     * include <code>COORDINATES</code>
+     *
+     * @exception IllegalArgumentException if the <code>USE_COORD_INDEX_ONLY</code>
+     * bit is set for non-indexed geometry arrays (that is, GeometryArray objects
+     * that are not a subclass of IndexedGeometryArray)
+     *
+     * @exception IllegalArgumentException if the <code>INTERLEAVED</code>
+     * bit is set without the <code>BY_REFERENCE</code> bit being set
+     *
+     * @exception IllegalArgumentException if the <code>USE_NIO_BUFFER</code>
+     * bit is set without the <code>BY_REFERENCE</code> bit being set
+     *
+     * @exception IllegalArgumentException if the <code>INTERLEAVED</code>
+     * bit and the <code>VERTEX_ATTRIBUTES</code> bit are both set
+     *
      * @exception IllegalArgumentException if
-     * <code>vertexCount&nbsp;<&nbsp;0</code>, if vertexFormat does
-     * NOT include <code>COORDINATES</code>, if the
-     * <code>INTERLEAVED</code> bit is set without the
-     * <code>BY_REFERENCE</code> bit being set, if the
-     * <code>USE_NIO_BUFFER</code> bit is set without the
-     * <code>BY_REFERENCE</code> bit being set, if
-     * the <code>USE_COORD_INDEX_ONLY</code> bit is set for non-indexed
-     * geometry arrays (that is, GeometryArray objects that are not a
-     * subclass of IndexedGeometryArray), if
-     * <code>texCoordSetCount&nbsp;<&nbsp;0</code>, or if any element
-     * in <code>texCoordSetMap[]&nbsp;>=&nbsp;texCoordSetCount</code>.
+     * <code>texCoordSetCount&nbsp;&lt;&nbsp;0</code>
+     *
+     * @exception IllegalArgumentException if any element in
+     * <code>texCoordSetMap[]&nbsp;&gt;=&nbsp;texCoordSetCount</code>.
      *
      * @since Java 3D 1.2
      */
@@ -516,20 +560,196 @@ public abstract class GeometryArray extends Geometry {
 			 int vertexFormat,
 			 int texCoordSetCount,
 			 int[] texCoordSetMap) {
+	this(vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap, 0, null);
+    }
+
+
+    /**
+     * Constructs an empty GeometryArray object with the specified
+     * number of vertices, vertex format, number of texture coordinate
+     * sets, texture coordinate mapping array, vertex attribute count,
+     * and vertex attribute sizes array.
+     *
+     * @param vertexCount the number of vertex elements in this
+     * GeometryArray<p>
+     *
+     * @param vertexFormat a mask indicating which components are
+     * present in each vertex.  This is specified as one or more
+     * individual flags that are bitwise "OR"ed together to describe
+     * the per-vertex data.
+     * The flags include: <code>COORDINATES</code>, to signal the inclusion of
+     * vertex positions--always present; <code>NORMALS</code>, to signal
+     * the inclusion of per vertex normals; one of <code>COLOR_3</code> or
+     * <code>COLOR_4</code>, to signal the inclusion of per vertex
+     * colors (without or with alpha information); one of
+     * <code>TEXTURE_COORDINATE_2</code> or <code>TEXTURE_COORDINATE_3</code>
+     * or <code>TEXTURE_COORDINATE_4</code>,
+     * to signal the
+     * inclusion of per-vertex texture coordinates (2D , 3D or 4D);
+     * <code>VERTEX_ATTRIBUTES</code>, to signal
+     * the inclusion of one or more arrays of vertex attributes;
+     * <code>BY_REFERENCE</code>, to indicate that the data is passed
+     * by reference
+     * rather than by copying; <code>INTERLEAVED</code>, to indicate
+     * that the referenced
+     * data is interleaved in a single array;
+     * <code>USE_NIO_BUFFER</code>, to indicate that the referenced data
+     * is accessed via a J3DBuffer object that wraps an NIO buffer;
+     * <code>USE_COORD_INDEX_ONLY</code>,
+     * to indicate that only the coordinate indices are used for indexed
+     * geometry arrays.<p>
+     *
+     * @param texCoordSetCount the number of texture coordinate sets
+     * in this GeometryArray object.  If <code>vertexFormat</code>
+     * does not include one of <code>TEXTURE_COORDINATE_2</code> or
+     * <code>TEXTURE_COORDINATE_3</code>, the
+     * <code>texCoordSetCount</code> parameter is not used.<p>
+     *
+     * <a name="texCoordSetMap">
+     * @param texCoordSetMap an array that maps texture coordinate
+     * sets to texture units.  The array is indexed by texture unit
+     * number for each texture unit in the associated Appearance
+     * object.  The values in the array specify the texture coordinate
+     * set within this GeometryArray object that maps to the
+     * corresponding texture
+     * unit.  All elements within the array must be less than
+     * <code>texCoordSetCount</code>.  A negative value specifies that
+     * no texture coordinate set maps to the texture unit
+     * corresponding to the index.  If there are more texture units in
+     * any associated Appearance object than elements in the mapping
+     * array, the extra elements are assumed to be -1.  The same
+     * texture coordinate set may be used for more than one texture
+     * unit.  Each texture unit in every associated Appearance must
+     * have a valid source of texture coordinates: either a
+     * non-negative texture coordinate set must be specified in the
+     * mapping array or texture coordinate generation must be enabled.
+     * Texture coordinate generation will take precedence for those
+     * texture units for which a texture coordinate set is specified
+     * and texture coordinate generation is enabled.  If
+     * <code>vertexFormat</code> does not include one of
+     * <code>TEXTURE_COORDINATE_2</code> or
+     * <code>TEXTURE_COORDINATE_3</code> or
+     * <code>TEXTURE_COORDINATE_4</code>, the
+     * <code>texCoordSetMap</code> array is not used.  The following example
+     * illustrates the use of the <code>texCoordSetMap</code> array.
+     *
+     * <p>
+     * <ul>
+     * <table BORDER=1 CELLSPACING=2 CELLPADDING=2>
+     * <tr>
+     * <td><center><b>Index</b></center></td>
+     * <td><center><b>Element</b></center></td>
+     * <td><b>Description</b></td>
+     * </tr>
+     * <tr>
+     * <td><center>0</center></td>
+     * <td><center>1</center></td>
+     * <td>Use tex coord set 1 for tex unit 0</td>
+     * </tr>
+     * <tr>
+     * <td><center>1</center></td>
+     * <td><center>-1</center></td>
+     * <td>Use no tex coord set for tex unit 1</td>
+     * </tr>
+     * <tr>
+     * <td><center>2</center></td>
+     * <td><center>0</center></td>
+     * <td>Use tex coord set 0 for tex unit 2</td>
+     * </tr>
+     * <tr>
+     * <td><center>3</center></td>
+     * <td><center>1</center></td>
+     * <td>Reuse tex coord set 1 for tex unit 3</td>
+     * </tr>
+     * </table>
+     * </ul>
+     * <p>
+     *
+     * @param vertexAttrCount the number of vertex attributes
+     * in this GeometryArray object. If <code>vertexFormat</code>
+     * does not include <code>VERTEX_ATTRIBUTES</code>, the
+     * <code>vertexAttrCount</code> parameter must be 0.<p>
+     *
+     * @param vertexAttrSizes is an array that specifes the size of
+     * each vertex attribute. Each element in the array specifies the
+     * number of components in the attribute, from 1 to 4. The length
+     * of the array must be equal to <code>vertexAttrCount</code>.<p>
+     *
+     * @exception IllegalArgumentException if vertexCount &lt; 0
+     *
+     * @exception IllegalArgumentException if vertexFormat does <b>not</b>
+     * include <code>COORDINATES</code>
+     *
+     * @exception IllegalArgumentException if the <code>USE_COORD_INDEX_ONLY</code>
+     * bit is set for non-indexed geometry arrays (that is, GeometryArray objects
+     * that are not a subclass of IndexedGeometryArray)
+     *
+     * @exception IllegalArgumentException if the <code>INTERLEAVED</code>
+     * bit is set without the <code>BY_REFERENCE</code> bit being set
+     *
+     * @exception IllegalArgumentException if the <code>USE_NIO_BUFFER</code>
+     * bit is set without the <code>BY_REFERENCE</code> bit being set
+     *
+     * @exception IllegalArgumentException if the <code>INTERLEAVED</code>
+     * bit and the <code>VERTEX_ATTRIBUTES</code> bit are both set
+     *
+     * @exception IllegalArgumentException if
+     * <code>texCoordSetCount&nbsp;&lt;&nbsp;0</code>
+     *
+     * @exception IllegalArgumentException if any element in
+     * <code>texCoordSetMap[]&nbsp;&gt;=&nbsp;texCoordSetCount</code>.
+     *
+     * @exception IllegalArgumentException if
+     * <code>vertexAttrCount&nbsp;&gt;&nbsp;0</code> and the
+     * <code>VERTEX_ATTRIBUTES</code> bit is not set
+     *
+     * @exception IllegalArgumentException if
+     * <code>vertexAttrCount&nbsp;&lt;&nbsp;0</code>
+     *
+     * @exception IllegalArgumentException if
+     * <code>vertexAttrSizes.length&nbsp;!=&nbsp;vertexAttrCount</code>
+     *
+     * @exception IllegalArgumentException if any element in
+     * <code>vertexAttrSizes[]</code> is <code>&lt; 1</code> or
+     * <code>&gt; 4</code>.
+     *
+     * @since Java 3D 1.4
+     */
+    public GeometryArray(int vertexCount,
+			 int vertexFormat,
+			 int texCoordSetCount,
+			 int[] texCoordSetMap,
+			 int vertexAttrCount,
+			 int[] vertexAttrSizes) {
 
         if (vertexCount < 0)
             throw new IllegalArgumentException(J3dI18N.getString("GeometryArray96"));
 
+        if (texCoordSetCount < 0)
+            throw new IllegalArgumentException(J3dI18N.getString("GeometryArray124"));
+
+        if (vertexAttrCount < 0)
+            throw new IllegalArgumentException(J3dI18N.getString("GeometryArray125"));
+
         if ((vertexFormat & COORDINATES) == 0)
-          throw new IllegalArgumentException(J3dI18N.getString("GeometryArray0"
-));
+          throw new IllegalArgumentException(J3dI18N.getString("GeometryArray0"));
 
         if ((vertexFormat & INTERLEAVED) != 0 &&
             (vertexFormat & BY_REFERENCE) == 0)
             throw new IllegalArgumentException(J3dI18N.getString("GeometryArray80"));
 
+        if ((vertexFormat & INTERLEAVED) != 0 &&
+                (vertexFormat & VERTEX_ATTRIBUTES) != 0) {
+            throw new IllegalArgumentException(J3dI18N.getString("GeometryArray128"));
+        }
+
+        if ((vertexFormat & USE_COORD_INDEX_ONLY) != 0 &&
+                !(this instanceof IndexedGeometryArray)) {
+            throw new IllegalArgumentException(J3dI18N.getString("GeometryArray135"));
+        }
+
         if ((vertexFormat & USE_NIO_BUFFER) != 0 &&
-	    (vertexFormat & BY_REFERENCE) == 0)
+            (vertexFormat & BY_REFERENCE) == 0)
 	    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray117"));
 
 	if ((vertexFormat & TEXTURE_COORDINATE) != 0) {
@@ -556,8 +776,36 @@ public abstract class GeometryArray extends Geometry {
 	        texCoord4fArray = new TexCoord4f[1];
 	    }
 	}
-
-        ((GeometryArrayRetained)this.retained).createGeometryArrayData(vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap);
+        
+        if ((vertexFormat & VERTEX_ATTRIBUTES) != 0) {
+            if (vertexAttrCount > 0) {
+                if (vertexAttrCount != vertexAttrSizes.length) {
+                    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray132"));
+                }
+                
+                for (int i = 0; i < vertexAttrSizes.length; i++) {
+                    if (vertexAttrSizes[i] < 1 || vertexAttrSizes[i] > 4) {
+                        throw new IllegalArgumentException(J3dI18N.getString("GeometryArray133"));
+                    }
+                }
+            } else {
+                if (vertexAttrSizes != null && vertexAttrSizes.length != 0) {
+                    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray132"));
+                }
+            }
+        } else {
+            if (vertexAttrCount > 0) {
+                throw new IllegalArgumentException(J3dI18N.getString("GeometryArray131"));
+            }
+        }
+        
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+        
+        ((GeometryArrayRetained)this.retained).createGeometryArrayData(
+	    vertexCount, vertexFormat,
+	    texCoordSetCount, texCoordSetMap,
+	    vertexAttrCount, vertexAttrSizes);
 
     }
 
@@ -636,7 +884,35 @@ public abstract class GeometryArray extends Geometry {
      */
     public void getTexCoordSetMap(int[] texCoordSetMap) {
         ((GeometryArrayRetained)this.retained).getTexCoordSetMap(texCoordSetMap);
-	return; 
+    }
+
+
+    /**
+     * Retrieves the number of vertex attributes in this GeometryArray
+     * object.
+     *
+     * @return the number of vertex attributes in this GeometryArray
+     * object
+     *
+     * @since Java 3D 1.4
+     */
+    public int getVertexAttrCount() {
+        return ((GeometryArrayRetained)this.retained).getVertexAttrCount();
+    }
+
+
+    /**
+     * 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.
+     *
+     * @since Java 3D 1.4
+     */
+    public void getVertexAttrSizes(int[] vertexAttrSizes) {
+        ((GeometryArrayRetained)this.retained).getVertexAttrSizes(vertexAttrSizes);
     }
 
 
@@ -688,27 +964,30 @@ public abstract class GeometryArray extends Geometry {
      * @exception IllegalArgumentException if any of the following are
      * true:
      * <ul>
-     * <code>validVertexCount < 0</code>,<br>
-     * <code>initialVertexIndex + validVertexCount > vertexCount</code>,<br>
-     * <code>initialCoordIndex + validVertexCount > vertexCount</code>,<br>
-     * <code>initialColorIndex + validVertexCount > vertexCount</code>,<br>
-     * <code>initialNormalIndex + validVertexCount > vertexCount</code>,<br>
-     * <code>initialTexCoordIndex + validVertexCount > vertexCount</code>
+     * <code>validVertexCount &lt; 0</code>,<br>
+     * <code>initialVertexIndex + validVertexCount &gt; vertexCount</code>,<br>
+     * <code>initialCoordIndex + validVertexCount &gt; vertexCount</code>,<br>
+     * <code>initialColorIndex + validVertexCount &gt; vertexCount</code>,<br>
+     * <code>initialNormalIndex + validVertexCount &gt; vertexCount</code>,<br>
+     * <code>initialTexCoordIndex + validVertexCount &gt; vertexCount</code>,<br>
+     * <code>initialVertexAttrIndex + validVertexCount &gt; vertexCount</code>
      * </ul>
      * <p>
      * @exception ArrayIndexOutOfBoundsException if the geometry data format
      * is <code>BY_REFERENCE</code> and any the following
      * are true for non-null array references:
      * <ul>
-     * <code>CoordRef.length</code> < <i>num_words</i> *
+     * <code>CoordRef.length</code> &lt; <i>num_words</i> *
      * (<code>initialCoordIndex + validVertexCount</code>),<br>
-     * <code>ColorRef.length</code> < <i>num_words</i> *
+     * <code>ColorRef.length</code> &lt; <i>num_words</i> *
      * (<code>initialColorIndex + validVertexCount</code>),<br>
-     * <code>NormalRef.length</code> < <i>num_words</i> *
+     * <code>NormalRef.length</code> &lt; <i>num_words</i> *
      * (<code>initialNormalIndex + validVertexCount</code>),<br>
-     * <code>TexCoordRef.length</code> < <i>num_words</i> *
+     * <code>TexCoordRef.length</code> &lt; <i>num_words</i> *
      * (<code>initialTexCoordIndex + validVertexCount</code>),<br>
-     * <code>InterleavedVertices.length</code> < <i>words_per_vertex</i> *
+     * <code>VertexAttrRef.length</code> &lt; <i>num_words</i> *
+     * (<code>initialVertexAttrIndex + validVertexCount</code>),<br>
+     * <code>InterleavedVertices.length</code> &lt; <i>words_per_vertex</i> *
      * (<code>initialVertexIndex + validVertexCount</code>)<br>
      * </ul>
      * where <i>num_words</i> depends on which variant of
@@ -727,7 +1006,7 @@ public abstract class GeometryArray extends Geometry {
 	    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray96"));
 
 	((GeometryArrayRetained)this.retained).setValidVertexCount(validVertexCount);
-	// NOTE: the checks for initial*Index + validVertexCount >
+	// NOTE: the checks for initial*Index + validVertexCount &gt;
 	// vertexCount need to be done in the retained method
     }
 
@@ -770,61 +1049,99 @@ public abstract class GeometryArray extends Geometry {
      * @see NodeComponent#setDuplicateOnCloneTree
      */
     void duplicateAttributes(NodeComponent originalNodeComponent,
-			     boolean forceDuplicate) {
-
-      super.duplicateAttributes(originalNodeComponent, forceDuplicate);
-      // vertexFormat and vertexCount are copied in subclass when constructor
-      //  public GeometryArray(int vertexCount, int vertexFormat) is used
-      // in cloneNodeComponent()
-      GeometryArrayRetained src = (GeometryArrayRetained) originalNodeComponent.retained;
-      GeometryArrayRetained dst = (GeometryArrayRetained) retained;
-      int format = src.getVertexFormat();
-      if ((format & BY_REFERENCE) == 0) {
-	  System.arraycopy(src.vertexData, 0, dst.vertexData, 0,
-			   src.vertexData.length);
-	  dst.setInitialVertexIndex(src.getInitialVertexIndex());
-	  
-      } else {
-	  dst.setInitialCoordIndex(src.getInitialCoordIndex());
-	  dst.setInitialColorIndex(src.getInitialColorIndex());
-	  dst.setInitialNormalIndex(src.getInitialNormalIndex());
-	  int setCount = src.getTexCoordSetCount();
-	  for (int i=0; i < setCount; i++) {
-	      dst.setInitialTexCoordIndex(i, src.getInitialTexCoordIndex(i));
-	  }
-	  if ((format & INTERLEAVED) == 0) {
-	      dst.setCoordRefFloat(src.getCoordRefFloat());
-	      dst.setCoordRefDouble(src.getCoordRefDouble());	      
-	      dst.setCoordRef3f(src.getCoordRef3f());
-	      dst.setCoordRef3d(src.getCoordRef3d());
-              dst.setColorRefFloat(src.getColorRefFloat());
-	      dst.setColorRefByte(src.getColorRefByte());
-	      if ((format & WITH_ALPHA) == 0) {
-		  dst.setColorRef3f(src.getColorRef3f());
-		  dst.setColorRef3b(src.getColorRef3b());
-	      } else {
-		  dst.setColorRef4f(src.getColorRef4f());
-		  dst.setColorRef4b(src.getColorRef4b());
-	      }
-	      dst.setNormalRefFloat(src.getNormalRefFloat());
-	      dst.setNormalRef3f(src.getNormalRef3f());	   
-	      for (int i=0; i < setCount; i++) {   
-		  dst.setTexCoordRefFloat(i, src.getTexCoordRefFloat(i));
-	      }
-	      if ((format & TEXTURE_COORDINATE_2) != 0) {
-		  for (int i=0; i < setCount; i++) {   
-		      dst.setTexCoordRef2f(i, src.getTexCoordRef2f(i));
-		  }
-	      } 
-	      if ((format & TEXTURE_COORDINATE_3) != 0) {
-		  for (int i=0; i < setCount; i++) {   
-		      dst.setTexCoordRef3f(i, src.getTexCoordRef3f(i));
-		  }
-	      }
-	  } else {
-	      dst.setInterleavedVertices(src.getInterleavedVertices());
-	  } 
-      }
+                             boolean forceDuplicate) {
+
+        super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+        // vertexFormat and vertexCount are copied in subclass when constructor
+        //  public GeometryArray(int vertexCount, int vertexFormat) is used
+        // in cloneNodeComponent()
+        GeometryArrayRetained src = (GeometryArrayRetained) originalNodeComponent.retained;
+        GeometryArrayRetained dst = (GeometryArrayRetained) retained;
+        int format = src.getVertexFormat();
+        if ((format & BY_REFERENCE) == 0) {
+            System.arraycopy(src.vertexData, 0, dst.vertexData, 0,
+                    src.vertexData.length);
+            dst.setInitialVertexIndex(src.getInitialVertexIndex());
+
+        } else {
+            dst.setInitialCoordIndex(src.getInitialCoordIndex());
+            dst.setInitialColorIndex(src.getInitialColorIndex());
+            dst.setInitialNormalIndex(src.getInitialNormalIndex());
+            int setCount = src.getTexCoordSetCount();
+            int vAttrCount = src.getVertexAttrCount();
+            for (int i=0; i < setCount; i++) {
+                dst.setInitialTexCoordIndex(i, src.getInitialTexCoordIndex(i));
+            }
+            if ((format & INTERLEAVED) == 0) {
+                if ((format & USE_NIO_BUFFER) == 0) {
+                    // Java arrays
+                    dst.setCoordRefFloat(src.getCoordRefFloat());
+                    dst.setCoordRefDouble(src.getCoordRefDouble());
+                    dst.setCoordRef3f(src.getCoordRef3f());
+                    dst.setCoordRef3d(src.getCoordRef3d());
+                    dst.setColorRefFloat(src.getColorRefFloat());
+                    dst.setColorRefByte(src.getColorRefByte());
+                    if ((format & WITH_ALPHA) == 0) {
+                        dst.setColorRef3f(src.getColorRef3f());
+                        dst.setColorRef3b(src.getColorRef3b());
+                    } else {
+                        dst.setColorRef4f(src.getColorRef4f());
+                        dst.setColorRef4b(src.getColorRef4b());
+                    }
+                    dst.setNormalRefFloat(src.getNormalRefFloat());
+                    dst.setNormalRef3f(src.getNormalRef3f());
+
+                    switch (src.getVertexAttrType()) {
+                    case GeometryArrayRetained.AF:
+                        for (int i=0; i < vAttrCount; i++) {
+                            dst.setVertexAttrRefFloat(i, src.getVertexAttrRefFloat(i));
+                        }
+                        break;
+                    }
+
+                    switch (src.getTexCoordType()) {
+                    case GeometryArrayRetained.TF:
+                        for (int i=0; i < setCount; i++) {
+                            dst.setTexCoordRefFloat(i, src.getTexCoordRefFloat(i));
+                        }
+                        break;
+                    case GeometryArrayRetained.T2F:
+                        for (int i=0; i < setCount; i++) {
+                            dst.setTexCoordRef2f(i, src.getTexCoordRef2f(i));
+                        }
+                        break;
+                    case GeometryArrayRetained.T3F:
+                        for (int i=0; i < setCount; i++) {
+                            dst.setTexCoordRef3f(i, src.getTexCoordRef3f(i));
+                        }
+                        break;
+                    }
+                } else {
+                    // NIO buffer
+                    dst.setCoordRefBuffer(src.getCoordRefBuffer());
+                    dst.setColorRefBuffer(src.getColorRefBuffer());
+                    dst.setNormalRefBuffer(src.getNormalRefBuffer());
+
+                    switch (src.getVertexAttrType()) {
+                    case GeometryArrayRetained.AF:
+                        for (int i=0; i < vAttrCount; i++) {
+                            dst.setVertexAttrRefBuffer(i, src.getVertexAttrRefBuffer(i));
+                        }
+                        break;
+                    }
+
+                    switch (src.getTexCoordType()) {
+                    case GeometryArrayRetained.TF:
+                        for (int i=0; i < setCount; i++) {
+                            dst.setTexCoordRefBuffer(i, src.getTexCoordRefBuffer(i));
+                        }
+                        break;
+                    }
+                }
+            } else {
+                dst.setInterleavedVertices(src.getInterleavedVertices());
+            }
+        }
     }
 
 
@@ -848,15 +1165,15 @@ public abstract class GeometryArray extends Geometry {
      * @exception IllegalArgumentException if either of the following are
      * true:
      * <ul>
-     * <code>initialVertexIndex < 0</code> or<br>
-     * <code>initialVertexIndex + validVertexCount > vertexCount</code><br>
+     * <code>initialVertexIndex &lt; 0</code> or<br>
+     * <code>initialVertexIndex + validVertexCount &gt; vertexCount</code><br>
      * </ul>
      *
      * @exception ArrayIndexOutOfBoundsException if the geometry data format
      * is <code>INTERLEAVED</code>, the InterleavedVertices array is
      * non-null, and:
      * <ul>
-     * <code>InterleavedVertices.length</code> < <i>num_words</i> *
+     * <code>InterleavedVertices.length</code> &lt; <i>num_words</i> *
      * (<code>initialVertexIndex + validVertexCount</code>)<br>
      * </ul>
      * where <i>num_words</i> depends on which vertex formats are enabled.
@@ -2271,7 +2588,7 @@ public abstract class GeometryArray extends Geometry {
 
 
 	if (((((GeometryArrayRetained)this.retained).vertexFormat) & 
-		(TEXTURE_COORDINATE_2 | TEXTURE_COORDINATE_3)) != 0)
+		(TEXTURE_COORDINATE_2 | TEXTURE_COORDINATE_4)) != 0)
 	    throw new IllegalStateException(J3dI18N.getString("GeometryArray95"));
 
 	((GeometryArrayRetained)this.retained).setTextureCoordinates(
@@ -2338,7 +2655,7 @@ public abstract class GeometryArray extends Geometry {
      *
      * @param index starting destination vertex index in this geometry array
      * @param texCoords source array of 2*n , 3*n or 4*n values containing 
-     * n new * texture coordinates
+     * n new texture coordinates
      * @param start starting source vertex index in <code>texCoords</code>
      * array.
      * @param length number of texture Coordinates to be copied.
@@ -2531,44 +2848,619 @@ public abstract class GeometryArray extends Geometry {
 		texCoordSet, index, texCoords, start, length);
     }
 
-  /**
-   * Gets the coordinate associated with the vertex at
-   * the specified index for this object using data in <code>texCoords</code>
-   * @param index source vertex index in this geometry array
-   * @param coordinate destination array of 3 values that will receive the coordinate
-   * @exception CapabilityNotSetException if the appropriate capability is
-   * not set and this object is part of a live or compiled scene graph
+
+    /**
+     * 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 source array of 1, 2, 3 or 4 values containing
+     * the new vertex attribute
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range, or if the vertexAttr array is
+     * too small.
+     *
      * @exception IllegalStateException if the data mode for this geometry
      * array object is <code>BY_REFERENCE</code>.
-   */
-  public void getCoordinate(int index, float coordinate[]) {
-    if (isLiveOrCompiled())
-    if(!this.getCapability(ALLOW_COORDINATE_READ))
-      throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray48"));
+     *
+     * @since Java 3D 1.4
+     */
+    public void setVertexAttr(int vertexAttrNum, int index,
+			      float[] vertexAttr) {
 
-    int format = ((GeometryArrayRetained)this.retained).vertexFormat;
-    if ((format & BY_REFERENCE) != 0)
-      throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126"));
+	    }
+	}
 
-    ((GeometryArrayRetained)this.retained).getCoordinate(index, coordinate);
-  }
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
 
-  /**
-   * Gets the coordinate associated with the vertex at
-   * the specified index for this object.
-   * @param index source vertex index in this geometry array
-   * @param coordinate destination array of 3 values that will receive the coordinate
-   * @exception CapabilityNotSetException if the appropriate capability is
-   * not set and this object is part of a live or compiled scene graph
+	((GeometryArrayRetained)this.retained).setVertexAttrs(
+		vertexAttrNum, index, vertexAttr, 0, 1);
+    }
+
+    /**
+     * 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
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range.
+     *
      * @exception IllegalStateException if the data mode for this geometry
      * array object is <code>BY_REFERENCE</code>.
-   */
-  public void getCoordinate(int index, double coordinate[]) {
-    if (isLiveOrCompiled())
-    if(!this.getCapability(ALLOW_COORDINATE_READ))
-      throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray48"));
-
-    int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+     *
+     * @exception IllegalStateException if the size of the specified
+     * vertex attribute number is not 2.
+     *
+     * @since Java 3D 1.4
+     */
+    public void setVertexAttr(int vertexAttrNum, int index,
+			      Point2f vertexAttr) {
+
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
+
+	int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum];
+	if (size != 2) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray134"));
+	}
+
+	((GeometryArrayRetained)this.retained).setVertexAttr(
+		vertexAttrNum, index, vertexAttr);
+    }
+
+    /**
+     * 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
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range.
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+     *
+     * @exception IllegalStateException if the size of the specified
+     * vertex attribute number is not 3.
+     *
+     * @since Java 3D 1.4
+     */
+    public void setVertexAttr(int vertexAttrNum, int index,
+			      Point3f vertexAttr) {
+
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
+
+	int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum];
+	if (size != 3) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray134"));
+	}
+
+	((GeometryArrayRetained)this.retained).setVertexAttr(
+		vertexAttrNum, index, vertexAttr);
+    }
+
+    /**
+     * 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
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range.
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+     *
+     * @exception IllegalStateException if the size of the specified
+     * vertex attribute number is not 4.
+     *
+     * @since Java 3D 1.4
+     */
+    public void setVertexAttr(int vertexAttrNum, int index,
+			      Point4f vertexAttr) {
+
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
+
+	int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum];
+	if (size != 4) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray134"));
+	}
+
+	((GeometryArrayRetained)this.retained).setVertexAttr(
+		vertexAttrNum, index, vertexAttr);
+    }
+
+    /**
+     * Sets the vertex attributes associated with the vertices starting at
+     * the specified index in the specified vertex attribute number
+     * for this object.  The entire source array is copied to this
+     * geometry array.
+     *
+     * @param vertexAttrNum vertex attribute number in this geometry array
+     * @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
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range, or if the vertexAttr array is
+     * too large.
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+     *
+     * @since Java 3D 1.4
+     */
+    public void setVertexAttrs(int vertexAttrNum, int index,
+			       float[] vertexAttrs) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
+
+	int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum];
+	((GeometryArrayRetained)this.retained).setVertexAttrs(
+		vertexAttrNum, index, vertexAttrs, 0, vertexAttrs.length / size);
+    }
+
+    /**
+     * Sets the vertex attributes associated with the vertices starting at
+     * the specified index in the specified vertex attribute number
+     * for this object.  The entire source array is copied to this
+     * geometry array.
+     *
+     * @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
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range, or if the vertexAttr array is
+     * too large.
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+     *
+     * @exception IllegalStateException if the size of the specified
+     * vertex attribute number is not 2.
+     *
+     * @since Java 3D 1.4
+     */
+    public void setVertexAttrs(int vertexAttrNum, int index,
+			       Point2f[] vertexAttrs) {
+
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
+
+	int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum];
+	if (size != 2) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray134"));
+	}
+
+	((GeometryArrayRetained)this.retained).setVertexAttrs(
+		vertexAttrNum, index, vertexAttrs, 0, vertexAttrs.length);
+    }
+
+    /**
+     * Sets the vertex attributes associated with the vertices starting at
+     * the specified index in the specified vertex attribute number
+     * for this object.  The entire source array is copied to this
+     * geometry array.
+     *
+     * @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
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range, or if the vertexAttr array is
+     * too large.
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+     *
+     * @exception IllegalStateException if the size of the specified
+     * vertex attribute number is not 3.
+     *
+     * @since Java 3D 1.4
+     */
+    public void setVertexAttrs(int vertexAttrNum, int index,
+			       Point3f[] vertexAttrs) {
+
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
+
+	int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum];
+	if (size != 3) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray134"));
+	}
+
+	((GeometryArrayRetained)this.retained).setVertexAttrs(
+		vertexAttrNum, index, vertexAttrs, 0, vertexAttrs.length);
+    }
+
+    /**
+     * Sets the vertex attributes associated with the vertices starting at
+     * the specified index in the specified vertex attribute number
+     * for this object.  The entire source array is copied to this
+     * geometry array.
+     *
+     * @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
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range, or if the vertexAttr array is
+     * too large.
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+     *
+     * @exception IllegalStateException if the size of the specified
+     * vertex attribute number is not 4.
+     *
+     * @since Java 3D 1.4
+     */
+    public void setVertexAttrs(int vertexAttrNum, int index,
+			       Point4f[] vertexAttrs) {
+
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
+
+	int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum];
+	if (size != 4) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray134"));
+	}
+
+	((GeometryArrayRetained)this.retained).setVertexAttrs(
+		vertexAttrNum, index, vertexAttrs, 0, vertexAttrs.length);
+    }
+
+    /**
+     * 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.
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if any of index,
+     * (index+length), or vertexAttrNum are out of range, or if
+     * vertexAttrs is too small.
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+     *
+     * @since Java 3D 1.4
+     */
+    public void setVertexAttrs(int vertexAttrNum, int index,
+			       float[] vertexAttrs,
+			       int start, int length) {
+
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
+
+	((GeometryArrayRetained)this.retained).setVertexAttrs(
+		vertexAttrNum, index, vertexAttrs, start, length);
+    }
+
+    /**
+     * 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.
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if any of index,
+     * (index+length), or vertexAttrNum are out of range, or if
+     * vertexAttrs is too small.
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+     *
+     * @exception IllegalStateException if the size of the specified
+     * vertex attribute number is not 2.
+     *
+     * @since Java 3D 1.4
+     */
+    public void setVertexAttrs(int vertexAttrNum, int index,
+			       Point2f[] vertexAttrs,
+			       int start, int length) {
+
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
+
+	int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum];
+	if (size != 2) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray134"));
+	}
+
+	((GeometryArrayRetained)this.retained).setVertexAttrs(
+		vertexAttrNum, index, vertexAttrs, start, length);
+    }
+
+    /**
+     * 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.
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if any of index,
+     * (index+length), or vertexAttrNum are out of range, or if
+     * vertexAttrs is too small.
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+     *
+     * @exception IllegalStateException if the size of the specified
+     * vertex attribute number is not 3.
+     *
+     * @since Java 3D 1.4
+     */
+    public void setVertexAttrs(int vertexAttrNum, int index,
+			       Point3f[] vertexAttrs,
+			       int start, int length) {
+
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
+
+	int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum];
+	if (size != 3) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray134"));
+	}
+
+	((GeometryArrayRetained)this.retained).setVertexAttrs(
+		vertexAttrNum, index, vertexAttrs, start, length);
+    }
+
+    /**
+     * 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.
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if any of index,
+     * (index+length), or vertexAttrNum are out of range, or if
+     * vertexAttrs is too small.
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+     *
+     * @exception IllegalStateException if the size of the specified
+     * vertex attribute number is not 4.
+     *
+     * @since Java 3D 1.4
+     */
+    public void setVertexAttrs(int vertexAttrNum, int index,
+			       Point4f[] vertexAttrs,
+			       int start, int length) {
+
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray126"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
+
+	int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum];
+	if (size != 4) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray134"));
+	}
+
+	((GeometryArrayRetained)this.retained).setVertexAttrs(
+		vertexAttrNum, index, vertexAttrs, start, length);
+    }
+
+
+  /**
+   * Gets the coordinate associated with the vertex at
+   * the specified index for this object using data in <code>texCoords</code>
+   * @param index source vertex index in this geometry array
+   * @param coordinate destination array of 3 values that will receive the coordinate
+   * @exception CapabilityNotSetException if the appropriate capability is
+   * not set and this object is part of a live or compiled scene graph
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+   */
+  public void getCoordinate(int index, float coordinate[]) {
+    if (isLiveOrCompiled())
+    if(!this.getCapability(ALLOW_COORDINATE_READ))
+      throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray48"));
+
+    int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+    if ((format & BY_REFERENCE) != 0)
+      throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+    ((GeometryArrayRetained)this.retained).getCoordinate(index, coordinate);
+  }
+
+  /**
+   * Gets the coordinate associated with the vertex at
+   * the specified index for this object.
+   * @param index source vertex index in this geometry array
+   * @param coordinate destination array of 3 values that will receive the coordinate
+   * @exception CapabilityNotSetException if the appropriate capability is
+   * not set and this object is part of a live or compiled scene graph
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+   */
+  public void getCoordinate(int index, double coordinate[]) {
+    if (isLiveOrCompiled())
+    if(!this.getCapability(ALLOW_COORDINATE_READ))
+      throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray48"));
+
+    int format = ((GeometryArrayRetained)this.retained).vertexFormat;
     if ((format & BY_REFERENCE) != 0)
       throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
 
@@ -3646,72 +4538,450 @@ public abstract class GeometryArray extends Geometry {
 		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray75"));
 
 	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
-	if ((format & BY_REFERENCE) != 0)
+	if ((format & BY_REFERENCE) != 0)
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+	if ((format & TEXTURE_COORDINATE ) == 0)
+	    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
+
+        if (((((GeometryArrayRetained)this.retained).vertexFormat) &
+                (TEXTURE_COORDINATE_2 | TEXTURE_COORDINATE_4)) != 0)
+            throw new IllegalStateException(J3dI18N.getString("GeometryArray95"));
+	((GeometryArrayRetained)this.retained).getTextureCoordinates(
+					texCoordSet, index, texCoords);
+    }
+
+
+    /**
+     * Gets the texture coordinates associated with the vertices starting at
+     * the specified index in the specified texture coordinate set
+     * for this object.  The length of the destination
+     * array determines the number of texture coordinates copied.
+     * A maximum of <code>vertexCount-index</code> texture coordinates
+     * are copied.  If the destination array is larger than is needed
+     * to hold the texture coordinates, the excess locations in the
+     * array are not modified.  If the destination array is smaller
+     * than is needed to hold the texture coordinates, only as
+     * many texture coordinates as the array will hold are copied.
+     *
+     * @param texCoordSet texture coordinate set in this geometry array
+     * @param index starting source vertex index in this geometry array
+     * @param texCoords destination array of TexCoord4f objects that will
+     * receive the texture coordinates
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if none of the
+     * <code>TEXTURE_COORDINATE</code> bits are set in the
+     * <code>vertexFormat</code> or if the index or
+     * texCoordSet is out of range.
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+     *
+     * @exception IllegalStateException if TEXTURE_COORDINATE_2 or
+     * TEXTURE_COORDINATE_3 is specified in vertex format
+     *
+     * @since Java 3D 1.3
+     */
+    public void getTextureCoordinates(int texCoordSet,
+				      int index, TexCoord4f texCoords[]) {
+	if (isLiveOrCompiled())
+	    if(!this.getCapability(ALLOW_TEXCOORD_READ))
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray75"));
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0)
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+
+	if ((format & TEXTURE_COORDINATE ) == 0)
+	    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
+
+        if (((((GeometryArrayRetained)this.retained).vertexFormat) &
+                (TEXTURE_COORDINATE_2 | TEXTURE_COORDINATE_3)) != 0)
+            throw new IllegalStateException(J3dI18N.getString("GeometryArray109"));
+
+	((GeometryArrayRetained)this.retained).getTextureCoordinates(
+					texCoordSet, index, texCoords);
+    }
+
+    /**
+     * Gets 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 source vertex index in this geometry array
+     * @param vertexAttr array of 1, 2, 3 or 4 values that will receive the
+     * vertex attribute
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range, or if the vertexAttr array is
+     * too small.
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+     *
+     * @since Java 3D 1.4
+     */
+    public void getVertexAttr(int vertexAttrNum, int index,
+			      float[] vertexAttr) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray127"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
+
+	((GeometryArrayRetained)this.retained).getVertexAttr(
+				vertexAttrNum, index, vertexAttr);
+    }
+
+    /**
+     * Gets 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 source vertex index in this geometry array
+     * @param vertexAttr the vector that will receive the vertex attributes
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range.
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+     *
+     * @exception IllegalStateException if the size of the specified
+     * vertex attribute number is not 2.
+     *
+     * @since Java 3D 1.4
+     */
+    public void getVertexAttr(int vertexAttrNum, int index,
+			      Point2f vertexAttr) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray127"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
+
+	int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum];
+	if (size != 2) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray134"));
+	}
+
+	((GeometryArrayRetained)this.retained).getVertexAttr(
+				vertexAttrNum, index, vertexAttr);
+    }
+
+    /**
+     * Gets 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 source vertex index in this geometry array
+     * @param vertexAttr the vector that will receive the vertex attributes
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range.
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+     *
+     * @exception IllegalStateException if the size of the specified
+     * vertex attribute number is not 3.
+     *
+     * @since Java 3D 1.4
+     */
+    public void getVertexAttr(int vertexAttrNum, int index,
+			      Point3f vertexAttr) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray127"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
+
+	int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum];
+	if (size != 3) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray134"));
+	}
+
+	((GeometryArrayRetained)this.retained).getVertexAttr(
+				vertexAttrNum, index, vertexAttr);
+    }
+
+    /**
+     * Gets 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 source vertex index in this geometry array
+     * @param vertexAttr the vector that will receive the vertex attributes
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range.
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+     *
+     * @exception IllegalStateException if the size of the specified
+     * vertex attribute number is not 4.
+     *
+     * @since Java 3D 1.4
+     */
+    public void getVertexAttr(int vertexAttrNum, int index,
+			      Point4f vertexAttr) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray127"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
+
+	int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum];
+	if (size != 4) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray134"));
+	}
+
+	((GeometryArrayRetained)this.retained).getVertexAttr(
+				vertexAttrNum, index, vertexAttr);
+    }
+
+    /**
+     * Gets the vertex attributes associated with the vertices starting at
+     * the specified index in the specified vertex attribute number
+     * for this object.  The length of the destination
+     * array determines the number of vertex attributes copied.
+     * A maximum of <code>vertexCount-index</code> vertex attributes
+     * are copied.  If the destination array is larger than is needed
+     * to hold the vertex attributes, the excess locations in the
+     * array are not modified.  If the destination array is smaller
+     * than is needed to hold the vertex attributes, only as
+     * many vertex attributes as the array will hold are copied.
+     *
+     * @param vertexAttrNum vertex attribute number in this geometry array
+     * @param index starting source vertex index in this geometry array
+     * @param vertexAttrs destination array of 1*n, 2*n, 3*n, or 4*n values
+     * that will receive n new vertex attributes
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range.
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+     *
+     * @since Java 3D 1.4
+     */
+    public void getVertexAttrs(int vertexAttrNum, int index,
+			       float[] vertexAttrs) {
+
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray127"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
+
+	((GeometryArrayRetained)this.retained).getVertexAttrs(
+				vertexAttrNum, index, vertexAttrs);
+    }
+
+    /**
+     * Gets the vertex attributes associated with the vertices starting at
+     * the specified index in the specified vertex attribute number
+     * for this object.  The length of the destination
+     * array determines the number of vertex attributes copied.
+     * A maximum of <code>vertexCount-index</code> vertex attributes
+     * are copied.  If the destination array is larger than is needed
+     * to hold the vertex attributes, the excess locations in the
+     * array are not modified.  If the destination array is smaller
+     * than is needed to hold the vertex attributes, only as
+     * many vertex attributes as the array will hold are copied.
+     *
+     * @param vertexAttrNum vertex attribute number in this geometry array
+     * @param index starting source vertex index in this geometry array
+     * @param vertexAttrs destination array of Point2f objects that will
+     * receive the vertex attributes
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range.
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+     *
+     * @exception IllegalStateException if the size of the specified
+     * vertex attribute number is not 2.
+     *
+     * @since Java 3D 1.4
+     */
+    public void getVertexAttrs(int vertexAttrNum, int index,
+			       Point2f[] vertexAttrs) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray127"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
+
+	int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum];
+	if (size != 2) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray134"));
+	}
+
+	((GeometryArrayRetained)this.retained).getVertexAttrs(
+				vertexAttrNum, index, vertexAttrs);
+    }
+
+    /**
+     * Gets the vertex attributes associated with the vertices starting at
+     * the specified index in the specified vertex attribute number
+     * for this object.  The length of the destination
+     * array determines the number of vertex attributes copied.
+     * A maximum of <code>vertexCount-index</code> vertex attributes
+     * are copied.  If the destination array is larger than is needed
+     * to hold the vertex attributes, the excess locations in the
+     * array are not modified.  If the destination array is smaller
+     * than is needed to hold the vertex attributes, only as
+     * many vertex attributes as the array will hold are copied.
+     *
+     * @param vertexAttrNum vertex attribute number in this geometry array
+     * @param index starting source vertex index in this geometry array
+     * @param vertexAttrs destination array of Point3f objects that will
+     * receive the vertex attributes
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range.
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is <code>BY_REFERENCE</code>.
+     *
+     * @exception IllegalStateException if the size of the specified
+     * vertex attribute number is not 3.
+     *
+     * @since Java 3D 1.4
+     */
+    public void getVertexAttrs(int vertexAttrNum, int index,
+			       Point3f[] vertexAttrs) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray127"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) != 0) {
 	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
 
-	if ((format & TEXTURE_COORDINATE ) == 0)
-	    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
+	int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum];
+	if (size != 3) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray134"));
+	}
 
-        if (((((GeometryArrayRetained)this.retained).vertexFormat) &
-                (TEXTURE_COORDINATE_2 | TEXTURE_COORDINATE_4)) != 0)
-            throw new IllegalStateException(J3dI18N.getString("GeometryArray95"));
-	((GeometryArrayRetained)this.retained).getTextureCoordinates(
-					texCoordSet, index, texCoords);
+	((GeometryArrayRetained)this.retained).getVertexAttrs(
+				vertexAttrNum, index, vertexAttrs);
     }
 
-
     /**
-     * Gets the texture coordinates associated with the vertices starting at
-     * the specified index in the specified texture coordinate set
+     * Gets the vertex attributes associated with the vertices starting at
+     * the specified index in the specified vertex attribute number
      * for this object.  The length of the destination
-     * array determines the number of texture coordinates copied.
-     * A maximum of <code>vertexCount-index</code> texture coordinates
+     * array determines the number of vertex attributes copied.
+     * A maximum of <code>vertexCount-index</code> vertex attributes
      * are copied.  If the destination array is larger than is needed
-     * to hold the texture coordinates, the excess locations in the
+     * to hold the vertex attributes, the excess locations in the
      * array are not modified.  If the destination array is smaller
-     * than is needed to hold the texture coordinates, only as
-     * many texture coordinates as the array will hold are copied.
+     * than is needed to hold the vertex attributes, only as
+     * many vertex attributes as the array will hold are copied.
      *
-     * @param texCoordSet texture coordinate set in this geometry array
+     * @param vertexAttrNum vertex attribute number in this geometry array
      * @param index starting source vertex index in this geometry array
-     * @param texCoords destination array of TexCoord4f objects that will
-     * receive the texture coordinates
+     * @param vertexAttrs destination array of Point4f objects that will
+     * receive the vertex attributes
      *
      * @exception CapabilityNotSetException if the appropriate capability is
      * not set and this object is part of a live or compiled scene graph
      *
-     * @exception ArrayIndexOutOfBoundsException if none of the
-     * <code>TEXTURE_COORDINATE</code> bits are set in the
-     * <code>vertexFormat</code> or if the index or
-     * texCoordSet is out of range.
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range.
      *
      * @exception IllegalStateException if the data mode for this geometry
      * array object is <code>BY_REFERENCE</code>.
      *
-     * @exception IllegalStateException if TEXTURE_COORDINATE_2 or
-     * TEXTURE_COORDINATE_3 is specified in vertex format
+     * @exception IllegalStateException if the size of the specified
+     * vertex attribute number is not 4.
      *
-     * @since Java 3D 1.3
+     * @since Java 3D 1.4
      */
-    public void getTextureCoordinates(int texCoordSet,
-				      int index, TexCoord4f texCoords[]) {
-	if (isLiveOrCompiled())
-	    if(!this.getCapability(ALLOW_TEXCOORD_READ))
-		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray75"));
+    public void getVertexAttrs(int vertexAttrNum, int index,
+			       Point4f[] vertexAttrs) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_VERTEX_ATTR_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray127"));
+	    }
+	}
 
 	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
-	if ((format & BY_REFERENCE) != 0)
+	if ((format & BY_REFERENCE) != 0) {
 	    throw new IllegalStateException(J3dI18N.getString("GeometryArray82"));
+	}
 
-	if ((format & TEXTURE_COORDINATE ) == 0)
-	    throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("GeometryArray79"));
-
-        if (((((GeometryArrayRetained)this.retained).vertexFormat) &
-                (TEXTURE_COORDINATE_2 | TEXTURE_COORDINATE_3)) != 0)
-            throw new IllegalStateException(J3dI18N.getString("GeometryArray109"));
+	int size = ((GeometryArrayRetained)this.retained).vertexAttrSizes[vertexAttrNum];
+	if (size != 4) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray134"));
+	}
 
-	((GeometryArrayRetained)this.retained).getTextureCoordinates(
-					texCoordSet, index, texCoords);
+	((GeometryArrayRetained)this.retained).getVertexAttrs(
+				vertexAttrNum, index, vertexAttrs);
     }
 
 
@@ -3742,14 +5012,14 @@ public abstract class GeometryArray extends Geometry {
      * @exception IllegalArgumentException if either of the following are
      * true:
      * <ul>
-     * <code>initialCoordIndex < 0</code> or<br>
-     * <code>initialCoordIndex + validVertexCount > vertexCount</code><br>
+     * <code>initialCoordIndex &lt; 0</code> or<br>
+     * <code>initialCoordIndex + validVertexCount &gt; vertexCount</code><br>
      * </ul>
      * <p>
      * @exception ArrayIndexOutOfBoundsException if
      * the CoordRef array is non-null and:
      * <ul>
-     * <code>CoordRef.length</code> < <i>num_words</i> *
+     * <code>CoordRef.length</code> &lt; <i>num_words</i> *
      * (<code>initialCoordIndex + validVertexCount</code>)<br>
      * </ul>
      * where <i>num_words</i> depends on which variant of
@@ -3820,14 +5090,14 @@ public abstract class GeometryArray extends Geometry {
      * @exception IllegalArgumentException if either of the following are
      * true:
      * <ul>
-     * <code>initialColorIndex < 0</code> or<br>
-     * <code>initialColorIndex + validVertexCount > vertexCount</code><br>
+     * <code>initialColorIndex &lt; 0</code> or<br>
+     * <code>initialColorIndex + validVertexCount &gt; vertexCount</code><br>
      * </ul>
      * <p>
      * @exception ArrayIndexOutOfBoundsException if
      * the ColorRef array is non-null and:
      * <ul>
-     * <code>ColorRef.length</code> < <i>num_words</i> *
+     * <code>ColorRef.length</code> &lt; <i>num_words</i> *
      * (<code>initialColorIndex + validVertexCount</code>)<br>
      * </ul>
      * where <i>num_words</i> depends on which variant of
@@ -3898,14 +5168,14 @@ public abstract class GeometryArray extends Geometry {
      * @exception IllegalArgumentException if either of the following are
      * true:
      * <ul>
-     * <code>initialNormalIndex < 0</code> or<br>
-     * <code>initialNormalIndex + validVertexCount > vertexCount</code><br>
+     * <code>initialNormalIndex &lt; 0</code> or<br>
+     * <code>initialNormalIndex + validVertexCount &gt; vertexCount</code><br>
      * </ul>
      * <p>
      * @exception ArrayIndexOutOfBoundsException if normals
      * the NormalRef array is non-null and:
      * <ul>
-     * <code>NormalRef.length</code> < <i>num_words</i> *
+     * <code>NormalRef.length</code> &lt; <i>num_words</i> *
      * (<code>initialNormalIndex + validVertexCount</code>)<br>
      * </ul>
      * where <i>num_words</i> depends on which variant of
@@ -3979,14 +5249,14 @@ public abstract class GeometryArray extends Geometry {
      * @exception IllegalArgumentException if either of the following are
      * true:
      * <ul>
-     * <code>initialTexCoordIndex < 0</code> or<br>
-     * <code>initialTexCoordIndex + validVertexCount > vertexCount</code><br>
+     * <code>initialTexCoordIndex &lt; 0</code> or<br>
+     * <code>initialTexCoordIndex + validVertexCount &gt; vertexCount</code><br>
      * </ul>
      * <p>
      * @exception ArrayIndexOutOfBoundsException if
      * the TexCoordRef array is non-null and:
      * <ul>
-     * <code>TexCoordRef.length</code> < <i>num_words</i> *
+     * <code>TexCoordRef.length</code> &lt; <i>num_words</i> *
      * (<code>initialTexCoordIndex + validVertexCount</code>)<br>
      * </ul>
      * where <i>num_words</i> depends on which variant of
@@ -4053,6 +5323,109 @@ public abstract class GeometryArray extends Geometry {
     }
 
 
+    /**
+     * Sets the initial vertex attribute index for the specified
+     * vertex attribute number for this GeometryArray object.  This
+     * index specifies the first vertex attribute within the array
+     * of vertex attributes referenced by this geometry array that
+     * is actually used in rendering or other operations such as
+     * picking and collision.  This attribute is initialized to 0.
+     * This attribute is only used when the data mode for this
+     * geometry array object is <code>BY_REFERENCE</code>
+     * and is <i>not</i> </code>INTERLEAVED</code>.
+     *
+     * @param vertexAttrNum vertex attribute number in this geometry array
+     * @param initialVertexAttrIndex the new initial vertex attribute index.
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     * <p>
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is not <code>BY_REFERENCE</code> or if the data mode
+     * is <code>INTERLEAVED</code>.
+     * <p>
+     * @exception IllegalArgumentException if either of the following are
+     * true:
+     * <ul>
+     * <code>initialVertexAttrIndex &lt; 0</code> or<br>
+     * <code>initialVertexAttrIndex + validVertexCount &gt; vertexCount</code><br>
+     * </ul>
+     * <p>
+     * @exception ArrayIndexOutOfBoundsException if
+     * the VertexAttrRef array is non-null and:
+     * <ul>
+     * <code>VertexAttrRef.length</code> &lt; <i>num_words</i> *
+     * (<code>initialVertexAttrIndex + validVertexCount</code>)<br>
+     * </ul>
+     * where <i>num_words</i> is the size of the specified
+     * vertexAttrNum (1, 2, 3, or 4).
+     * <p>
+     * @exception ArrayIndexOutOfBoundsException if vertexAttrNum is
+     * out of range.
+     *
+     * @since Java 3D 1.4
+     */
+    public void setInitialVertexAttrIndex(int vertexAttrNum,
+					  int initialVertexAttrIndex) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_COUNT_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray90"));
+	    }
+	}
+
+        if (initialVertexAttrIndex < 0) {
+	    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray97"));
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) == 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+	}
+
+	if ((format & INTERLEAVED) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+	}
+
+	((GeometryArrayRetained)this.retained).setInitialVertexAttrIndex(
+		vertexAttrNum, initialVertexAttrIndex);
+
+	// NOTE: the check for initialVertexAttrIndex + validVertexCount >
+	// vertexCount needs to be done in the retained method
+    }
+
+
+    /**
+     * Gets the initial vertex attribute index for the specified
+     * vertex attribute number for this GeometryArray object.
+     * This attribute is only used when the data mode for this
+     * geometry array object is <code>BY_REFERENCE</code>
+     * and is <i>not</i> </code>INTERLEAVED</code>.
+     *
+     * @param vertexAttrNum vertex attribute number in this geometry array
+     *
+     * @return the current initial vertex attribute index for the specified
+     * vertex attribute number
+     *
+     * @exception ArrayIndexOutOfBoundsException if vertexAttrNum is
+     * out of range.
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @since Java 3D 1.4
+     */
+    public int getInitialVertexAttrIndex(int vertexAttrNum) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_COUNT_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray91"));
+	    }
+	}
+
+	return ((GeometryArrayRetained)this.retained).getInitialVertexAttrIndex(
+		vertexAttrNum);
+    }
+
+
     /**
      * Sets the coordinate buffer reference to the specified
      * buffer object.  The buffer contains either a java.nio.FloatBuffer
@@ -4081,7 +5454,7 @@ public abstract class GeometryArray extends Geometry {
      * java.nio.FloatBuffer or a java.nio.DoubleBuffer object.
      *
      * @exception ArrayIndexOutOfBoundsException if
-     * <code>coords.getBuffer().limit() <
+     * <code>coords.getBuffer().limit() &lt;
      * 3 * (initialCoordIndex + validVertexCount)</code>.
      *
      * @exception ArrayIndexOutOfBoundsException if this GeometryArray
@@ -4164,7 +5537,7 @@ public abstract class GeometryArray extends Geometry {
      * @exception IllegalArgumentException if the specified array is
      * non-null and any other coordinate reference is also non-null.
      * @exception ArrayIndexOutOfBoundsException if
-     * <code>coords.length < 3 * (initialCoordIndex + validVertexCount)</code>.
+     * <code>coords.length &lt; 3 * (initialCoordIndex + validVertexCount)</code>.
      *
      * @exception ArrayIndexOutOfBoundsException if this GeometryArray
      * object is a subclass of IndexedGeometryArray, and any element
@@ -4253,7 +5626,7 @@ public abstract class GeometryArray extends Geometry {
      * @exception IllegalArgumentException if the specified array is
      * non-null and any other coordinate reference is also non-null.
      * @exception ArrayIndexOutOfBoundsException if
-     * <code>coords.length < 3 * (initialCoordIndex + validVertexCount)</code>.
+     * <code>coords.length &lt; 3 * (initialCoordIndex + validVertexCount)</code>.
      *
      * @exception ArrayIndexOutOfBoundsException if this GeometryArray
      * object is a subclass of IndexedGeometryArray, and any element
@@ -4456,7 +5829,7 @@ public abstract class GeometryArray extends Geometry {
      * @exception ArrayIndexOutOfBoundsException if none of the
      * <code>COLOR</code> bits are set in the
      * <code>vertexFormat</code>, or if
-     * <code>colors.getBuffer().limit() < </code> <i>num_words</i> <code> *
+     * <code>colors.getBuffer().limit() &lt; </code> <i>num_words</i> <code> *
      * (initialColorIndex + validVertexCount)</code>,
      * where <i>num_words</i> is 3 or 4 depending on the vertex color format.
      *
@@ -4549,7 +5922,7 @@ public abstract class GeometryArray extends Geometry {
      * @exception ArrayIndexOutOfBoundsException if none of the
      * <code>COLOR</code> bits are set in the
      * <code>vertexFormat</code>, or if
-     * <code>colors.length < </code> <i>num_words</i> <code> *
+     * <code>colors.length &lt; </code> <i>num_words</i> <code> *
      * (initialColorIndex + validVertexCount)</code>,
      * where <i>num_words</i> is 3 or 4 depending on the vertex color format.
      *
@@ -4646,7 +6019,7 @@ public abstract class GeometryArray extends Geometry {
      * @exception ArrayIndexOutOfBoundsException if none of the
      * <code>COLOR</code> bits are set in the
      * <code>vertexFormat</code>, or if
-     * <code>colors.length < </code> <i>num_words</i> <code> *
+     * <code>colors.length &lt; </code> <i>num_words</i> <code> *
      * (initialColorIndex + validVertexCount)</code>,
      * where <i>num_words</i> is 3 or 4 depending on the vertex color format.
      *
@@ -4978,7 +6351,7 @@ public abstract class GeometryArray extends Geometry {
      * @exception ArrayIndexOutOfBoundsException if
      * <code>NORMALS</code> bit is not set in the
      * <code>vertexFormat</code>, or if
-     * <code>normals.getBuffer().limit() <
+     * <code>normals.getBuffer().limit() &lt;
      * 3 * (initialNormalIndex + validVertexCount)</code>.
      *
      * @exception ArrayIndexOutOfBoundsException if this GeometryArray
@@ -5064,7 +6437,7 @@ public abstract class GeometryArray extends Geometry {
      * @exception ArrayIndexOutOfBoundsException if
      * <code>NORMALS</code> bit is not set in the
      * <code>vertexFormat</code>, or if
-     * <code>normals.length < 3 * (initialNormalIndex + validVertexCount)</code>.
+     * <code>normals.length &lt; 3 * (initialNormalIndex + validVertexCount)</code>.
      *
      * @exception ArrayIndexOutOfBoundsException if this GeometryArray
      * object is a subclass of IndexedGeometryArray, and any element
@@ -5186,7 +6559,7 @@ public abstract class GeometryArray extends Geometry {
 
 
     /**
-     * Sets the texture coordinate array reference for the specified
+     * Sets the texture coordinate buffer reference for the specified
      * texture coordinate set to the
      * specified buffer object.  The buffer contains a java.nio.FloatBuffer
      * object containing <i>s</i>,
@@ -5223,7 +6596,7 @@ public abstract class GeometryArray extends Geometry {
      * <code>TEXTURE_COORDINATE</code> bits are set in the
      * <code>vertexFormat</code>, or if texCoordSet is out of range,
      * or if
-     * <code>texCoords.getBuffer().limit() < </code> <i>num_words</i>
+     * <code>texCoords.getBuffer().limit() &lt; </code> <i>num_words</i>
      * <code> * (initialTexCoordIndex + validVertexCount)</code>,
      * where <i>num_words</i> is 2, 3, or 4 depending on the vertex
      * texture coordinate format.
@@ -5336,7 +6709,7 @@ public abstract class GeometryArray extends Geometry {
      * <code>TEXTURE_COORDINATE</code> bits are set in the
      * <code>vertexFormat</code>, or if texCoordSet is out of range,
      * or if
-     * <code>texCoords.length < </code> <i>num_words</i> <code> *
+     * <code>texCoords.length &lt; </code> <i>num_words</i> <code> *
      * (initialTexCoordIndex + validVertexCount)</code>,
      * where <i>num_words</i> is 2, 3, or 4 depending on the vertex
      * texture coordinate format.
@@ -5543,11 +6916,265 @@ public abstract class GeometryArray extends Geometry {
 							texCoordSet);
     }
 
+
+    /**
+     * Sets the vertex attribute buffer reference for the specified
+     * vertex attribute number to the specified buffer object. The
+     * buffer contains a java.nio.FloatBuffer object containing 1, 2,
+     * 3, or 4 values for each vertex (for a total of 1*<i>n</i>,
+     * 2*<i>n</i>, 3*<i>n</i>, or 4*<i>n</i> values, where <i>n</i> is
+     * the number of vertices).
+     * If the vertexAttr buffer reference is null and vertex
+     * attributes are enabled (that is, the vertexFormat includes
+     * <code>VERTEX_ATTRIBUTES</code>), the entire geometry array
+     * object is treated as if it were null--any Shape3D node that
+     * uses this geometry array will not be drawn.
+     *
+     * @param vertexAttrNum vertex attribute number in this geometry array
+     *
+     * @param vertexAttrs a J3DBuffer object to which a reference will
+     * be set.  The buffer contains an NIO buffer of 1*<i>n</i>,
+     * 2*<i>n</i>, 3*<i>n</i>, or 4*<i>n</i> float values.
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is not <code>BY_REFERENCE</code>,
+     * is not <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+     *
+     * @exception IllegalArgumentException if the java.nio.Buffer
+     * contained in the specified J3DBuffer is not a
+     * java.nio.FloatBuffer object.
+     *
+     * @exception ArrayIndexOutOfBoundsException if vertexAttrNum is out of
+     * range, or if
+     * <code>vertexAttrs.getBuffer().limit() &lt; </code> <i>num_words</i>
+     * <code> * (initialVertexAttrIndex + validVertexCount)</code>,
+     * where <i>num_words</i> is the size of the specified
+     * vertexAttrNum (1, 2, 3, or 4).
+     *
+     * @exception ArrayIndexOutOfBoundsException if this GeometryArray
+     * object is a subclass of IndexedGeometryArray, and any element
+     * in the range
+     * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+     * in the vertex attribute index array is greater than or equal to the
+     * number of vertices defined by the vertexAttrs object,
+     * <code>vertexAttrs.getBuffer().limit() / </code> <i>num_words</i>.
+     *
+     * @since Java 3D 1.4
+     */
+    public void setVertexAttrRefBuffer(int vertexAttrNum, J3DBuffer vertexAttrs) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_REF_DATA_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+
+	if ((format & USE_NIO_BUFFER) == 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray118"));
+	}
+
+	if ((format & INTERLEAVED) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+	}
+
+	((GeometryArrayRetained)this.retained).setVertexAttrRefBuffer(
+		vertexAttrNum, vertexAttrs);
+    }
+
+
+    /**
+     * Gets the vertex attribute array buffer reference for the specified
+     * vertex attribute number.
+     *
+     * @param vertexAttrNum vertex attribute number in this geometry array
+     *
+     * @return the current vertex attribute array buffer reference
+     * for the specified vertex attribute number
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is not <code>BY_REFERENCE</code>,
+     * is not <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+     *
+     * @exception ArrayIndexOutOfBoundsException if vertexAttrNum is out
+     *  of range.
+     *
+     * @since Java 3D 1.4
+     */
+    public J3DBuffer getVertexAttrRefBuffer(int vertexAttrNum) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+		!this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+
+	if ((format & USE_NIO_BUFFER) == 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray118"));
+	}
+
+	if ((format & INTERLEAVED) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+	}
+
+	return ((GeometryArrayRetained)this.retained).getVertexAttrRefBuffer(vertexAttrNum);
+    }
+
+
+    /*
+     * XXXX: add the following to the javadoc if we ever add double-precision
+     * methods for vertex attribtues.
+     *
+     *-----------------------------------------------------------------
+     * Only one of <code>vertexAttrRefFloat</code>, or
+     * <code>vertexAttrRefDouble</code> may be non-null (or they may
+     * all be null).  An attempt to set more than one of these
+     * attributes to a non-null reference will result in an exception
+     * being thrown.
+     *
+     * If all vertexAttr array references are null and vertex
+     * ...
+     * @exception IllegalArgumentException if the specified array is
+     * non-null and any other vertexAttr reference is also non-null.
+     * ...
+     *-----------------------------------------------------------------
+     */
+
+    /**
+     * Sets the float vertex attribute array reference for the
+     * specified vertex attribute number to the specified array.  The
+     * array contains 1, 2, 3, or 4 floating-point values for each
+     * vertex (for a total of 1*<i>n</i>, 2*<i>n</i>, 3*<i>n</i>, or
+     * 4*<i>n</i> values, where <i>n</i> is the number of vertices).
+     *
+     * If the vertexAttr array reference is null and vertex
+     * attributes are enabled (that is, the vertexFormat includes
+     * <code>VERTEX_ATTRIBUTES</code>), the entire geometry array
+     * object is treated as if it were null--any Shape3D node that
+     * uses this geometry array will not be drawn.
+     *
+     * @param vertexAttrNum vertex attribute number in this geometry array
+     *
+     * @param vertexAttrs an array of 1*<i>n</i>, 2*<i>n</i>,
+     * 3*<i>n</i>, or 4*<i>n</i> values to which a reference will be
+     * set.
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is not <code>BY_REFERENCE</code>,
+     * is <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+     *
+     * @exception ArrayIndexOutOfBoundsException if vertexAttrNum is
+     * out of range, or if
+     * <code>vertexAttrs.length &lt; </code> <i>num_words</i> <code> *
+     * (initialVertexAttrIndex + validVertexCount)</code>,
+     * where <i>num_words</i> is the size of the specified
+     * vertexAttrNum (1, 2, 3, or 4).
+     *
+     * @exception ArrayIndexOutOfBoundsException if this GeometryArray
+     * object is a subclass of IndexedGeometryArray, and any element
+     * in the range
+     * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+     * in the vertex attribute index array is greater than or equal to the
+     * number of vertices defined by the vertexAttrs array,
+     * <code>vertexAttrs.length / </code> <i>num_words</i>.
+     *
+     * @since Java 3D 1.4
+     */
+    public void setVertexAttrRefFloat(int vertexAttrNum, float[] vertexAttrs) {
+
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_REF_DATA_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray86"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) == 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+	}
+
+	if ((format & USE_NIO_BUFFER) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+	}
+
+	if ((format & INTERLEAVED) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+	}
+
+	((GeometryArrayRetained)this.retained).setVertexAttrRefFloat(
+		vertexAttrNum, vertexAttrs);
+
+	// NOTE: the checks for multiple non-null references, and the
+	// array length check need to be done in the retained method
+    }
+
+
+    /**
+     * Gets the float vertex attribute array reference for the specified
+     * vertex attribute number.
+     *
+     * @param vertexAttrNum vertex attribute number in this geometry array
+     *
+     * @return the current float vertex attribute array reference
+     * for the specified vertex attribute number
+     *
+     * @exception CapabilityNotSetException if the appropriate capability is
+     * not set and this object is part of a live or compiled scene graph
+     *
+     * @exception IllegalStateException if the data mode for this geometry
+     * array object is not <code>BY_REFERENCE</code>,
+     * is <code>USE_NIO_BUFFER</code>, or is <code>INTERLEAVED</code>.
+     *
+     * @exception ArrayIndexOutOfBoundsException if vertexAttrNum is
+     * out of range.
+     *
+     * @since Java 3D 1.4
+     */
+    public float[] getVertexAttrRefFloat(int vertexAttrNum) {
+
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_REF_DATA_READ) &&
+		!this.getCapability(J3D_1_2_ALLOW_REF_DATA_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("GeometryArray87"));
+	    }
+	}
+
+	int format = ((GeometryArrayRetained)this.retained).vertexFormat;
+	if ((format & BY_REFERENCE) == 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray83"));
+	}
+
+	if ((format & USE_NIO_BUFFER) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray119"));
+	}
+
+	if ((format & INTERLEAVED) != 0) {
+	    throw new IllegalStateException(J3dI18N.getString("GeometryArray84"));
+	}
+
+	return ((GeometryArrayRetained)this.retained).getVertexAttrRefFloat(
+		vertexAttrNum);
+    }
+
+
     /**
      * Sets the interleaved vertex array reference to the specified
      * array.  The vertex components must be stored in a predetermined
      * order in the array.  The order is: texture coordinates, colors,
-     * normals, and positional coordinates.  In the case of texture
+     * normals, and positional coordinates.
+     * Vertex attributes are not supported in interleaved mode.
+     * In the case of texture
      * coordinates, the values for each texture coordinate set
      * are stored in order from 0 through texCoordSetCount-1.  Only those
      * components that are enabled appear in the vertex.  The number
@@ -5584,7 +7211,7 @@ public abstract class GeometryArray extends Geometry {
      * or is <code>USE_NIO_BUFFER</code>.
      *
      * @exception ArrayIndexOutOfBoundsException if
-     * <code>vertexData.length</code> < <i>words_per_vertex</i> *
+     * <code>vertexData.length</code> &lt; <i>words_per_vertex</i> *
      * (<code>initialVertexIndex + validVertexCount</code>),
      * where <i>words_per_vertex</i> depends on which formats are enabled.
      *
@@ -5652,7 +7279,9 @@ public abstract class GeometryArray extends Geometry {
      * buffer object. The buffer must contain a java.nio.FloatBuffer object.
      * The vertex components must be stored in a predetermined
      * order in the buffer.  The order is: texture coordinates, colors,
-     * normals, and positional coordinates.  In the case of texture
+     * normals, and positional coordinates.
+     * Vertex attributes are not supported in interleaved mode.
+     * In the case of texture
      * coordinates, the values for each texture coordinate set
      * are stored in order from 0 through texCoordSetCount-1.  Only those
      * components that are enabled appear in the vertex.  The number
@@ -5696,7 +7325,7 @@ public abstract class GeometryArray extends Geometry {
      * java.nio.FloatBuffer object.
      *
      * @exception ArrayIndexOutOfBoundsException if
-     * <code>vertexData.getBuffer().limit()</code> < <i>words_per_vertex</i> *
+     * <code>vertexData.getBuffer().limit()</code> &lt; <i>words_per_vertex</i> *
      * (<code>initialVertexIndex + validVertexCount</code>),
      * where <i>words_per_vertex</i> depends on which formats are enabled.
      *
diff --git a/src/classes/share/javax/media/j3d/GeometryArrayRetained.java b/src/classes/share/javax/media/j3d/GeometryArrayRetained.java
index fce41cd..dcc380c 100644
--- a/src/classes/share/javax/media/j3d/GeometryArrayRetained.java
+++ b/src/classes/share/javax/media/j3d/GeometryArrayRetained.java
@@ -36,9 +36,7 @@ import com.sun.j3d.internal.DoubleBufferWrapper;
 
 abstract class GeometryArrayRetained extends GeometryRetained{
 
-
-
-    // TODO: Memory footprint reduction. Should have separate object to
+    // 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
@@ -89,6 +87,12 @@ abstract class GeometryArrayRetained extends GeometryRetained{
     // 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
     float[] lastAlpha = new float[1];
     float lastScreenAlpha = -1;
@@ -132,7 +136,8 @@ abstract class GeometryArrayRetained extends GeometryRetained{
     int initialCoordIndex = 0;
     int initialColorIndex = 0;
     int initialNormalIndex = 0;
-    int initialTexCoordIndex[] = null;
+    int[] initialTexCoordIndex = null;
+    int[] initialVertexAttrIndex = null;
     int initialVertexIndex = 0;
 
 
@@ -174,6 +179,19 @@ abstract class GeometryArrayRetained extends GeometryRetained{
     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;
@@ -181,9 +199,10 @@ abstract class GeometryArrayRetained extends GeometryRetained{
     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 TEXCOORD_FLOAT = 0x20; 
+    static final int VATTR_FLOAT = 0x40;
+
+
     // used by "by reference" normals
     float[] floatRefNormals = null;
     Vector3f[] v3fRefNormals = null;
@@ -192,6 +211,13 @@ abstract class GeometryArrayRetained extends GeometryRetained{
     J3DBuffer normalRefBuffer = null;
     FloatBufferWrapper floatBufferRefNormals = null;
 
+    // used for "by reference" vertex attrs
+    float[][] floatRefVertexAttrs = null;
+
+    // Used for NIO buffer vertex attrs
+    J3DBuffer[] vertexAttrsRefBuffer = null;
+    FloatBufferWrapper[] floatBufferRefVertexAttrs = null;
+    Object[] nioFloatBufferRefVertexAttrs = null;
 
     // used by "by reference" tex coords
     Object[] refTexCoords = null;
@@ -212,10 +238,10 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 
     // pointers used, when transparency is turned on
     // or when its an object such as C3F, P3F etc ..
-    // TODO: Update this for J3DBuffer
     float[] mirrorFloatRefCoords = null;
     double[] mirrorDoubleRefCoords = null;
     float[] mirrorFloatRefNormals = null;
+    float[][] mirrorFloatRefVertexAttrs = null;
     float[] mirrorFloatRefTexCoords = null;
     Object[] mirrorRefTexCoords = null;
 
@@ -223,14 +249,9 @@ abstract class GeometryArrayRetained extends GeometryRetained{
     byte[][] mirrorUnsignedByteRefColors= new byte[1][];
     float[][] mirrorInterleavedColorPointer = null;
 
-    // This native method builds a native representation of this object, then
-    // returns the nativeId.
-    native int build(int geoType);
-
     // boolean to determine if a mirror was allocated
     int mirrorVertexAllocated = 0;
     int mirrorColorAllocated = 0;
-    boolean mirrorTexCoordAllocated = false;
     boolean mirrorNormalAllocated = false;
 
     // Some dirty bits for GeometryArrays
@@ -240,12 +261,13 @@ abstract class GeometryArrayRetained extends GeometryRetained{
     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 STRIPCOUNT_CHANGED 	= 0x40;
+    static final int VATTR_CHANGED 		= 0x80;
     static final int VERTEX_CHANGED             = COORDINATE_CHANGED |
                                                   NORMAL_CHANGED |
                                                   COLOR_CHANGED |
-                                                  TEXTURE_CHANGED;
-                      
+                                                  TEXTURE_CHANGED |
+                                                  VATTR_CHANGED;
 
     static final int defaultTexCoordSetMap[] = {0};
     int texCoordSetCount = 0;
@@ -255,6 +277,11 @@ abstract class GeometryArrayRetained extends GeometryRetained{
     // 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.
@@ -936,26 +963,6 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 
     }
 
-    // TODO: may not need this function in NIO buffer version
-    // setup mirror vertex pointers for J3DBuffer version
-    void setupMirrorVertexPointerNIOBuffer(int vType) {
-	int i, index = 0;
-	switch(vType) {
-	case PF:
-
-	    break;
-	case PD:
-
-	    break;
-	    
-	    // do not need to handle P3F and P3D case in NIO buffer version
-	default:
-	    break;
-	    
-	}
-	
-    }
-
     // If turned transparent the first time, then force it to allocate
     void setupMirrorInterleavedColorPointer(boolean force) {
 	int index, length, offset;
@@ -1266,7 +1273,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		vertexType |= CF;
 		vertexType &= ~CUB;
 		if (c4fAllocated == 0 && !force) {
-		    // TODO: make suren mirrorFloatRefColors[0] is set right
+		    // NOTE: make suren mirrorFloatRefColors[0] is set right
 		    mirrorFloatRefColors[0] = null; 
 		    mirrorColorAllocated &= ~CF;
 		}
@@ -1302,7 +1309,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		vertexType |= CUB;
 		vertexType &= ~CF;
 		if (c4fAllocated == 0 && !force) {
-		    // TODO: make suren mirrorUnsignedByteRefColors[0] is set right
+		    // NOTE: make sure mirrorUnsignedByteRefColors[0] is set right
 		    mirrorUnsignedByteRefColors[0] = null;
 		    mirrorColorAllocated &= ~CUB;;
 		}
@@ -1387,42 +1394,58 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 
     void setupMirrorTexCoordPointer(int type) {
 	for (int i = 0; i < texCoordSetCount; i++) {
-	     setupMirrorTexCoordPointer(i, type);
+	     doSetupMirrorTexCoordPointer(i, type);
 	}
-    }
 
+        validateTexCoordPointerType();
+    }
+    
     void setupMirrorTexCoordPointer(int texCoordSet, int type) {
-	int i, index;
+        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;
+        }
 
-        if (mirrorRefTexCoords == null)
-	    mirrorRefTexCoords = new Object[texCoordSetCount];
+        // 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: 
-	    if (refTexCoords[texCoordSet] == null) {
-		if ((vertexType & TEXCOORD_DEFINED) == TF) {
-		    vertexType &= ~TF;
-		    mirrorRefTexCoords[texCoordSet] =  null;
-		    mirrorTexCoordAllocated = false;
-		}
-	    }
-	    else {
-		vertexType |= TF;
-		mirrorRefTexCoords[texCoordSet] =  refTexCoords[texCoordSet];
-		mirrorTexCoordAllocated = false;
-	    }
+        switch (type) { 
+	case TF:
+            texCoordType = TF;
+            mirrorRefTexCoords[texCoordSet] = refTexCoords[texCoordSet];
 	    break;
-	case T2F:
+
+        case T2F:
+            texCoordType = T2F;
 	    t2fRefTexCoords = (TexCoord2f[])refTexCoords[texCoordSet];
 
 	    if (t2fRefTexCoords == null) {
-		if ((vertexType & TEXCOORD_DEFINED) == T2F) {
-		    vertexType &= ~T2F;
-		}
-		return;
-	    }
-	    else {
-		vertexType |= T2F;
+                mirrorRefTexCoords[texCoordSet] = null;
+		break;
 	    }
 
             mirrorFloatRefTexCoords = (float[])mirrorRefTexCoords[texCoordSet];
@@ -1441,19 +1464,15 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		mirrorFloatRefTexCoords[index++] = t2fRefTexCoords[i].x;
 		mirrorFloatRefTexCoords[index++] = t2fRefTexCoords[i].y;
 	    }
-	    mirrorTexCoordAllocated = true;
 	    break;
-	case T3F: 
 
+	case T3F:
+            texCoordType = T3F;
 	    t3fRefTexCoords = (TexCoord3f[])refTexCoords[texCoordSet];
-	    if (t3fRefTexCoords == null) {
-		if ((vertexType & TEXCOORD_DEFINED) == T3F) {
-		    vertexType &= ~T3F;
-		}
-		return;
-	    }
-	    else {
-		vertexType |= T3F;
+
+            if (t3fRefTexCoords == null) {
+                mirrorRefTexCoords[texCoordSet] = null;
+		break;
 	    }
 
             mirrorFloatRefTexCoords = (float[])mirrorRefTexCoords[texCoordSet];
@@ -1473,142 +1492,240 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		mirrorFloatRefTexCoords[index++] = t3fRefTexCoords[i].y;
 		mirrorFloatRefTexCoords[index++] = t3fRefTexCoords[i].z;
 	    }
-	    mirrorTexCoordAllocated = true;
 	    break;
 
 	default:
 	    break;
 	}
     }
-		
 
-    void createGeometryArrayData(int vertexCount, int vertexFormat)
-    {
+    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 (nioFloatBufferRefVertexAttrs[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);
+				    defaultTexCoordSetMap);
 	} else {
 	    createGeometryArrayData(vertexCount, vertexFormat, 0, null);
 	}
     }
 
     void createGeometryArrayData(int vertexCount, int vertexFormat,
-				 int texCoordSetCount, int[] texCoordSetMap) 
-    {
+				 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;
-	this.texCoordSetMap = texCoordSetMap;
+	if (texCoordSetMap == null) {
+	    this.texCoordSetMap = null;
+	}
+	else {
+	    this.texCoordSetMap = (int[])texCoordSetMap.clone();
+	}
+
+        this.vertexAttrCount = vertexAttrCount;
+	if (vertexAttrSizes == null) {
+	    this.vertexAttrSizes = null;
+	}
+	else {
+	    this.vertexAttrSizes = (int[])vertexAttrSizes.clone();
+	}
+
+        this.vertexAttrStride = this.vertexAttrStride();
 	this.stride = this.stride();
+
+	this.vertexAttrOffsets = this.vertexAttrOffsets();
 	this.texCoordSetMapOffset = this.texCoordSetMapOffset();
-	this.textureOffset = 0;
+	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 Object[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 FloatBufferWrapper[vertexAttrCount];
+                    this.nioFloatBufferRefVertexAttrs = new Object[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;
 
     }
 
     // used for GeometryArrays by Copy or interleaved
-    native void execute(long ctx,
-			GeometryArrayRetained geo, int geo_type, 
-			boolean isNonUniformScale,
-			boolean useAlpha,
-			boolean multiScreen,
-			boolean ignoreVertexColors,
-			int startVIndex, int vcount, int vformat, 
-			int texCoordSetCount, int texCoordSetMap[],
-			int texCoordSetMapLen,
-		        int[] texCoordSetOffset,
-			int numActiveTexUnitState,
-			int[] texUnitStateMap,
-			float[] varray, float[] cdata, int texUnitIndex, int cdirty);
-
-
-
+    private native void execute(long ctx,
+            GeometryArrayRetained geo, int geo_type,
+            boolean isNonUniformScale,
+            boolean useAlpha,
+            boolean multiScreen,
+            boolean ignoreVertexColors,
+            int startVIndex, int vcount, int vformat,
+            int texCoordSetCount, int texCoordSetMap[],
+            int texCoordSetMapLen,
+            int[] texCoordSetOffset,
+            int numActiveTexUnitState,
+            int[] texUnitStateMap,
+            int vertexAttrCount, int[] vertexAttrSizes,
+            float[] varray, float[] cdata, int texUnitIndex, int cdirty);
 
     // used by GeometryArray by Reference with java arrays
-    native void executeVA(long ctx,
-			  GeometryArrayRetained geo, int geo_type, 
-			  boolean isNonUniformScale, 
-			  boolean multiScreen,
-			  boolean ignoreVertexColors,
-			  int vcount,
-			  int vformat,
-			  int vdefined,
-			  int coordIndex, float[] vfcoords, double[] vdcoords,
-			  int colorIndex, float[] cfdata, byte[] cbdata,
-			  int normalIndex, float[] ndata,
-			  int pass, int texcoordmaplength, 
-			  int[] texcoordoffset, 
-			  int numActiveTexUnitState, int[] texunitstatemap,
-			  int[] texIndex, int texstride, Object[] texCoords,
-			  int cdirty);
-
- 
+    private native void executeVA(long ctx,
+            GeometryArrayRetained geo, int geo_type,
+            boolean isNonUniformScale,
+            boolean multiScreen,
+            boolean ignoreVertexColors,
+            int vcount,
+            int vformat,
+            int vdefined,
+            int coordIndex, float[] vfcoords, double[] vdcoords,
+            int colorIndex, float[] cfdata, byte[] cbdata,
+            int normalIndex, float[] ndata,
+            int vertexAttrCount, int[] vertexAttrSizes,
+            int[] vertexAttrIndex, float[][] vertexAttrData,
+            int pass, int texcoordmaplength,
+            int[] texcoordoffset,
+            int numActiveTexUnitState, int[] texunitstatemap,
+            int[] texIndex, int texstride, Object[] texCoords,
+            int cdirty);
 
     // used by GeometryArray by Reference with NIO buffer
-    native void executeVABuffer(long ctx,
-				GeometryArrayRetained geo, int geo_type, 
-				boolean isNonUniformScale, 
-				boolean multiScreen,
-				boolean ignoreVertexColors,
-				int vcount,
-				int vformat,
-				int vdefined, 
-				int coordIndex,
-				Object vcoords,
-				int colorIndex,
-				Object cdataBuffer,
-				float[] cfdata, byte[] cbdata,
-				int normalIndex, Object ndata,
-				int pass, int texcoordmaplength, 
-				int[] texcoordoffset, 
-				int numActiveTexUnitState, int[] texunitstatemap,
-				int[] texIndex, int texstride, Object[] texCoords,
-				int cdirty);
+    private native void executeVABuffer(long ctx,
+            GeometryArrayRetained geo, int geo_type,
+            boolean isNonUniformScale,
+            boolean multiScreen,
+            boolean ignoreVertexColors,
+            int vcount,
+            int vformat,
+            int vdefined,
+            int coordIndex,
+            Object vcoords,
+            int colorIndex,
+            Object cdataBuffer,
+            float[] cfdata, byte[] cbdata,
+            int normalIndex, Object ndata,
+            int vertexAttrCount, int[] vertexAttrSizes,
+            int[] vertexAttrIndex, Object[] vertexAttrData,
+            int pass, int texcoordmaplength,
+            int[] texcoordoffset,
+            int numActiveTexUnitState, int[] texunitstatemap,
+            int[] texIndex, int texstride, Object[] texCoords,
+            int cdirty);
 
     // used by GeometryArray by Reference in interleaved format with NIO buffer
-    native void executeInterleavedBuffer(long ctx,
-					 GeometryArrayRetained geo, int geo_type, 
-					 boolean isNonUniformScale,
-					 boolean useAlpha,
-					 boolean multiScreen,
-					 boolean ignoreVertexColors,
-					 int startVIndex, int vcount, int vformat, 
-					 int texCoordSetCount, int texCoordSetMap[],
-					 int texCoordSetMapLen,
-					 int[] texCoordSetOffset,
-					 int numActiveTexUnitState,
-					 int[] texUnitStateMap,
-					 Object varray, float[] cdata, int texUnitIndex, int cdirty);
-
-
-			  
-    native void setVertexFormat(int vformat, boolean useAlpha, 
-				boolean ignoreVertexColors, long ctx);
-    native void disableGlobalAlpha(long ctx, int vformat, 
-				   boolean useAlpha, 
-				   boolean ignoreVertexColors);
+    private native void executeInterleavedBuffer(long ctx,
+            GeometryArrayRetained geo, int geo_type,
+            boolean isNonUniformScale,
+            boolean useAlpha,
+            boolean multiScreen,
+            boolean ignoreVertexColors,
+            int startVIndex, int vcount, int vformat,
+            int texCoordSetCount, int texCoordSetMap[],
+            int texCoordSetMapLen,
+            int[] texCoordSetOffset,
+            int numActiveTexUnitState,
+            int[] texUnitStateMap,
+            Object varray, float[] cdata, int texUnitIndex, int cdirty);
+
+    private native void setVertexFormat(long ctx,
+            int vformat, boolean useAlpha, boolean ignoreVertexColors);
+
+    private native void disableGlobalAlpha(long ctx, int vformat,
+            boolean useAlpha, boolean ignoreVertexColors);
 
     void setVertexFormat(boolean useAlpha, boolean ignoreVC, long ctx) {
-	setVertexFormat(vertexFormat, useAlpha, ignoreVC, ctx);
+	setVertexFormat(ctx, vertexFormat, useAlpha, ignoreVC);
     }
     
     void disableGlobalAlpha(long ctx, boolean useAlpha, boolean ignoreVC) {
@@ -1628,6 +1745,12 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	    return mirrorFloatRefColors[0];
 	}
 
+	// Issue 113
+	// TODO: Fix this for screen > 0, for now just ignore transparency
+	if (screen > 0) {
+	    return mirrorFloatRefColors[0];
+	}
+
 	// update alpha only if vertex format includes alpha
 	if (((vertexFormat | c4fAllocated) & GeometryArray.WITH_ALPHA) == 0)
 	    return mirrorFloatRefColors[0];
@@ -1796,6 +1919,12 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	    return mirrorUnsignedByteRefColors[0];
 	}
 
+	// Issue 113
+	// TODO: Fix this for screen > 0, for now just ignore transparency
+	if (screen > 0) {
+	    return mirrorUnsignedByteRefColors[0];
+	}
+
 	// update alpha only if vertex format includes alpha
 	if (((vertexFormat | c4fAllocated) & GeometryArray.WITH_ALPHA) == 0)
 	    return mirrorUnsignedByteRefColors[0];
@@ -1951,6 +2080,13 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	    return retVal;
 	}
 
+	// Issue 113
+	// TODO: Fix this for screen > 0, for now just ignore transparency
+	if (screen > 0) {
+	    retVal[1] = vertexData;
+	    return retVal;
+	}
+
 	// update alpha only if vertex format includes alpha
 	if ((vertexFormat & GeometryArray.COLOR) == 0) {
 	    retVal[1] = vertexData;
@@ -1980,7 +2116,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 
 	// allocate a copy of the vertex data for the screen if needed.
 	// this piece of code is mainly for multi-screens case
-	// TODO: this might not too much data for just to update alpha
+	// NOTE: this might not too much data for just to update alpha
 	if (mvertexData == null || mvertexData.length <= screen) {
 
 	    float[][] cfData = new float[screen + 1][];
@@ -2101,6 +2237,13 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	    return retVal;
 	}
 
+	// Issue 113
+	// TODO: Fix this for screen > 0, for now just ignore transparency
+	if (screen > 0) {
+	    retVal[1] = null;
+	    return retVal;
+	}
+
 	// update alpha only if vertex format includes alpha
 	if (((vertexFormat | c4fAllocated) & GeometryArray.COLOR) == 0) {
 	    retVal[1] = mirrorInterleavedColorPointer[0];
@@ -2131,7 +2274,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 
 	// allocate a copy of the vertex data for the screen if needed.
 	// this piece of code is mainly for multi-screens case
-	// TODO: this might not too much data for just to update alpha
+	// NOTE: this might not too much data for just to update alpha
 	if (mirrorInterleavedColorPointer.length <= screen) {
 
 	    float[][] cfData = new float[screen + 1][];
@@ -2252,8 +2395,9 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	boolean useAlpha = false;
 	Object[] retVal;
 
+        // Check for by-copy case
 	if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
-	    float[] vdata;
+            float[] vdata;
 
 	    synchronized (this) {
 		cdirty = dirtyFlag;
@@ -2294,8 +2438,9 @@ abstract class GeometryArrayRetained extends GeometryRetained{
                     (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
                     texCoordSetMapOffset, 
 		    cv.numActiveTexUnit, cv.texUnitStateMap, 
-		    vdata, null,
-		    pass, cdirty);
+                    vertexAttrCount, vertexAttrSizes,
+                    vdata, null,
+                    pass, cdirty);
 	}
 
 	//By reference with java array
@@ -2338,8 +2483,9 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 			texCoordSetCount, texCoordSetMap,
 			(texCoordSetMap == null) ? 0 : texCoordSetMap.length,
 			texCoordSetMapOffset, 
-			cv.numActiveTexUnit, cv.texUnitStateMap, 
-			interLeavedVertexData, cdata,
+			cv.numActiveTexUnit, cv.texUnitStateMap,
+                        vertexAttrCount, vertexAttrSizes,
+                        interLeavedVertexData, cdata,
 			pass, cdirty);
 
 	    } // end of interleaved case
@@ -2354,7 +2500,9 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		    (((vertexFormat & GeometryArray.COLOR) != 0) &&
 		     (vertexType & COLOR_DEFINED) == 0) ||
 		    (((vertexFormat & GeometryArray.NORMALS) != 0) &&
-		     (vertexType & NORMAL_DEFINED) == 0) || 
+		     (vertexType & NORMAL_DEFINED) == 0) ||
+		    (((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) &&
+		     (vertexType & VATTR_DEFINED) == 0) ||
 		    (((vertexFormat& GeometryArray.TEXTURE_COORDINATE) != 0) &&
 		     (vertexType & TEXCOORD_DEFINED) == 0)) {
 		    return;  
@@ -2419,6 +2567,8 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 			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;
 
@@ -2432,6 +2582,8 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 			      mirrorFloatRefCoords, mirrorDoubleRefCoords,
 			      initialColorIndex, cfdata, cbdata,
 			      initialNormalIndex, mirrorFloatRefNormals,
+			      vertexAttrCount, vertexAttrSizes,
+                              initialVertexAttrIndex, mirrorFloatRefVertexAttrs,
 			      pass,
 			      ((texCoordSetMap == null) ? 0:texCoordSetMap.length),
 			      texCoordSetMap,
@@ -2456,7 +2608,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		    cdirty = dirtyFlag;
 		    if (updateAlpha && !ignoreVertexColors) {
 			// update the alpha values
-			// TODO: to handle alpha case
+			// XXXX: to handle alpha case
 			retVal = updateAlphaInInterLeavedData(cv, screen, alpha);
 			useAlpha = (retVal[0] == Boolean.TRUE);
 			cdata = (float[])retVal[1];
@@ -2466,7 +2618,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 			    cdirty |= COLOR_CHANGED;
 			}
 		    } else {
-			// TODO: to handle alpha case
+			// XXXX: to handle alpha case
 			cdata = null;
 			// if transparency switch between on/off
 			if (lastScreenAlpha != -1) {
@@ -2477,7 +2629,6 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		    dirtyFlag = 0;
 		}
 
-
 		executeInterleavedBuffer(cv.ctx, this, geoType, isNonUniformScale, 
 					 useAlpha,
 					 multiScreen,
@@ -2493,9 +2644,10 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 					 pass, 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) ||
@@ -2503,15 +2655,16 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		    (((vertexFormat & GeometryArray.COLOR) != 0) &&
 		     (vertexType & COLOR_DEFINED) == 0) ||
 		    (((vertexFormat & GeometryArray.NORMALS) != 0) &&
-		     (vertexType & NORMAL_DEFINED) == 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;
@@ -2523,7 +2676,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 				    cdirty |= COLOR_CHANGED;
 				}
 			    } else {
-				// TODO: handle transparency case
+				// XXXX: handle transparency case
 				//cfdata = null;
 				cfdata = mirrorFloatRefColors[0]; 
 				// if transparency switch between on/off
@@ -2547,7 +2700,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 				    cdirty |= COLOR_CHANGED;
 				}
 			    } else {
-				// TODO: handle transparency case
+				// XXXX: handle transparency case
 				//cbdata = null;
 				cbdata = mirrorUnsignedByteRefColors[0]; 
 				// if transparency switch between on/off
@@ -2585,9 +2738,13 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		    if((vertexType & NORMAL_DEFINED) != 0) {
 			vdefined |= NORMAL_FLOAT;
 			normal = floatBufferRefNormals.getBufferAsObject();
-		    }
+                    }
 
-		    if((vertexType & TEXCOORD_DEFINED) != 0)
+                    if ((vertexType & VATTR_DEFINED) != 0) {
+                        vdefined |= VATTR_FLOAT;
+                    }
+
+                    if((vertexType & TEXCOORD_DEFINED) != 0)
 		       vdefined |= TEXCOORD_FLOAT;
 
 		    executeVABuffer(cv.ctx, this, geoType, isNonUniformScale, 
@@ -2603,6 +2760,9 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 				    cfdata, cbdata,
 				    initialNormalIndex,
 				    normal,
+				    vertexAttrCount, vertexAttrSizes,
+				    initialVertexAttrIndex,
+				    nioFloatBufferRefVertexAttrs,
 				    pass,
 				    ((texCoordSetMap == null) ? 0:texCoordSetMap.length),
 				    texCoordSetMap,
@@ -2616,60 +2776,68 @@ abstract class GeometryArrayRetained extends GeometryRetained{
     }
 
     // used for GeometryArrays
-    native void buildGA(long ctx, GeometryArrayRetained geo, int geo_type, 
-			boolean isNonUniformScale, boolean updateAlpha,
-			float alpha,
-			boolean ignoreVertexColors,
-			int startVIndex,
-			int vcount, int vformat, 
-			int texCoordSetCount, int texCoordSetMap[],
-			int texCoordSetMapLen,
-			int[] texCoordSetMapOffset, 
-			double[] xform, double[] nxform,
-			float[] varray);
-
-   // used to Build Dlist GeometryArray by Reference with java arrays
-    native void buildGAForByRef(long ctx,
-			  GeometryArrayRetained geo, int geo_type, 
-			  boolean isNonUniformScale,  boolean updateAlpha,
-			float alpha,
-			  boolean ignoreVertexColors,
-			  int vcount,
-			  int vformat,
-			int vdefined,
-			int coordIndex, float[] vfcoords, double[] vdcoords,
-			  int colorIndex, float[] cfdata, byte[] cbdata,
-			  int normalIndex, float[] ndata,
-			  int texcoordmaplength, 
-			  int[] texcoordoffset, 
-			  int[] texIndex, int texstride, Object[] texCoords,
-			double[] xform, double[] nxform);
-
-
-   // used to Build Dlist GeometryArray by Reference with java arrays
-    native void buildGAForBuffer(long ctx,
-			  GeometryArrayRetained geo, int geo_type, 
-			  boolean isNonUniformScale,  boolean updateAlpha,
-			float alpha,
-			  boolean ignoreVertexColors,
-			  int vcount,
-			      int vformat,
-			      int vdefined,
-			      int coordIndex, Object vcoords,
-			  int colorIndex, Object cdata,
-			  int normalIndex, Object ndata,
-			  int texcoordmaplength, 
-			  int[] texcoordoffset, 
-			  int[] texIndex, int texstride, Object[] texCoords,
-			double[] xform, double[] nxform);
-
-    
+    private native void buildGA(long ctx,
+            GeometryArrayRetained geo, int geo_type,
+            boolean isNonUniformScale, boolean updateAlpha,
+            float alpha,
+            boolean ignoreVertexColors,
+            int startVIndex,
+            int vcount, int vformat,
+            int texCoordSetCount, int texCoordSetMap[],
+            int texCoordSetMapLen, int[] texCoordSetMapOffset,
+            int vertexAttrCount, int[] vertexAttrSizes,    
+            double[] xform, double[] nxform,
+            float[] varray);
+
+    // used to Build Dlist GeometryArray by Reference with java arrays
+    private native void buildGAForByRef(long ctx,
+            GeometryArrayRetained geo, int geo_type,
+            boolean isNonUniformScale,  boolean updateAlpha,
+            float alpha,
+            boolean ignoreVertexColors,
+            int vcount,
+            int vformat,
+            int vdefined,
+            int coordIndex, float[] vfcoords, double[] vdcoords,
+            int colorIndex, float[] cfdata, byte[] cbdata,
+            int normalIndex, float[] ndata,
+            int vertexAttrCount, int[] vertexAttrSizes,
+            int[] vertexAttrIndex, float[][] vertexAttrData,
+            int texcoordmaplength,
+            int[] texcoordoffset,
+            int[] texIndex, int texstride, Object[] texCoords,
+            double[] xform, double[] nxform);
+
+
+    // used to Build Dlist GeometryArray by Reference with NIO buffer
+    // NOTE: NIO buffers are no longer supported in display lists.
+    /*
+    private native void buildGAForBuffer(long ctx,
+            GeometryArrayRetained geo, int geo_type,
+            boolean isNonUniformScale,  boolean updateAlpha,
+            float alpha,
+            boolean ignoreVertexColors,
+            int vcount,
+            int vformat,
+            int vdefined,
+            int coordIndex, Object vcoords,
+            int colorIndex, Object cdata,
+            int normalIndex, Object ndata,
+            int texcoordmaplength,
+            int[] texcoordoffset,
+            int[] texIndex, int texstride, Object[] texCoords,
+            double[] xform, double[] nxform);
+    */
 
 
     void buildGA(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale, 
 		 boolean updateAlpha, float alpha, boolean ignoreVertexColors,
 		 Transform3D xform, Transform3D nxform) {
-	float[] vdata = null;
+
+        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;
@@ -2690,13 +2858,31 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		    texCoordSetCount, texCoordSetMap,
 		    (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
 		    texCoordSetMapOffset, 
-		    (xform == null) ? null : xform.mat,
+                    vertexAttrCount, vertexAttrSizes,
+                    (xform == null) ? null : xform.mat,
 		    (nxform == null) ? null : nxform.mat,
 		    vdata);
 	}
 	else {
-	    // Either non-interleaved, by-ref or nio buffer
+            // 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)
@@ -2709,8 +2895,11 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 			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;
+
 		    buildGAForByRef(cv.ctx, this, geoType, isNonUniformScale,
 			    updateAlpha, alpha,
 			    ignoreVertexColors,
@@ -2721,6 +2910,8 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 			    mirrorFloatRefCoords, mirrorDoubleRefCoords,
 			    initialColorIndex, mirrorFloatRefColors[0], mirrorUnsignedByteRefColors[0],
 			    initialNormalIndex, mirrorFloatRefNormals,
+			    vertexAttrCount, vertexAttrSizes,
+                            initialVertexAttrIndex, mirrorFloatRefVertexAttrs,
 			    ((texCoordSetMap == null) ? 0:texCoordSetMap.length),
 			    texCoordSetMap,
 			    initialTexCoordIndex,texCoordStride,
@@ -2728,50 +2919,60 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 			    (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 {
-		    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();
-		    }
+                // 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 & TEXCOORD_DEFINED) != 0)
-		       vdefined |= TEXCOORD_FLOAT;
-		    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);
-	    }
+                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
+                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);
+	    }
+            */
 
 	}
       
@@ -2786,8 +2987,10 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	}
     }
 
-    void unIndexifyJavaArray(IndexedGeometryArrayRetained src) {
-	int vOffset = 0, srcOffset, tOffset = 0;
+    private void unIndexifyJavaArray(IndexedGeometryArrayRetained src) {
+//        System.err.println("unIndexifyJavaArray");
+
+        int vOffset = 0, srcOffset, tOffset = 0;
         int index, colorStride = 0;
 	float[] vdata = null;
         int i;
@@ -2843,10 +3046,24 @@ abstract class GeometryArrayRetained extends GeometryRetained{
                         }
 
 			 System.arraycopy(vdata,
-			    (((int[])src.indexTexCoord[i])[index])*src.stride + src.textureOffset + interleavedOffset,
+			    (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.out.println("===> copying coords");
 		    System.arraycopy(vdata,
@@ -2859,7 +3076,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	    }
 
 	} else {
-	    if ((vertexFormat & GeometryArray.NORMALS) != 0){
+	    if ((vertexFormat & GeometryArray.NORMALS) != 0) {
 		vOffset = normalOffset;
 		switch ((src.vertexType & NORMAL_DEFINED)) { 
 		case NF: 
@@ -2884,7 +3101,8 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		    break;
 		}
 	    }
-	    if ((vertexFormat & GeometryArray.COLOR) != 0){
+
+	    if ((vertexFormat & GeometryArray.COLOR) != 0) {
 		vOffset = colorOffset;
 		int multiplier = 3;
 		if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
@@ -2968,6 +3186,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		    break;
 		}
 	    }
+
 	    if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
 		vOffset = textureOffset;
 		switch ((src.vertexType & TEXCOORD_DEFINED)) {
@@ -2976,7 +3195,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 			for (i = 0, tOffset = vOffset; 
 				i < texCoordSetCount; i++) {
 			    System.arraycopy(src.refTexCoords[i],
-				((int[])src.indexTexCoord[i])[index]*texCoordStride,
+				src.indexTexCoord[i][index]*texCoordStride,
 				vertexData, tOffset, texCoordStride);
 			    tOffset += texCoordStride;
 			}
@@ -2987,7 +3206,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		    for (index=start; index < end; index++) {
 			for (i = 0, tOffset = vOffset;
 			        i < texCoordSetCount; i++) {
-			     srcOffset = ((int[])src.indexTexCoord[i])[index];
+			     srcOffset = src.indexTexCoord[i][index];
 			     vertexData[tOffset] = 
 			      ((TexCoord2f[])src.refTexCoords[i])[srcOffset].x;
 			     vertexData[tOffset+1] = 
@@ -3001,7 +3220,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		    for (index=start; index < end; index++) {
 			for (i = 0, tOffset = vOffset;
 			        i < texCoordSetCount; i++) {
-			     srcOffset = ((int[])src.indexTexCoord[i])[index];
+			     srcOffset = src.indexTexCoord[i][index];
 			     vertexData[tOffset] = 
 			      ((TexCoord3f[])src.refTexCoords[i])[srcOffset].x;
 			     vertexData[tOffset+1] = 
@@ -3016,8 +3235,26 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		default:
 		    break;
 		}
-	    }	
-	    if ((vertexFormat & GeometryArray.COORDINATES) != 0){
+	    }
+
+            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:
@@ -3062,11 +3299,13 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	    }		
 
 	}
-    }   
+    }
 
 
-    void unIndexifyNIOBuffer(IndexedGeometryArrayRetained src) {
-	int vOffset = 0, srcOffset, tOffset = 0;
+    private void unIndexifyNIOBuffer(IndexedGeometryArrayRetained src) {
+//        System.err.println("unIndexifyNIOBuffer");
+
+        int vOffset = 0, srcOffset, tOffset = 0;
         int index, colorStride = 0;
 	float[] vdata = null;
         int i;
@@ -3101,7 +3340,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		    for (i = 0; i < texCoordSetCount; 
 				i++, tcOffset += texCoordStride) {
 			
-			src.interleavedFloatBufferImpl.position((((int[])src.indexTexCoord[i])[index])*src.stride +
+			src.interleavedFloatBufferImpl.position((src.indexTexCoord[i][index])*src.stride +
 							    src.textureOffset);
 			src.interleavedFloatBufferImpl.get(vertexData, tcOffset, texCoordStride);
 		    }
@@ -3124,7 +3363,8 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		    }
 		}
 	    }
-	    if ((vertexFormat & GeometryArray.COLOR) != 0){
+
+            if ((vertexFormat & GeometryArray.COLOR) != 0){
 		vOffset = colorOffset;
 		int multiplier = 3;
 		if ((src.vertexFormat & GeometryArray.WITH_ALPHA) != 0)
@@ -3165,7 +3405,8 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		    break;
 		}
 	    }
-	    if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+
+            if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
 		vOffset = textureOffset;
 		 FloatBufferWrapper texBuffer;
 		if ((src.vertexType & TEXCOORD_DEFINED) != 0) {
@@ -3173,15 +3414,31 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 			for (i = 0, tOffset = vOffset; 
 				i < texCoordSetCount; i++) {
 			    texBuffer = (FloatBufferWrapper)(((J3DBuffer) (src.refTexCoordsBuffer[i])).getBufferImpl());
-			    texBuffer.position(((int[])src.indexTexCoord[i])[index]*texCoordStride);
+			    texBuffer.position(src.indexTexCoord[i][index]*texCoordStride);
 			    texBuffer.get(vertexData, tOffset, texCoordStride);
 			    tOffset += texCoordStride;
 			}
 			vOffset += stride;
 		    }
 		}
-	    }	
-	    if ((vertexFormat & GeometryArray.COORDINATES) != 0){
+	    }
+
+            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];
+			    FloatBufferWrapper 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:
@@ -3206,7 +3463,8 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	    }		
 
 	}
-    }   
+    }
+
 
     /**
      * Returns the vertex stride in numbers of floats as a function 
@@ -3250,6 +3508,11 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	    stride += texCoordStride * texCoordSetCount;
 	}
 
+	if ((this.vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
+	    stride += vertexAttrStride;
+	}
+
+	//System.err.println("stride() = " + stride);
 	return stride;
     }
 
@@ -3269,6 +3532,61 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	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.
@@ -3344,6 +3662,32 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	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) {
@@ -3360,7 +3704,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 
 		// Send a message to renderBin to rebuild the display list or
 		// process the vertex array accordingly
-		// TODO: Should I send one per universe, isn't display list
+		// 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
@@ -4887,10 +5231,262 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	    geomLock.unLock();
 	    return;
 	}
-	
+	
+	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];
+
+	geomLock.getLock();
+	dirtyFlag |= VATTR_CHANGED;
+
+	this.vertexData[offset] = vertexAttr.x;
+	this.vertexData[offset+1] = vertexAttr.y;
+        
+	if (source == null || !source.isLive()) {
+	    geomLock.unLock();
+	    return;
+	}
+
+	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];
+
+	geomLock.getLock();
+	dirtyFlag |= VATTR_CHANGED;
+
+	this.vertexData[offset] = vertexAttr.x;
+	this.vertexData[offset+1] = vertexAttr.y;
+	this.vertexData[offset+2] = vertexAttr.z;
+        
+	if (source == null || !source.isLive()) {
+	    geomLock.unLock();
+	    return;
+	}
+
+	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];
+
+	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 (source == null || !source.isLive()) {
+	    geomLock.unLock();
+	    return;
+	}
+
+	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;
+
+	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 (source == null || !source.isLive()) {
+	    geomLock.unLock();
+	    return;
+	}
+
+	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;
+
+	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 (source == null || !source.isLive()) {
+	    geomLock.unLock();
+	    return;
+	}
+
+	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;
+
+	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 (source == null || !source.isLive()) {
+	    geomLock.unLock();
+	    return;
+	}
+
+	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;
+
+	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 (source == null || !source.isLive()) {
+	    geomLock.unLock();
+	    return;
+	}
+
 	geomLock.unLock();
 	sendDataChangedMessage(false);
-    }      
+    }
+
 
     /**
      * Gets the coordinate associated with the vertex at
@@ -5504,6 +6100,158 @@ abstract class GeometryArrayRetained extends GeometryRetained{
     }
 
 
+    /**
+     * 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.
      */
@@ -5519,7 +6267,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	inUpdater = false;
 	if ((vertexFormat & GeometryArray.BY_REFERENCE) != 0) {
 	    if((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) {
-		// TODO: handle the nio buffer
+		// XXXX: handle the nio buffer
 		if (!(this instanceof IndexedGeometryArrayRetained) ||
 		    (vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) {
 		    if (((vertexFormat & GeometryArray.INTERLEAVED) != 0)) {
@@ -5540,10 +6288,11 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 			nullGeo = (interLeavedVertexData == null);
 		    }
 		    else {
-			setupMirrorVertexPointer((vertexType & VERTEX_DEFINED));
+			setupMirrorVertexPointer(vertexType & VERTEX_DEFINED);
 			setupMirrorColorPointer((vertexType & COLOR_DEFINED), false);
-			setupMirrorNormalPointer((vertexType & NORMAL_DEFINED));
-			setupMirrorTexCoordPointer((vertexType & TEXCOORD_DEFINED));
+			setupMirrorNormalPointer(vertexType & NORMAL_DEFINED);
+			setupMirrorTexCoordPointer(texCoordType);
+                        setupMirrorVertexAttrPointer(vertexAttrType);
 			nullGeo = ((vertexType & GeometryArrayRetained.VERTEX_DEFINED) == 0);
 		    }
 		}
@@ -7254,7 +8003,12 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	direction.z = end.z - start.z;
 	result = intersectRayOrSegment(coordinates, direction, start, dist, iPnt, true);
 	freeVector3d(direction);
-	return result;
+        if((result == true) && (dist[0] <= 1.0)) {
+            return true;
+        }
+        
+        return false;
+        
     }
     
 
@@ -7435,7 +8189,6 @@ abstract class GeometryArrayRetained extends GeometryRetained{
     // 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.
-				lastSign = 0; 
 			    }
 			}
 		    }
@@ -7467,7 +8220,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 				isIntersect = ((t > -EPS) && (t < 1+EPS));
 				break;
 			    } else {
-				lastSign = 0; //degenerate line=>point
+				//degenerate line=>point
 			    }
 			}
 		    }
@@ -7499,7 +8252,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 				isIntersect = ((t > -EPS) && (t < 1+EPS));
 				break;
 			    } else {
-				lastSign = 0; //degenerate line=>point
+				//degenerate line=>point
 			    }
 			}
 		    }
@@ -7529,7 +8282,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 				isIntersect = ((t > -EPS) && (t < 1+EPS));
 				break;
 			    } else {
-				lastSign = 0; //degenerate line=>point
+				//degenerate line=>point
 			    }
 			}
 		    }
@@ -8036,18 +8789,10 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	if (coords != null) {
 	    switch (coords.getBufferType()) {
 	    case J3DBuffer.TYPE_FLOAT:
-		if ( !((FloatBufferWrapper)coords.getBufferImpl()).isDirect())
-		    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray120"));
-
-		// TODO: may need to check whether it is direct and if so,
-		// whether it is consistent with native byte order
+		assert ((FloatBufferWrapper)coords.getBufferImpl()).isDirect();
 		break;
 	    case J3DBuffer.TYPE_DOUBLE:
-		if ( !((DoubleBufferWrapper)coords.getBufferImpl()).isDirect())
-		    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray120"));
-
-		// TODO: may need to check whether it is direct and if so,
-		// whether it is consistent with native byte order
+		assert ((DoubleBufferWrapper)coords.getBufferImpl()).isDirect();
 		break;
 	    case J3DBuffer.TYPE_NULL:
 		throw new IllegalArgumentException(J3dI18N.getString("GeometryArray115"));
@@ -8066,7 +8811,6 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	    }
 	}
 
-	//throw new RuntimeException("method not implemeted");
 	// lock the geometry and start to do real work
 	geomLock.getLock();
 	dirtyFlag |= COORDINATE_CHANGED;
@@ -8074,7 +8818,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	if(coords == null) {
 	    floatBufferRefCoords = null;
 	    doubleBufferRefCoords = null;
-	    // TODO: if not mix java array with nio buffer
+	    // XXXX: if not mix java array with nio buffer
 	    // vertexType can be used as vertexTypeBuffer 
 	    vertexType &= ~PD;
 	    vertexType &= ~PF;
@@ -8101,7 +8845,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 
 	// need not call setupMirrorVertexPointer() since
 	// we are not going to set mirror in NIO buffer case
-	// TODO: if we need to mix java array with buffer,
+	// XXXX: if we need to mix java array with buffer,
 	//        we may need to consider setupMirrorVertexPointer()
 
 	geomLock.unLock();
@@ -8375,12 +9119,10 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	if (colors != null) {
 	    switch(colors.getBufferType()) {
 	    case J3DBuffer.TYPE_FLOAT:
-		if ( !((FloatBufferWrapper)colors.getBufferImpl()).isDirect())
-		    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray120"));
+		assert ((FloatBufferWrapper)colors.getBufferImpl()).isDirect();
 		break;
 	    case J3DBuffer.TYPE_BYTE:
-		if ( !((ByteBufferWrapper)colors.getBufferImpl()).isDirect())
-		    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray120"));
+		assert ((ByteBufferWrapper)colors.getBufferImpl()).isDirect();
 		break;
 	    case J3DBuffer.TYPE_NULL:
 		throw new IllegalArgumentException(J3dI18N.getString("GeometryArray115"));
@@ -8759,8 +9501,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	    
 	    bufferImpl = (FloatBufferWrapper)normals.getBufferImpl();
 
-	    if ( ! bufferImpl.isDirect())
-		throw new IllegalArgumentException(J3dI18N.getString("GeometryArray120"));
+	    assert bufferImpl.isDirect();
 
 	    if ((vertexFormat & GeometryArray.NORMALS) == 0) {
 		throw new IllegalStateException(J3dI18N.getString("GeometryArray122"));
@@ -8863,15 +9604,17 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 
     void setTexCoordRefFloat(int texCoordSet, float[] texCoords) {
 
-	if (texCoords != null) {
+        if (texCoordType != 0 && texCoordType != TF) {
+            if (texCoords != null) {
+                throw new IllegalArgumentException(
+                        J3dI18N.getString("GeometryArray98"));
+            }
+            return;
+        }
 
-	    if ((vertexType & TEXCOORD_DEFINED) != 0 &&
-		(vertexType & TEXCOORD_DEFINED) != TF) {
-		throw new IllegalArgumentException(
-				J3dI18N.getString("GeometryArray98"));
-	    }
+        if (texCoords != null) {
 
-	    int ts = getTexStride();
+            int ts = getTexStride();
 
 	    if (this instanceof IndexedGeometryArrayRetained) {
 		IndexedGeometryArrayRetained idx = (IndexedGeometryArrayRetained)this;
@@ -8889,10 +9632,8 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	refTexCoords[texCoordSet] = texCoords;
 	if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
 			  ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
-	    if (texCoords == null)
-		vertexType &= ~TF;
-	    else
-		vertexType |= TF;
+	    texCoordType = TF;
+            validateTexCoordPointerType();
 	}
 	else {
 	    setupMirrorTexCoordPointer(texCoordSet, TF);
@@ -8903,8 +9644,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	}
     }
 
-    
-    
+
     float[] getTexCoordRefFloat(int texCoordSet) {
 	return ((float[])refTexCoords[texCoordSet]);
     }
@@ -8921,8 +9661,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	    bufferImpl = (FloatBufferWrapper)texCoords.getBufferImpl();
 	    int bufferSize = bufferImpl.limit();
 
-	    if ( ! bufferImpl.isDirect())
-		throw new IllegalArgumentException(J3dI18N.getString("GeometryArray120"));
+	    assert bufferImpl.isDirect();
 
 	    int ts = getTexStride();
 
@@ -8941,14 +9680,14 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	// refTexCoordsBuffer contains J3DBuffer object for tex coord
 	refTexCoordsBuffer[texCoordSet] = texCoords;
 	if (texCoords == null) {
-	    vertexType &= ~TF;
 	    refTexCoords[texCoordSet] = null;
 	}
 	else {
-	    vertexType |= TF;
 	    // refTexCoords contains NIOBuffer object for tex coord
 	    refTexCoords[texCoordSet] = bufferImpl.getBufferAsObject();
 	}
+        texCoordType = TF;
+        validateTexCoordPointerType();
 	geomLock.unLock();
 	if (!inUpdater && source != null && source.isLive()) {
 	    sendDataChangedMessage(false);
@@ -8961,13 +9700,15 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 
     void setTexCoordRef2f(int texCoordSet, TexCoord2f[] texCoords) {
 
-	if (texCoords != null) {
-	    if ((vertexType & TEXCOORD_DEFINED) != 0 &&
-		(vertexType & TEXCOORD_DEFINED) != T2F) {
-		throw new IllegalArgumentException(
-				J3dI18N.getString("GeometryArray98"));
-	    }
-	    
+        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"));
@@ -8990,13 +9731,11 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	refTexCoords[texCoordSet] = texCoords;	
 	if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
 			  ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
-	    if (texCoords == null) 
-		vertexType &= ~T2F;
-	    else
-		vertexType |= T2F;
+	    texCoordType = T2F;
+            validateTexCoordPointerType();
 	}
 	else {
-	    setupMirrorTexCoordPointer(T2F);
+	    setupMirrorTexCoordPointer(texCoordSet, T2F);
 	}
 	geomLock.unLock();
 	if (!inUpdater && source != null && source.isLive()) {
@@ -9017,14 +9756,16 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 
     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 ((vertexType & TEXCOORD_DEFINED) != 0 &&
-		(vertexType & TEXCOORD_DEFINED) != T3F) {
-		throw new IllegalArgumentException(
-				J3dI18N.getString("GeometryArray98"));
-	    }
-	    
 	    if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) == 0) {
 		throw new IllegalStateException(
 				J3dI18N.getString("GeometryArray95"));
@@ -9048,13 +9789,11 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	refTexCoords[texCoordSet] = texCoords;
 	if (inUpdater || (this instanceof IndexedGeometryArrayRetained &&
 			  ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0))) {
-	    if (texCoords == null)
-		vertexType &= ~T3F;
-	    else
-		vertexType |= T3F;
+	    texCoordType = T3F;
+            validateTexCoordPointerType();
 	}
 	else {
-	    setupMirrorTexCoordPointer(T3F);
+	    setupMirrorTexCoordPointer(texCoordSet, T3F);
 	}
 	geomLock.unLock();
 	if (!inUpdater && source != null && source.isLive()) {
@@ -9073,6 +9812,125 @@ abstract class GeometryArrayRetained extends GeometryRetained{
     }    
 
 
+    /**
+     * 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"));
+	    }
+	}
+
+	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);
+	}
+	geomLock.unLock();
+	if (!inUpdater && source != null && source.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) {
+
+	FloatBufferWrapper bufferImpl = null;
+
+	if (vertexAttrs != null) {
+	    if(vertexAttrs.getBufferType() != J3DBuffer.TYPE_FLOAT)
+		throw new IllegalArgumentException(J3dI18N.getString("GeometryArray116"));
+
+	    bufferImpl = (FloatBufferWrapper)vertexAttrs.getBufferImpl();
+	    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"));
+            }
+        }
+
+        geomLock.getLock();
+        dirtyFlag |= VATTR_CHANGED;
+        vertexAttrsRefBuffer[vertexAttrNum] = vertexAttrs;
+        if (vertexAttrs == null) {
+            floatBufferRefVertexAttrs[vertexAttrNum] = null;
+            nioFloatBufferRefVertexAttrs[vertexAttrNum] = null;
+        }
+        else {
+            floatBufferRefVertexAttrs[vertexAttrNum] = bufferImpl;
+            nioFloatBufferRefVertexAttrs[vertexAttrNum] =
+                bufferImpl.getBufferAsObject();
+        }
+        vertexAttrType = AF;
+        validateVertexAttrPointerType();
+        geomLock.unLock();
+        if (!inUpdater && source != null && source.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) {
 
@@ -9135,10 +9993,9 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		throw new IllegalArgumentException(J3dI18N.getString("GeometryArray116"));
 	    
 	    bufferImpl = (FloatBufferWrapper)vertexData.getBufferImpl();
-	    
-	    if (!bufferImpl.isDirect())
-		throw new IllegalArgumentException(J3dI18N.getString("GeometryArray120"));
-	    
+
+            assert bufferImpl.isDirect();
+
 	    int bufferSize = bufferImpl.limit();
 
 	    if (this instanceof IndexedGeometryArrayRetained) {
@@ -9203,37 +10060,20 @@ abstract class GeometryArrayRetained extends GeometryRetained{
     }
     
     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"));
-	}
-	else if ((initialCoordIndex + validVertexCount) > vertexCount) {
-	    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray104"));
-	}
-	else if ((initialColorIndex + validVertexCount) > vertexCount) {
-	    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray101"));
-	}
-	else if ((initialNormalIndex + validVertexCount) > vertexCount) {
-	    throw new IllegalArgumentException(J3dI18N.getString("GeometryArray102"));
-	}
-	else {
-	    if ((vertexFormat & (GeometryArray.BY_REFERENCE|vertexFormat &GeometryArray.INTERLEAVED)) == GeometryArray.BY_REFERENCE) {
-		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.INTERLEAVED) != 0) {
-	    // use nio buffer for interleaved data
+
+        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"));
@@ -9249,10 +10089,41 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		nullGeo = true;
 	    }
 	} else if ((vertexFormat & GeometryArray.BY_REFERENCE) != 0) {
-	    if ((vertexType & GeometryArrayRetained.VERTEX_DEFINED) == 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) {
+            }
+
+	    if (( vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) {
 		// by reference with nio buffer
 		switch ((vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
 		case PF:
@@ -9324,6 +10195,18 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		    }
 		    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 { 
@@ -9444,6 +10327,18 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 			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;
+		}
 	    }
 	}
 
@@ -9455,7 +10350,6 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	    processCoordsChanged(nullGeo);    
 	    sendDataChangedMessage(true);
 	}
-	
     }
 
 
@@ -9689,6 +10583,49 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	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"));
+                }
+            }
+        }
+        geomLock.getLock();
+        dirtyFlag |= VATTR_CHANGED;
+        this.initialVertexAttrIndex[vertexAttrNum] = initialVertexAttrIndex;
+        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"));
@@ -9900,6 +10837,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	    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;
@@ -9963,7 +10901,8 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	float[] curVertexData;
 	int length, srcOffset;
 	int curOffset = 0;
-	// We only merge if the texCoordSetCount is 1;
+	// 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];
@@ -10002,7 +10941,11 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	     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))
@@ -10011,32 +10954,6 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	return true;
     }
 
-    boolean isTextureGeometryMergeable(GeometryArrayRetained srcGeo) {
-
-	if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
-	    if (texCoordSetCount != srcGeo.texCoordSetCount )
-		return false;
-
-	    // If they are both non-null, then check if they are equivalent
-	    if (texCoordSetMap != null && srcGeo.texCoordSetMap != null) {
-		if (texCoordSetMap.length != srcGeo.texCoordSetMap.length)
-		    return false;
-
-		// Check the texCoordSetMap is same
-		for (int j = 0; j < texCoordSetMap.length; j++) {
-		    if (texCoordSetMap[j] != srcGeo.texCoordSetMap[j])
-			return false;
-		}
-	    }
-	    // Check if they are both null;
-	    // if one is null and other is non-null return false
-	    else if (texCoordSetMap != srcGeo.texCoordSetMap)
-		return false;
-	}
-
-	return true;
-    }
-
     void compile(CompileState compState) {
         super.compile(compState);
 
@@ -10097,7 +11014,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	geomLock.getLock();
 	if (this instanceof IndexedGeometryArrayRetained) {
 	    if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
-		mirrorGeometry = (GeometryRetained)
+		mirrorGeometry =
 		    ((IndexedGeometryArrayRetained)this).cloneNonIndexedGeometry();
 	    }
 	    else {
@@ -10113,7 +11030,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	geomLock.getLock();
 	if (this instanceof IndexedGeometryArrayRetained) {
 	    if (mirrorGeometry != null) {
-		mirrorGeometry = (GeometryRetained)
+		mirrorGeometry =
 		    ((IndexedGeometryArrayRetained)this).cloneNonIndexedGeometry();
 	    }
 	}
@@ -10261,7 +11178,7 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		if ((vertexFormat & GeometryArray.INTERLEAVED) == 0){
 		    switch ((vertexType & GeometryArrayRetained.VERTEX_DEFINED)) {
 		    case PF:
-			count =  floatBufferRefCoords.limit()/3; // TODO: limit or capacity
+			count =  floatBufferRefCoords.limit()/3; // XXXX: limit or capacity?
 			break;
 		    case PD:
 			count = doubleBufferRefCoords.limit()/3;
@@ -10437,8 +11354,12 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	    }
 	}
 	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,
@@ -10633,9 +11554,11 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 		(((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_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;
 	    }
@@ -10649,5 +11572,12 @@ abstract class GeometryArrayRetained extends GeometryRetained{
 	}
     }
 
+    int getTexCoordType() {
+        return texCoordType;
+    }
+
+    int getVertexAttrType() {
+        return vertexAttrType;
+    }
 
 }
diff --git a/src/classes/share/javax/media/j3d/GeometryDecompressorRetained.java b/src/classes/share/javax/media/j3d/GeometryDecompressorRetained.java
index 6511553..e9cbdc8 100644
--- a/src/classes/share/javax/media/j3d/GeometryDecompressorRetained.java
+++ b/src/classes/share/javax/media/j3d/GeometryDecompressorRetained.java
@@ -57,7 +57,7 @@ class GeometryDecompressorRetained extends GeometryDecompressor {
     // normal-per-vertex data collected from the HelloUniverse.cg file
     // (seagull, '57 Chevy, dinosaur).
     //
-    // TODO: get fudge values for other vertex combinations
+    // XXXX: get fudge values for other vertex combinations
     private static final float bytesPerVertexFudge = 5.3f ;
 
     // Used for benchmarking if so configured.
diff --git a/src/classes/share/javax/media/j3d/GeometryRetained.java b/src/classes/share/javax/media/j3d/GeometryRetained.java
index 2b0a39d..3def9c3 100644
--- a/src/classes/share/javax/media/j3d/GeometryRetained.java
+++ b/src/classes/share/javax/media/j3d/GeometryRetained.java
@@ -229,12 +229,11 @@ abstract class GeometryRetained extends NodeComponentRetained {
 	return 0 ;
     }
 
-    abstract boolean intersect(PickShape pickShape, double dist[], Point3d iPnt);
+    abstract boolean intersect(PickShape pickShape, PickInfo.IntersectionInfo iInfo, int flags, Point3d iPnt);   
     abstract boolean intersect(Bounds targetBound);
     abstract boolean intersect(Point3d[] pnts);
     abstract boolean intersect(Transform3D thisToOtherVworld, GeometryRetained geom);
 
-
     boolean intersect(Transform3D thisLocalToVworld, 
 		      Transform3D otherLocalToVworld, GeometryRetained  geom) {
 	Transform3D tg =  VirtualUniverse.mc.getTransform3D(null);
@@ -256,15 +255,69 @@ abstract class GeometryRetained extends NodeComponentRetained {
     }
 
 
+    // Return a flag indicating whether or not this Geometry object can be in
+    // a display list.
+    //
+    // XXXX: Note that for IndexedGeometryArray objects, the original
+    // vertex format is used in making this determination, even when it has
+    // been unindexified. This should be fixed by using the vertex format of
+    // the mirror geometry if there is one.
     boolean canBeInDisplayList(boolean alphaEditable) {
-
-	return (VirtualUniverse.mc.isDisplayList) &&
-	    !(this.isEditable || 
-	      (!(this instanceof GeometryArrayRetained) && alphaEditable)||
-	      (alphaEditable && ((((GeometryArrayRetained)this).vertexFormat&
-				  GeometryArray.COLOR) != 0)) ||
-	      (((((GeometryArrayRetained)this).vertexFormat & 
-		 GeometryArray.BY_REFERENCE) != 0) && !VirtualUniverse.mc.buildDisplayListIfPossible));
+        // Check global flag to see whether we can build display lists
+        if (!VirtualUniverse.mc.isDisplayList) {
+            return false;
+        }
+
+        // Can't build display lists if geometry is editable
+        // XXXX: should look at isFrequent bit and allow DL if
+        // infrequently writable
+        if (this.isEditable) {
+            return false;
+        }
+
+        if (this instanceof GeometryArrayRetained) {
+            int vFormat = ((GeometryArrayRetained)this).vertexFormat;
+
+            // If geometry has vertex attributes, check whether
+            // vertex attributes are allowed in display lists
+            if (((vFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) &&
+                    !VirtualUniverse.mc.vertexAttrsInDisplayList) {
+                return false;
+            }
+
+            // Can't build display lists if alpha is editable and
+            // geometry array has colors
+            if (alphaEditable && ((vFormat & GeometryArray.COLOR) != 0)) {
+                return false;
+            }
+
+            // Only build DL for by-ref geometry when system property is set.
+            // Exclude NIO buffers and use-coord-index-only
+            if ((vFormat &  GeometryArray.BY_REFERENCE) != 0) {
+                if (!VirtualUniverse.mc.buildDisplayListIfPossible) {
+                    return false;
+                }
+
+                // XXXX: we could change this to allow display lists for
+                // non-interleaved NIO buffers, but we would first need to
+                // update the now-obsolete buildGAForBuffer method to handle
+                // vertex attrs
+                if ((vFormat & GeometryArray.USE_NIO_BUFFER) != 0) {
+                    return false;
+                }
+
+                if ((vFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) {
+                    return false;
+                }
+            }
+
+            return true;
+        } else {
+            // Can't build display lists for other kind of geometry
+            // NOTE: This method is not called for any type of Geometry
+            // other than GeometryArray, so we shouldn't even get here.
+            return false;
+        }
     }
 
     void computeCentroid() {
diff --git a/src/classes/share/javax/media/j3d/GeometryStripArray.java b/src/classes/share/javax/media/j3d/GeometryStripArray.java
index 39c4dde..88d927f 100644
--- a/src/classes/share/javax/media/j3d/GeometryStripArray.java
+++ b/src/classes/share/javax/media/j3d/GeometryStripArray.java
@@ -31,18 +31,15 @@ public abstract class GeometryStripArray extends GeometryArray {
      * Constructs an empty GeometryStripArray object with the specified
      * number of vertices, vertex format, and
      * array of per-strip vertex counts.
-     * @param vertexCount the number of vertex elements in this array
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2 or TEXTURE_COORDINATE_3, to signal the
-     * inclusion of per-vertex texture coordinates 2D or 3D.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
      * @param stripVertexCounts array that specifies
      * the count of the number of vertices for each separate strip.
      * The length of this array is the number of separate strips.
@@ -50,7 +47,10 @@ public abstract class GeometryStripArray extends GeometryArray {
      * of valid vertices that are rendered (validVertexCount).
      *
      * @exception IllegalArgumentException if
-     * <code>validVertexCount > vertexCount</code>
+     * <code>validVertexCount &gt; vertexCount</code>
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int)}
+     * for more exceptions that can be thrown
      */
     public GeometryStripArray(int vertexCount,
 			      int vertexFormat,
@@ -66,50 +66,21 @@ public abstract class GeometryStripArray extends GeometryArray {
      * sets, texture coordinate mapping array, and
      * array of per-strip vertex counts.
      *
-     * @param vertexCount the number of vertex elements in this array<p>
-     *
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2 or TEXTURE_COORDINATE_3, to signal the
-     * inclusion of per-vertex texture coordinates 2D or 3D.<p>
-     *
-     * @param texCoordSetCount the number of texture coordinate sets
-     * in this GeometryArray object.  If <code>vertexFormat</code>
-     * does not include one of <code>TEXTURE_COORDINATE_2</code> or
-     * <code>TEXTURE_COORDINATE_3</code>, the
-     * <code>texCoordSetCount</code> parameter is not used.<p>
-     *
-     * @param texCoordSetMap an array that maps texture coordinate
-     * sets to texture units.  The array is indexed by texture unit
-     * number for each texture unit in the associated Appearance
-     * object.  The values in the array specify the texture coordinate
-     * set within this GeometryArray object that maps to the
-     * corresponding texture
-     * unit.  All elements within the array must be less than
-     * <code>texCoordSetCount</code>.  A negative value specifies that
-     * no texture coordinate set maps to the texture unit
-     * corresponding to the index.  If there are more texture units in
-     * any associated Appearance object than elements in the mapping
-     * array, the extra elements are assumed to be -1.  The same
-     * texture coordinate set may be used for more than one texture
-     * unit.  Each texture unit in every associated Appearance must
-     * have a valid source of texture coordinates: either a
-     * non-negative texture coordinate set must be specified in the
-     * mapping array or texture coordinate generation must be enabled.
-     * Texture coordinate generation will take precedence for those
-     * texture units for which a texture coordinate set is specified
-     * and texture coordinate generation is enabled.  If
-     * <code>vertexFormat</code> does not include one of
-     * <code>TEXTURE_COORDINATE_2</code> or
-     * <code>TEXTURE_COORDINATE_3</code>, the
-     * <code>texCoordSetMap</code> array is not used.<p>
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
      *
      * @param stripVertexCounts array that specifies
      * the count of the number of vertices for each separate strip.
@@ -118,7 +89,10 @@ public abstract class GeometryStripArray extends GeometryArray {
      * of valid vertices that are rendered (validVertexCount).
      *
      * @exception IllegalArgumentException if
-     * <code>validVertexCount > vertexCount</code>
+     * <code>validVertexCount &gt; vertexCount</code>
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for more exceptions that can be thrown
      *
      * @since Java 3D 1.2
      */
@@ -133,6 +107,62 @@ public abstract class GeometryStripArray extends GeometryArray {
 	((GeometryStripArrayRetained)this.retained).setStripVertexCounts(stripVertexCounts);
     }
 
+    /**
+     * Constructs an empty GeometryStripArray object with the
+     * specified number of vertices, vertex format, number of texture
+     * coordinate sets, texture coordinate mapping array, vertex
+     * attribute count, vertex attribute sizes array, and array of
+     * per-strip vertex counts.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrSizes
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param stripVertexCounts array that specifies
+     * the count of the number of vertices for each separate strip.
+     * The length of this array is the number of separate strips.
+     * The sum of the elements in this array defines the total number
+     * of valid vertices that are rendered (validVertexCount).
+     *
+     * @exception IllegalArgumentException if
+     * <code>validVertexCount &gt; vertexCount</code>
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for more exceptions that can be thrown
+     *
+     * @since Java 3D 1.4
+     */
+    public GeometryStripArray(int vertexCount,
+			      int vertexFormat,
+			      int texCoordSetCount,
+			      int[] texCoordSetMap,
+			      int vertexAttrCount,
+			      int[] vertexAttrSizes,
+			      int[] stripVertexCounts) {
+
+	super(vertexCount, vertexFormat,
+	      texCoordSetCount, texCoordSetMap,
+	      vertexAttrCount, vertexAttrSizes);
+
+	((GeometryStripArrayRetained)this.retained).setStripVertexCounts(stripVertexCounts);
+    }
+
     /**
      * Get number of strips in the GeometryStripArray.
      * @return numStrips number of strips
diff --git a/src/classes/share/javax/media/j3d/GeometryStripArrayRetained.java b/src/classes/share/javax/media/j3d/GeometryStripArrayRetained.java
index 1340be7..e6f83c0 100644
--- a/src/classes/share/javax/media/j3d/GeometryStripArrayRetained.java
+++ b/src/classes/share/javax/media/j3d/GeometryStripArrayRetained.java
@@ -46,9 +46,9 @@ abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
     /**
      * Set stripVertexCount data into local array
      */
-    void setStripVertexCounts(int stripVertexCounts[]){
-       boolean nullGeo = false;
-       
+    void setStripVertexCounts(int stripVertexCounts[]) {
+	boolean nullGeo = false;
+
 	int i, num = stripVertexCounts.length, total = 0;
 	for (i=0; i < num; i++) {
 	    total += stripVertexCounts[i];
@@ -72,28 +72,37 @@ abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
 	if ((initialVertexIndex + total) > vertexCount) {
 	    throw new IllegalArgumentException(J3dI18N.getString("GeometryStripArray3"));
 	}
-	else if ((initialCoordIndex + total) > vertexCount) {
+	if ((initialCoordIndex + total) > vertexCount) {
 	    throw new IllegalArgumentException(J3dI18N.getString("GeometryStripArray7"));
 	}
-	else if ((initialColorIndex + total) > vertexCount) {
+	if ((initialColorIndex + total) > vertexCount) {
 	    throw new IllegalArgumentException(J3dI18N.getString("GeometryStripArray4"));
 	}
-	else if ((initialNormalIndex + total) > vertexCount) {
+	if ((initialNormalIndex + total) > vertexCount) {
 	    throw new IllegalArgumentException(J3dI18N.getString("GeometryStripArray5"));
 	}
-	else {
-            if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
-		if ((vertexFormat & (GeometryArray.BY_REFERENCE|vertexFormat &GeometryArray.INTERLEAVED)) == GeometryArray.BY_REFERENCE) {
-		    for (i = 0; i < texCoordSetCount; i++) {
-			if ((initialTexCoordIndex[i] + total) > vertexCount) {
-			    throw new IllegalArgumentException(J3dI18N.getString(
-									     "GeometryStripArray6"));
-			}
-		    }
-		}
-	    }
-	}
-	geomLock.getLock();
+        if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+            if ((vertexFormat & (GeometryArray.BY_REFERENCE|vertexFormat &GeometryArray.INTERLEAVED)) == GeometryArray.BY_REFERENCE) {
+                for (i = 0; i < texCoordSetCount; i++) {
+                    if ((initialTexCoordIndex[i] + total) > vertexCount) {
+                        throw new IllegalArgumentException(
+                                J3dI18N.getString("GeometryStripArray6"));
+                    }
+                }
+            }
+        }
+        if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
+            if ((vertexFormat & (GeometryArray.BY_REFERENCE|vertexFormat &GeometryArray.INTERLEAVED)) == GeometryArray.BY_REFERENCE) {
+                for (i = 0; i < vertexAttrCount; i++) {
+                    if ((initialVertexAttrIndex[i] + total) > vertexCount) {
+                        throw new IllegalArgumentException(
+                                J3dI18N.getString("GeometryStripArray8"));
+                    }
+                }
+            }
+        }
+
+        geomLock.getLock();
 	dirtyFlag |= STRIPCOUNT_CHANGED;
 	validVertexCount = total;
 	this.stripVertexCounts = new int[num];
@@ -139,7 +148,8 @@ abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
 	    unIndexifyNIOBuffer(src);
 	}
     }
-    void unIndexifyJavaArray(IndexedGeometryStripArrayRetained src) {
+
+    private void unIndexifyJavaArray(IndexedGeometryStripArrayRetained src) {
         int vOffset = 0, srcOffset, tOffset = 0;
         int base = src.initialIndexIndex;
 	int i,j, k, index, colorStride = 0;
@@ -169,7 +179,8 @@ abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
 			  	src.indexNormal[index]*src.stride + src.normalOffset,
 				vertexData, vOffset + normalOffset, 3);
 		    }
-		    if (colorStride == 4) {
+
+                    if (colorStride == 4) {
 			/*
 			System.out.println("vdata.length = "+vdata.length);
 			System.out.println("vertexData.length = "+vertexData.length);
@@ -188,11 +199,12 @@ abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
 				vertexData, vOffset + colorOffset, colorStride);
 			vertexData[vOffset + colorOffset + 3] = 1.0f;
 		    }
-		    if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+
+                    if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
 			for (k = 0; k < texCoordSetCount; k++) {
                              System.arraycopy(vdata,
-                            	(((int[])src.indexTexCoord[k])[index])
-					*src.stride + src.textureOffset +
+                            	(src.indexTexCoord[k][index])
+					* src.stride + src.textureOffset +
 					src.texCoordSetMapOffset[k],
                             	vertexData,
                             	vOffset + textureOffset + 
@@ -201,7 +213,17 @@ abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
                         }
 		    }
 
-		    if ((vertexFormat & GeometryArray.COORDINATES) != 0){
+                    if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
+			for (k = 0; k < vertexAttrCount; k++) {
+                             System.arraycopy(vdata,
+                            	src.indexVertexAttr[k][index] * src.stride + src.vertexAttrOffsets[k],
+                            	vertexData,
+                            	vOffset + vertexAttrOffsets[k],
+                            	vertexAttrSizes[k]);
+                        }
+		    }
+
+		    if ((vertexFormat & GeometryArray.COORDINATES) != 0) {
 			System.arraycopy(vdata,
 					 src.indexCoord[index]*src.stride + src.coordinateOffset,
 					 vertexData, vOffset + coordinateOffset, 3);
@@ -245,7 +267,8 @@ abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
 		    break;
 		}
 	    }
-	    if ((vertexFormat & GeometryArray.COLOR) != 0){
+
+            if ((vertexFormat & GeometryArray.COLOR) != 0){
 		base = src.initialIndexIndex;
 		vOffset = colorOffset;
 		int multiplier = 3;
@@ -351,7 +374,8 @@ abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
 		    break;
 		}
 	    }
-	    if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+
+            if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
 		base = src.initialIndexIndex;
 		vOffset = textureOffset;
 		switch ((src.vertexType & TEXCOORD_DEFINED)) {
@@ -363,7 +387,7 @@ abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
 			    for (k = 0, tOffset = vOffset; 
 					k < texCoordSetCount; k++) {
                                  System.arraycopy(src.refTexCoords[k],
-                                     ((int[])src.indexTexCoord[k])[index]
+                                     src.indexTexCoord[k][index]
 					*texCoordStride,
                                 	vertexData, tOffset, texCoordStride);
                             	 tOffset += texCoordStride;
@@ -380,7 +404,7 @@ abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
 			    for (k = 0, tOffset = vOffset;
 				    k < texCoordSetCount; k++) {
                              	 srcOffset = 
-				    ((int[])src.indexTexCoord[k])[index];
+				    src.indexTexCoord[k][index];
                                  vertexData[tOffset] = ((TexCoord2f[])
 					src.refTexCoords[k])[srcOffset].x;
                                  vertexData[tOffset+1] = ((TexCoord2f[])
@@ -399,7 +423,7 @@ abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
 			    for (k = 0, tOffset = vOffset;
 				    k < texCoordSetCount; k++) {
                              	 srcOffset = 
-				    ((int[])src.indexTexCoord[k])[index];
+				    src.indexTexCoord[k][index];
                                  vertexData[tOffset] = ((TexCoord3f[])
 					src.refTexCoords[k])[srcOffset].x;
                                  vertexData[tOffset+1] = ((TexCoord3f[])
@@ -417,7 +441,32 @@ abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
 		default:
 		    break;
 		}
-	    }	
+	    }
+
+            if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
+		base = src.initialIndexIndex;
+		vOffset = 0;
+		switch (src.vertexType & VATTR_DEFINED) {
+		case AF:
+		    for (i=0; i < src.stripIndexCounts.length; i++) {
+			for (j=0; j < src.stripIndexCounts[i]; j++) {
+			    index = j+base;
+
+                            for (k = 0; k < vertexAttrCount; k++) {
+                                System.arraycopy(src.floatRefVertexAttrs[k],
+                                        src.indexVertexAttr[k][index]*vertexAttrSizes[k],
+                                        vertexData,
+                                        vOffset + vertexAttrOffsets[k],
+                                        vertexAttrSizes[k]);
+                            }
+                            vOffset += stride;
+			}
+			base += src.stripIndexCounts[i];
+		    }
+		    break;
+		}
+	    }
+
 	    if ((vertexFormat & GeometryArray.COORDINATES) != 0){
 		vOffset = coordinateOffset;
 		base = src.initialIndexIndex;
@@ -477,9 +526,9 @@ abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
 	    }		
 	}
     }
-    
-    void unIndexifyNIOBuffer(IndexedGeometryStripArrayRetained src) {	
-	int vOffset = 0, srcOffset, tOffset = 0;
+
+    private void unIndexifyNIOBuffer(IndexedGeometryStripArrayRetained src) {
+        int vOffset = 0, srcOffset, tOffset = 0;
         int base = src.initialIndexIndex;
 	int i,j, k, index, colorStride = 0;
 	
@@ -506,9 +555,10 @@ abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
 			src.interleavedFloatBufferImpl.get(vertexData, vOffset + colorOffset, colorStride);			
 			vertexData[vOffset + colorOffset + 3] = 1.0f;
 		    }
-		    if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+
+                    if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
 			for (k = 0; k < texCoordSetCount; k++) {
-			    src.interleavedFloatBufferImpl.position((((int[])src.indexTexCoord[k])[index])
+			    src.interleavedFloatBufferImpl.position((src.indexTexCoord[k][index])
 					*src.stride + src.textureOffset +
 					src.texCoordSetMapOffset[k]);
 			    
@@ -527,7 +577,7 @@ abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
 	    }
 	}
 	else {
-	    if ((vertexFormat & GeometryArray.NORMALS) != 0){
+	    if ((vertexFormat & GeometryArray.NORMALS) != 0) {
 		base = src.initialIndexIndex;
 		vOffset = normalOffset;
 		if((src.vertexType & NORMAL_DEFINED) != 0) {
@@ -542,7 +592,8 @@ abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
 		    }
 		}
 	    }
-	    if ((vertexFormat & GeometryArray.COLOR) != 0){
+
+            if ((vertexFormat & GeometryArray.COLOR) != 0) {
 		base = src.initialIndexIndex;
 		vOffset = colorOffset;
 		int multiplier = 3;
@@ -593,7 +644,8 @@ abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
 		    break;
 		}
 	    }
-	    if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+
+            if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
 		base = src.initialIndexIndex;
 		vOffset = textureOffset;
 		FloatBufferWrapper texBuffer;
@@ -605,7 +657,7 @@ abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
 			    for (k = 0, tOffset = vOffset; 
 					k < texCoordSetCount; k++) {
 				texBuffer = (FloatBufferWrapper)(((J3DBuffer) (src.refTexCoordsBuffer[k])).getBufferImpl());
-				texBuffer.position(((int[])src.indexTexCoord[k])[index]*texCoordStride);
+				texBuffer.position(src.indexTexCoord[k][index]*texCoordStride);
 				texBuffer.get(vertexData, tOffset, texCoordStride);
 				tOffset += texCoordStride;
                             }
@@ -614,8 +666,30 @@ abstract class GeometryStripArrayRetained extends GeometryArrayRetained {
 			base += src.stripIndexCounts[i];
 		    }
 		}
-	    }	
-	    if ((vertexFormat & GeometryArray.COORDINATES) != 0){
+	    }
+
+            if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
+		base = src.initialIndexIndex;
+		vOffset = 0;
+		if((src.vertexType & VATTR_DEFINED) == AF) {
+		    for (i=0; i < src.stripIndexCounts.length; i++) {
+			for (j=0; j < src.stripIndexCounts[i]; j++) {
+			    index = j+base;
+
+			    for (k = 0; k < vertexAttrCount; k++) {
+                                int vaOffset = vOffset + vertexAttrOffsets[k];
+                                FloatBufferWrapper vaBuffer = src.floatBufferRefVertexAttrs[k];
+				vaBuffer.position(src.indexVertexAttr[k][index]*vertexAttrSizes[k]);
+				vaBuffer.get(vertexData, vaOffset, vertexAttrSizes[k]);
+                            }
+                            vOffset += stride;
+			}
+			base += src.stripIndexCounts[i];
+		    }
+		}
+	    }
+
+	    if ((vertexFormat & GeometryArray.COORDINATES) != 0) {
 		vOffset = coordinateOffset;
 		base = src.initialIndexIndex;
 		switch ((src.vertexType & VERTEX_DEFINED)) { 
diff --git a/src/classes/share/javax/media/j3d/GeometryStructure.java b/src/classes/share/javax/media/j3d/GeometryStructure.java
index 7bb9552..b8d7e0d 100644
--- a/src/classes/share/javax/media/j3d/GeometryStructure.java
+++ b/src/classes/share/javax/media/j3d/GeometryStructure.java
@@ -168,7 +168,6 @@ class GeometryStructure extends J3dStructure {
 		case J3dMessage.MORPH_CHANGED: {
 		    int comp = ((Integer)m.args[1]).intValue();
 		    if (comp == MorphRetained.GEOMETRY_CHANGED) {
-			// TODO: Optimize this case.
 			processBoundsChanged((Object []) m.args[3], false);
 		    }
 		    else if (comp == MorphRetained.APPEARANCE_CHANGED) {
diff --git a/src/classes/share/javax/media/j3d/GraphStructureChangeListener.java b/src/classes/share/javax/media/j3d/GraphStructureChangeListener.java
new file mode 100755
index 0000000..c0a50a0
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GraphStructureChangeListener.java
@@ -0,0 +1,54 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Listener interface for monitoring structural changes to live scene
+ * graphs. BranchGroup additions, removals and moves are reported.
+ *
+ * @see VirtualUniverse#addGraphStructureChangeListener
+ *
+ * @since Java 3D 1.4
+ */
+public interface GraphStructureChangeListener {
+    /**
+     * Invoked when a branch group is added.
+     * Called just before the child is added to the parent.
+     * Parent can be either a BranchGroup or a Locale.
+     *
+     * @param parent the parent of the child being added
+     * @param child the child being added
+     */
+    public void branchGroupAdded(Object parent, BranchGroup child);
+    
+    /**
+     * Invoked when a branch group is removed.
+     * Called just after the child has been removed from the parent.
+     * Parent can be either a BranchGroup or a Locale.
+     *
+     * @param parent the parent of the child being added
+     * @param child the child being added
+     */
+    public void branchGroupRemoved(Object parent, BranchGroup child);
+    
+    /**
+     * Invoked when a branch group is moved.
+     * Called after a child has been moved to it's new parent. This call differs
+     * from the other methods in that the child is live when this method is called.
+     *
+     * @param oldParent the original parent of the child being moved
+     * @param newParent the new parent of the child being moved
+     * @param child the child being moved
+     */
+    public void branchGroupMoved(Object oldParent, Object newParent, BranchGroup child);
+}
diff --git a/src/classes/share/javax/media/j3d/GraphicsConfigInfo.java b/src/classes/share/javax/media/j3d/GraphicsConfigInfo.java
new file mode 100644
index 0000000..7d9e04f
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/GraphicsConfigInfo.java
@@ -0,0 +1,35 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+class GraphicsConfigInfo {
+    private int reqStencilSize = 0;
+    private long fbConfig = 0L;
+
+    int getRequestedStencilSize() {
+	return reqStencilSize;
+    }
+
+    void setRequestedStencilSize(int reqSS) {
+	reqStencilSize = reqSS;
+    }
+    
+    long getFBConfig() {
+	return fbConfig;
+    }
+    
+    void setFBConfig(long fbC) {
+	fbConfig = fbC;
+    }
+
+}
diff --git a/src/classes/share/javax/media/j3d/GraphicsConfigTemplate3D.java b/src/classes/share/javax/media/j3d/GraphicsConfigTemplate3D.java
index 8b59219..38e8156 100644
--- a/src/classes/share/javax/media/j3d/GraphicsConfigTemplate3D.java
+++ b/src/classes/share/javax/media/j3d/GraphicsConfigTemplate3D.java
@@ -39,6 +39,7 @@ public class GraphicsConfigTemplate3D extends GraphicsConfigTemplate {
     int redSize;
     int sceneAntialiasing;
     int stereo;
+    int stencilSize;
 
     // Temporary variables use for passing argument to/from Request Renderer
     Object testCfg;
@@ -66,12 +67,14 @@ public class GraphicsConfigTemplate3D extends GraphicsConfigTemplate {
      * redSize : 2<br>
      * greenSize : 2<br>
      * blueSize : 2<br>
+     * stencilSize : 0<br>
      * </ul>
      */
     public GraphicsConfigTemplate3D() {
         doubleBuffer = REQUIRED;
         stereo = UNNECESSARY;
         depthSize = 16;
+        stencilSize = 0;
         redSize = greenSize = blueSize = 2;
         sceneAntialiasing = UNNECESSARY;
     }
@@ -171,6 +174,35 @@ public class GraphicsConfigTemplate3D extends GraphicsConfigTemplate {
         return depthSize;
     }
 
+    /**
+     * Sets the stencil buffer size requirement.
+     * This is the minimum requirement.
+     * If no GraphicsConfiguration is found that meets or
+     * exceeds this minimum requirement, null will be returned in
+     * getBestConfiguration().
+     *
+     * @param value the value to set this field to
+     *
+     * @since Java 3D 1.4
+     */
+    public void setStencilSize(int value) {
+        if (value < 0)
+            return;
+        
+	stencilSize = value;
+    }
+
+    /**
+     * Retrieves the size of the stencil buffer.
+     *
+     * @return the current value of the stencilSize attribute
+     *
+     * @since Java 3D 1.4
+     */
+    public int getStencilSize() {
+        return stencilSize;
+    }
+
     /**
      * Sets the number of red bits required. This is the minimum requirement.
      * If no GraphicsConfiguration is found that meets or
@@ -298,7 +330,8 @@ public class GraphicsConfigTemplate3D extends GraphicsConfigTemplate {
 	if (gc == null) {
 	    return false;
 	}
-	synchronized (globalLock) {
+
+        synchronized (globalLock) {
 	    testCfg = gc;
 	    threadWaiting = true;
 	    if (Thread.currentThread() instanceof BehaviorScheduler) {
diff --git a/src/classes/share/javax/media/j3d/GraphicsContext3D.java b/src/classes/share/javax/media/j3d/GraphicsContext3D.java
index b5c7516..003134f 100644
--- a/src/classes/share/javax/media/j3d/GraphicsContext3D.java
+++ b/src/classes/share/javax/media/j3d/GraphicsContext3D.java
@@ -183,7 +183,7 @@ public class GraphicsContext3D extends Object   {
     boolean lightsChanged = false;
 
     // A boolean that indicates the sounds have changed
-    // TODO: the soundsChanged flag are set like lights methods set 
+    // XXXX: the soundsChanged flag are set like lights methods set 
     //       lightsChanged? but where is this supposed to be check???
     //       lightsChanged tested in 'draw'; but Sound are not processed
     //       in draw.
@@ -285,27 +285,29 @@ public class GraphicsContext3D extends Object   {
 					      // when a new command is to be
 					      // added to the list
 
-    static Integer commands[]   = new Integer[NCOMMANDS];
-    static Integer stereoModes[] = {new Integer(STEREO_LEFT), 
-				   new Integer(STEREO_RIGHT),
-				   new Integer(STEREO_BOTH)};
+    private static Integer[] commands = new Integer[NCOMMANDS];
+    private static Integer[] stereoModes = {
+        new Integer(STEREO_LEFT),
+        new Integer(STEREO_RIGHT),
+        new Integer(STEREO_BOTH)
+    };
 
     // dirty bits
-    static final int BUFFER_MODE	= 0x1;
+    private static final int BUFFER_MODE	= 0x1;
     private int dirtyMask = 0;
 
 
     // multi-texture
-    int numActiveTexUnit = 0;
-    int lastActiveTexUnitIndex = 0;
-    boolean toSimulateMultiTex = true;
+    private int numActiveTexUnit = 0;
+    private int lastActiveTexUnitIndex = 0;
+    private boolean toSimulateMultiTex = false;
 
     // for read raster
-    volatile boolean readRasterReady = false;
+    private volatile boolean readRasterReady = false;
 
     // for runMonitor
-    boolean gcReady = false;
-    int waiting = 0;
+    private boolean gcReady = false;
+    private int waiting = 0;
 
 
     /**
@@ -381,7 +383,12 @@ public class GraphicsContext3D extends Object   {
 		enableLighting = false;
 	    }
 
-	    if (((AppearanceRetained)appearance.retained).texUnitState != null) {
+            if (appearance instanceof ShaderAppearance) {
+                // TODO : handle ShaderProgram and ShaderAttributeSet
+                System.err.println("ShaderProgram not implemented for immediate mode rendering");
+            }
+
+            if (((AppearanceRetained)appearance.retained).texUnitState != null) {
 		TextureUnitStateRetained[] texUnitState = 
 		    ((AppearanceRetained)appearance.retained).texUnitState;
 
@@ -626,9 +633,8 @@ public class GraphicsContext3D extends Object   {
 	if (fog != null) {
 	    ((FogRetained)fog.retained).setInImmCtx(true);
 
-
-	    if (fog.retained instanceof LinearFogRetained)
-		updateFogState((LinearFogRetained)fog.retained);
+            // Issue 144: updateFogState now called unconditionally
+            updateFogState((FogRetained)fog.retained);
 	}
     }
 
@@ -912,8 +918,9 @@ public class GraphicsContext3D extends Object   {
     }
 
 
-    void updateFogState(LinearFogRetained lfog) {
-	lfog.localToVworldScale = modelTransform.getDistanceScale();
+    void updateFogState(FogRetained fogRet) {
+        // Issue 144: update localToVWorldScale for all types of Fog
+        fogRet.setLocalToVworldScale(modelTransform.getDistanceScale());
     }
 
 
@@ -1280,7 +1287,7 @@ public class GraphicsContext3D extends Object   {
         if (view != null) {
             SoundScheduler soundScheduler = getSoundScheduler();
             if (soundScheduler == null) {
-		// TODO: Re-implement
+		// XXXX: Re-implement
                 // start up SoundScheduler since it hasn't already been started
             }
         }
@@ -1291,13 +1298,13 @@ public class GraphicsContext3D extends Object   {
 	   this.modelTransform.transform(cs.direction, cs.xformDirection);
            cs.xformDirection.normalize();
 	   this.modelTransform.transform(cs.position, cs.xformPosition);
-           // TODO (Question) Is drawTranform equivalent to Vworld-to-Local?
+           // XXXX (Question) Is drawTranform equivalent to Vworld-to-Local?
            cs.trans.setWithLock(drawTransform);
 
    	} else if (sound instanceof PointSoundRetained) {
 	   PointSoundRetained ps = (PointSoundRetained) sound;
            this.modelTransform.transform(ps.position, ps.xformPosition);
-           // TODO (Question) Is drawTranform equivalent to Vworld-to-Local?
+           // XXXX (Question) Is drawTranform equivalent to Vworld-to-Local?
            ps.trans.setWithLock(drawTransform);
        }
     }
@@ -1559,7 +1566,7 @@ public class GraphicsContext3D extends Object   {
 	else
 	    back = this.black;
 
-	// TODO: This should ideally be done by the renderer (or by the
+	// XXXX: This should ideally be done by the renderer (or by the
 	// canvas itself) when the canvas is first added to a view.
 	/*
 	if ((canvas3d.screen.renderer != null) &&
@@ -1575,7 +1582,7 @@ public class GraphicsContext3D extends Object   {
 
         try {
 	    if (canvas3d.drawingSurfaceObject.renderLock()) {
-		// TODO : Fix texture
+		// XXXX : Fix texture
 		/*
 		if (canvas3d.useSharedCtx) {
 		    if (canvas3d.screen.renderer.sharedCtx == 0) {
@@ -1594,13 +1601,7 @@ public class GraphicsContext3D extends Object   {
 		
 		if (canvas3d.ctx == 0) {
 		    synchronized (VirtualUniverse.mc.contextCreationLock) {
-			canvas3d.ctx =
-			    canvas3d.createNewContext(canvas3d.screen.display, 
-						   canvas3d.window,
-						   canvas3d.vid,
-						   canvas3d.fbConfig,
-						   0, false,
-						   canvas3d.offScreen);
+			canvas3d.ctx = canvas3d.createNewContext(0, false);
 			if (canvas3d.ctx == 0) {
 			    canvas3d.drawingSurfaceObject.unLock();
 			    return;
@@ -1608,8 +1609,8 @@ public class GraphicsContext3D extends Object   {
 
 			canvas3d.ctxTimeStamp =
 			    VirtualUniverse.mc.getContextTimeStamp();
-			canvas3d.screen.renderer.listOfCtxs.add(
-								new Long(canvas3d.ctx));
+                        canvas3d.screen.renderer.listOfCtxs.add(
+                                new Long(canvas3d.ctx));
 			canvas3d.screen.renderer.listOfCanvases.add(canvas3d);
 
 			canvas3d.beginScene();
@@ -1618,43 +1619,21 @@ public class GraphicsContext3D extends Object   {
 			    canvas3d.graphics2D.init();
 			}
 
-                        // query for the number of texture units
-			// supported
-                        if (canvas3d.multiTexAccelerated) {
-                            canvas3d.numTexUnitSupported =
-                                    canvas3d.getTextureUnitCount(canvas3d.ctx);
-                        }
-			
-			// enable separate specular color
+                        // enable separate specular color
 			canvas3d.enableSeparateSpecularColor();
 		    }
 
                     // create the cache texture state in canvas
                     // for state download checking purpose
-
                     if (canvas3d.texUnitState == null) {
-                        canvas3d.texUnitState =
-                                new TextureUnitStateRetained[
-                                        canvas3d.numTexUnitSupported];
-                        for (int t = 0; t < canvas3d.numTexUnitSupported; t++) {
-                            canvas3d.texUnitState[t] =
-                                        new TextureUnitStateRetained();
-                            canvas3d.texUnitState[t].texture = null;
-                            canvas3d.texUnitState[t].mirror = null;
-                        }
+                        canvas3d.createTexUnitState();
                     }
 
-
-                    // also create the texture unit state map
-                    // which is a mapping from texture unit state to
-                    // the actual underlying texture unit
-
+                    // Create the texture unit state map
                     if (canvas3d.texUnitStateMap == null) {
-                        canvas3d.texUnitStateMap = 
-                                        new int[canvas3d.numTexUnitSupported];
+                        canvas3d.createTexUnitStateMap();
                     }
 
-
 		    canvas3d.drawingSurfaceObject.contextValidated();
 		    canvas3d.screen.renderer.currentCtx = canvas3d.ctx;
 		    initializeState();
@@ -1830,7 +1809,7 @@ public class GraphicsContext3D extends Object   {
 	//	rb.setVworldToVpc(vp.getVworldToVpc());
 	//	rb.setVpcToVworld(vp.getVpcToVworld());
 
-	// TODO: Fix this
+	// XXXX: Fix this
 	rb.vpcToVworld = vpR.getVpcToVworld();
 	rb.vworldToVpc = vpR.getVworldToVpc();
 
@@ -1890,7 +1869,7 @@ public class GraphicsContext3D extends Object   {
 		switch(stereoMode) {
 		case STEREO_RIGHT:
 		    vpcToEc = cvCache.getRightVpcToEc();
-		    // TODO: move this under check for 
+		    // XXXX: move this under check for 
 		    // (dirtyMask & BUFFER_MODE) above after testing
 		    // PureImmediate mode
 		    canvas3d.setProjectionMatrix(canvas3d.ctx,
@@ -1901,7 +1880,7 @@ public class GraphicsContext3D extends Object   {
 		case STEREO_BOTH:
 		default:
 		    vpcToEc = cvCache.getLeftVpcToEc();
-		    // TODO: move this under check for 
+		    // XXXX: move this under check for 
 		    // (dirtyMask & BUFFER_MODE) above after testing
 		    // PureImmediate mode
 		    canvas3d.setProjectionMatrix(canvas3d.ctx,
@@ -2000,7 +1979,7 @@ public class GraphicsContext3D extends Object   {
 	        if ((geometry.retained instanceof IndexedGeometryArrayRetained) &&
 		    ((((GeometryArrayRetained)geometry.retained).vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0)) {
 		    if (geoRetained.dirtyFlag != 0) {
-			geoRetained.mirrorGeometry = (GeometryRetained) 
+			geoRetained.mirrorGeometry =
 			    ((IndexedGeometryArrayRetained)geoRetained).cloneNonIndexedGeometry();
 			// Change the source geometry dirtyFlag
 			// drawGeo.execute() will change the
@@ -2030,7 +2009,7 @@ public class GraphicsContext3D extends Object   {
 		drawGeo = (GeometryRetained)geometry.retained;
 	    }
 
-	    if (!toSimulateMultiTex) {
+            if (!toSimulateMultiTex) {
 		drawGeo.execute(canvas3d, null, isNonUniformScale,
 				false, alpha, 
 				((canvas3d.view.getScreens()).length > 1), 
@@ -2038,30 +2017,19 @@ public class GraphicsContext3D extends Object   {
 				ignoreVertexColors, 
 				-1);
 	    } else {
-		// TODO: need to leverage the code in textureBin
+		// NOTE: we really should leverage the code in textureBin
 		boolean startToSimulate = false;
-		if (numActiveTexUnit < 1) {
-		    // no active texture unit
-		    drawGeo.execute(canvas3d, null, isNonUniformScale,
-				false, alpha, 
-				((canvas3d.view.getScreens()).length > 1), 
-				canvas3d.screen.screen,
-				ignoreVertexColors, 
-				0);
-		} else if (numActiveTexUnit == 1) {
-		    // one active texture unit
-		    drawGeo.execute(canvas3d, null, isNonUniformScale,
-				    false,   alpha, 
-				((canvas3d.view.getScreens()).length > 1), 
-				canvas3d.screen.screen,
-				ignoreVertexColors, 
-				lastActiveTexUnitIndex);
-		} else {
+
 		    // simulate multiple texture units
-		    AppearanceRetained app = 
-				(AppearanceRetained)appearance.retained;
+                    AppearanceRetained app =
+                            (AppearanceRetained)appearance.retained;
+
+                    assert VirtualUniverse.mc.allowSimulatedMultiTexture;
+                    assert numActiveTexUnit > 1;
+                    assert app.texUnitState != null;
+                    assert app.texUnitState.length > 1;
 
-		    // first turn off fog
+                    // first turn off fog
 		    if (fog != null)
 			canvas3d.setFogEnableFlag(canvas3d.ctx, false);
 
@@ -2088,16 +2056,20 @@ public class GraphicsContext3D extends Object   {
 
 		    // adjust the depth test back to what it was
 		    // and adjust the blend func to what it it was
-		    if (startToSimulate) {
-		        app.transparencyAttributes.updateNative(
-				canvas3d.ctx, alpha, geometryType,
-				polygonMode, lineAA, pointAA);
-		    }
+                    if (startToSimulate) {
+                        if (app.transparencyAttributes != null) {
+                            app.transparencyAttributes.updateNative(
+                                    canvas3d.ctx, alpha, geometryType,
+                                    polygonMode, lineAA, pointAA);
+                        } else {
+                            canvas3d.resetTransparency(canvas3d.ctx, geometryType,
+                                    polygonMode, lineAA, pointAA);
+                        }
+                    }
 
 		    if (fog != null) {
 			canvas3d.setFogEnableFlag(canvas3d.ctx, true);
 		    }
-		}
 	    }
 	    if (geoRetained != null)
 	        geoRetained.geomLock.unLock();
@@ -2229,7 +2201,7 @@ public class GraphicsContext3D extends Object   {
            throw new IllegalSharingException(J3dI18N.getString("GraphicsContext3D21"));
         }
 
-	// TODO: implement illegal argument exception
+	// XXXX: implement illegal argument exception
 	/*
 	if (ras.image.byReference &&
 	    !(ras.image.imageReference instanceof BufferedImage)) {
@@ -2419,7 +2391,7 @@ public class GraphicsContext3D extends Object   {
 
 	if (canvas3d.enableMask != enableMask) {
 	    canvas3d.canvasDirty |= Canvas3D.LIGHTENABLES_DIRTY;
-	    // TODO: 32 => renderBin.maxLights
+	    // XXXX: 32 => renderBin.maxLights
 	    canvas3d.setLightEnables(canvas3d.ctx, enableMask, 32);
 	    canvas3d.enableMask = enableMask;
 	}
@@ -2481,16 +2453,16 @@ public class GraphicsContext3D extends Object   {
 
     boolean updateState(RenderBin rb, int geometryType) {
 
-	boolean useAlpha = false;;
-	toSimulateMultiTex = true;
-	numActiveTexUnit = 0;
+	boolean useAlpha = false;
+	toSimulateMultiTex = false;
+        numActiveTexUnit = 0;
 	lastActiveTexUnitIndex = 0;
 
 	// Update Appearance
 	if (appearance != null) {
 	    AppearanceRetained app = (AppearanceRetained) appearance.retained;
 
-	    // If the material is not null then check if the one in the canvas
+            // If the material is not null then check if the one in the canvas
 	    // is equivalent to the one being sent down. If Yes, do nothing
 	    // Otherwise, cache the sent down material and mark the canvas
 	    // dirty flag so that the compiled/compiled-retained rendering
@@ -2514,75 +2486,95 @@ public class GraphicsContext3D extends Object   {
 		}
 	    }
 
+            // Set flag indicating whether we are using shaders
+            boolean useShaders = false;
+            if (app instanceof ShaderAppearanceRetained) {
+                if (((ShaderAppearanceRetained)app).shaderProgram != null) {
+                    useShaders = true;
+                }
+            }
+
+            // Set the number of available texture units; this depends on
+            // whether or not shaders are being used.
+            int availableTextureUnits =
+                    useShaders ? canvas3d.maxTextureImageUnits : canvas3d.maxTextureUnits;
+
             int prevNumActiveTexUnit = canvas3d.getNumActiveTexUnit();
 
+            // Get the number of active texture units.
+            // Note that this total number now includes disabled units.
 	    if (app.texUnitState != null) {
-		boolean d3dBlendMode = false;
-
 		TextureUnitStateRetained tus;
 
 		for (int i = 0; i < app.texUnitState.length; i++) {
 		    tus = app.texUnitState[i];
 		    if (tus != null && tus.isTextureEnabled()) {
-			numActiveTexUnit++;
 			lastActiveTexUnitIndex = i;
-			useAlpha = useAlpha ||
-				 (tus.texAttrs.textureMode ==
-				  TextureAttributes.BLEND);
-			if (tus.needBlend2Pass(canvas3d)) {
-		            // use multi-pass if one of the stage use blend mode
-			    d3dBlendMode = true;
-			}
+                        numActiveTexUnit = i + 1;
+                        if (tus.texAttrs != null) {
+                            useAlpha = useAlpha ||
+                                    (tus.texAttrs.textureMode ==
+                                    TextureAttributes.BLEND);
+                        }
 		    }
 		}
 
-		if (canvas3d.numTexUnitSupported >= numActiveTexUnit &&
-			canvas3d.multiTexAccelerated && !d3dBlendMode) {
-
-		    int j = 0;
+               if (numActiveTexUnit <= availableTextureUnits) {
+                    // Normal, single-pass rendering case
 
                     // update all active texture unit states
-
 		    for (int i = 0; i < app.texUnitState.length; i++) {
-			if ((app.texUnitState[i] != null) &&
+                        if (i >= availableTextureUnits) {
+                            // This can happen if there are disabled units at
+                            // the end of the array
+                            break;
+                        }
+
+                        if ((app.texUnitState[i] != null) &&
 				    app.texUnitState[i].isTextureEnabled()) {
-			    app.texUnitState[i].updateNative(j, canvas3d, 
+			    app.texUnitState[i].updateNative(i, canvas3d, 
 								false, false);
-			    canvas3d.setTexUnitStateMap(i, j++);
-			} 
+			} else {
+                            canvas3d.resetTexture(canvas3d.ctx, i);
+                        }
 		    }
 
                     // reset the remaining texture units
-
-                    for (int i = j; i < prevNumActiveTexUnit; i++) {
-			if (canvas3d.texUnitState[i].texture != null) {
-                            canvas3d.resetTexture(canvas3d.ctx, i);
-			    canvas3d.texUnitState[i].texture = null;
-			}
+                    for (int i = app.texUnitState.length; i < prevNumActiveTexUnit; i++) {
+                        canvas3d.resetTexture(canvas3d.ctx, i);
                     }
 
                     // set the number active texture unit in Canvas3D
                     canvas3d.setNumActiveTexUnit(numActiveTexUnit);
 
-                    // set the active texture unit back to 0
-                    canvas3d.activeTextureUnit(canvas3d.ctx, 0);
-
-		    toSimulateMultiTex = false;
-
-		} else {
+		} else if (!useShaders && VirtualUniverse.mc.allowSimulatedMultiTexture) {
+                    // Simulated (multi-pass) multi-texture rendering
+                    
+                    toSimulateMultiTex = true;
 
                     // will fall back to the multi-pass case;
                     // reset all the texture units first
+                    for (int i = 0; i < prevNumActiveTexUnit; i++) {
+                        canvas3d.resetTexture(canvas3d.ctx, i);
+                    }
 
+                    // set the number active texture unit in Canvas3D
+                    canvas3d.setNumActiveTexUnit(1);
+                }
+                else {
+                // Exceeded limit, and not using simulated multi-texture
+
+                    // disable all the texture units
                     for (int i = 0; i < prevNumActiveTexUnit; i++) {
-			if (canvas3d.texUnitState[i].texture != null) {
-                            canvas3d.resetTexture(canvas3d.ctx, i);
-			    canvas3d.texUnitState[i].texture = null;
-			}
+                        canvas3d.resetTexture(canvas3d.ctx, i);
                     }
+                    canvas3d.setNumActiveTexUnit(0);
                 }
+ 
+                // set the active texture unit back to 0
+                canvas3d.activeTextureUnit(canvas3d.ctx, 0);
 	    } else {
-		// if texUnitState is null, let's disable
+                // if texUnitState is null, let's disable
 		// all texture units first
 		if (canvas3d.multiTexAccelerated) {
 		    if (canvas3d.texUnitState != null) {
@@ -2599,9 +2591,9 @@ public class GraphicsContext3D extends Object   {
                     canvas3d.activeTextureUnit(canvas3d.ctx, 0);
 		}
 
-	        if ((canvas3d.texUnitState != null) && 
-		    (canvas3d.texUnitState[0] != null) &&
-		    (canvas3d.texUnitState[0].texture != app.texture)) {
+                if ((canvas3d.texUnitState != null) &&
+                        (canvas3d.texUnitState[0] != null) &&
+                        (canvas3d.texUnitState[0].texture != app.texture)) {
 
 		    // If the image is by reference, check if the image
 		    // should be processed
@@ -2616,7 +2608,6 @@ public class GraphicsContext3D extends Object   {
 			    }
 			}
 		        app.texture.updateNative(canvas3d);
-			canvas3d.setTexUnitStateMap(0, 0);
 		        canvas3d.canvasDirty |= Canvas3D.TEXTUREBIN_DIRTY|Canvas3D.TEXTUREATTRIBUTES_DIRTY;
 			numActiveTexUnit = 1;
 		        lastActiveTexUnitIndex = 0;
@@ -2728,7 +2719,7 @@ public class GraphicsContext3D extends Object   {
 
 	    if (app.renderingAttributes != null) {
 		ignoreVertexColors =app.renderingAttributes.ignoreVertexColors;
-		app.renderingAttributes.updateNative(canvas3d.ctx,
+		app.renderingAttributes.updateNative(canvas3d,
 				canvas3d.depthBufferWriteEnableOverride,
 				canvas3d.depthBufferEnableOverride);
 		canvas3d.canvasDirty |= Canvas3D.ATTRIBUTEBIN_DIRTY|Canvas3D.TEXTUREATTRIBUTES_DIRTY;
diff --git a/src/classes/share/javax/media/j3d/Group.java b/src/classes/share/javax/media/j3d/Group.java
index c7a71f2..b5c763b 100644
--- a/src/classes/share/javax/media/j3d/Group.java
+++ b/src/classes/share/javax/media/j3d/Group.java
@@ -60,6 +60,13 @@ public class Group extends Node {
     ALLOW_COLLISION_BOUNDS_WRITE =
         CapabilityBits.GROUP_ALLOW_COLLISION_BOUNDS_WRITE;
 
+    // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_CHILDREN_READ,
+        ALLOW_COLLISION_BOUNDS_READ
+    };
+    
+    
     /**
      * Creates the retained mode GroupRetained object that this
      * Group component object will point to.
@@ -528,5 +535,7 @@ public class Group extends Node {
      * </ul>
      */
     public Group() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);        
     }
 }
diff --git a/src/classes/share/javax/media/j3d/GroupRetained.java b/src/classes/share/javax/media/j3d/GroupRetained.java
index b300758..da9155a 100644
--- a/src/classes/share/javax/media/j3d/GroupRetained.java
+++ b/src/classes/share/javax/media/j3d/GroupRetained.java
@@ -238,6 +238,7 @@ class GroupRetained extends NodeRetained implements BHLeafInterface {
 	if(oldchildr != null) {
 	    oldchildr.setParent(null);
 	    checkClearLive(oldchildr, messages, 0, index, null);
+            universe.notifyStructureChangeListeners(false, this.source, (BranchGroup)oldchildr.source);
 	}
 	removeChildrenData(index);
 
@@ -249,6 +250,7 @@ class GroupRetained extends NodeRetained implements BHLeafInterface {
 	    return;
 	}
 	
+        universe.notifyStructureChangeListeners(true, this.source, (BranchGroup)child);
 	NodeRetained childr = (NodeRetained) child.retained;
 	childr.setParent(this);
 	children.set(index, childr);
@@ -276,6 +278,7 @@ class GroupRetained extends NodeRetained implements BHLeafInterface {
 	if (this.source.isLive()) {
 	    universe.resetWaitMCFlag();
 	    synchronized (universe.sceneGraphLock) {
+                universe.notifyStructureChangeListeners(true, this.source, (BranchGroup)child);
 	        doInsertChild(child, index);
 		universe.setLiveState.clear();	
 	    }
@@ -324,8 +327,10 @@ class GroupRetained extends NodeRetained implements BHLeafInterface {
 	if (this.source.isLive()) {
 	    universe.resetWaitMCFlag();
 	    synchronized (universe.sceneGraphLock) {
+              NodeRetained childr = (NodeRetained)children.get(index);
 	      doRemoveChild(index, null, 0);
 	      universe.setLiveState.clear();	
+              universe.notifyStructureChangeListeners(false, this.source, (BranchGroup)childr.source);
 	    }
 	    universe.waitForMC();
 	} else {
@@ -454,6 +459,7 @@ class GroupRetained extends NodeRetained implements BHLeafInterface {
 	if (this.source.isLive()) {
 	    universe.resetWaitMCFlag();
 	    synchronized (universe.sceneGraphLock) {
+                universe.notifyStructureChangeListeners(true, this.source, (BranchGroup)child);
 	        doAddChild(child, null, 0);
 		universe.setLiveState.clear();	
 	    }
@@ -492,8 +498,13 @@ class GroupRetained extends NodeRetained implements BHLeafInterface {
 	if (this.source.isLive()) {
 	    universe.resetWaitMCFlag();
 	    synchronized (universe.sceneGraphLock) {
+                GroupRetained oldParent = (GroupRetained)((BranchGroupRetained)bg.retained).parent;
 	        doMoveTo(bg);
 		universe.setLiveState.clear();	
+                if (oldParent==null)
+                    universe.notifyStructureChangeListeners(((BranchGroupRetained)bg.retained).locale, this.source, bg);
+                else
+                    universe.notifyStructureChangeListeners(oldParent.source, this.source, bg);
 	    }
 	    universe.waitForMC();
 	} else {
@@ -632,7 +643,7 @@ class GroupRetained extends NodeRetained implements BHLeafInterface {
 	}
 
 	/*
-	// TODO: lights may remove twice or more during clearLive(),
+	// XXXX: lights may remove twice or more during clearLive(),
 	// one from itself and one call from every LightRetained
 	// reference this.  So there is case that this procedure get
 	// called when light already removed.
@@ -1828,7 +1839,7 @@ class GroupRetained extends NodeRetained implements BHLeafInterface {
 	    }
 	    s.transformTargets = newTargets;
 	    
-	    // TODO - optimization for targetThreads computation, require
+	    // XXXX: optimization for targetThreads computation, require
 	    // cleanup in GroupRetained.doSetLive()
 	    //s.transformTargetThreads = 0;
 	}
@@ -2441,14 +2452,23 @@ class GroupRetained extends NodeRetained implements BHLeafInterface {
 	return super.getEffectiveBounds();
     }
     
-    // returns true if children cannot be read/written
+    // returns true if children cannot be read/written and none of the
+    // children can read their parent (i.e., "this") group node
     boolean isStaticChildren() {
 	if (source.getCapability(Group.ALLOW_CHILDREN_READ) ||
 	    source.getCapability(Group.ALLOW_CHILDREN_WRITE)) {
 	    return false;
 	}
+
+	for (int i = children.size() - 1; i >= 0; i--) {
+	    SceneGraphObjectRetained nodeR =
+		(SceneGraphObjectRetained) children.get(i);
+	    if (nodeR != null && nodeR.source.getCapability(Node.ALLOW_PARENT_READ)) {
+		return false;
+	    }
+	}
+
 	return true;
-	
     }
 
 
@@ -2644,7 +2664,7 @@ class GroupRetained extends NodeRetained implements BHLeafInterface {
 	    }
 	}
 	// Has its own copy
-	// TODO: Handle the case of
+	// XXXX: Handle the case of
 	// was non-zero, gone to zero?
 	if (savedParentLights != null) {
 	    if (allocatedLights) {
diff --git a/src/classes/share/javax/media/j3d/ImageComponent.java b/src/classes/share/javax/media/j3d/ImageComponent.java
index 37f4530..4f927cf 100644
--- a/src/classes/share/javax/media/j3d/ImageComponent.java
+++ b/src/classes/share/javax/media/j3d/ImageComponent.java
@@ -195,10 +195,19 @@ public abstract class ImageComponent extends NodeComponent {
     public static final int
     ALLOW_IMAGE_WRITE = CapabilityBits.IMAGE_COMPONENT_ALLOW_IMAGE_WRITE;
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_SIZE_READ,
+        ALLOW_SIZE_READ,
+        ALLOW_FORMAT_READ
+    };
+    
     /**
      * Not a public constructor, for internal use
      */
     ImageComponent() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);        
     }
 
     /**
@@ -220,6 +229,9 @@ public abstract class ImageComponent extends NodeComponent {
      * width or height are not positive.
      */  
     public ImageComponent(int format, int width, int height) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((ImageComponentRetained)this.retained).processParams(format, width, height, 1);
     }
 
@@ -249,6 +261,8 @@ public abstract class ImageComponent extends NodeComponent {
 			  int height,
 			  boolean byReference,
 			  boolean yUp) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
 
  	((ImageComponentRetained)this.retained).setYUp(yUp);
  	((ImageComponentRetained)this.retained).setByReference(byReference);
diff --git a/src/classes/share/javax/media/j3d/ImageComponent2DRetained.java b/src/classes/share/javax/media/j3d/ImageComponent2DRetained.java
index 809884d..6037355 100644
--- a/src/classes/share/javax/media/j3d/ImageComponent2DRetained.java
+++ b/src/classes/share/javax/media/j3d/ImageComponent2DRetained.java
@@ -171,7 +171,7 @@ class ImageComponent2DRetained extends ImageComponentRetained {
 	    ((biType == BufferedImage.TYPE_BYTE_GRAY) &&
 	     (format == ImageComponent.FORMAT_CHANNEL8)) ||
 	    (is4ByteRGBAOr3ByteRGB(ri))) {
-	    /* ||TODO: Don't do short for now!
+	    /* ||XXXX: Don't do short for now!
 	       ((biType ==  BufferedImage.TYPE_USHORT_GRAY) &&
 	       (format == ImageComponent.FORMAT_CHANNEL8)
 	    */
@@ -1293,7 +1293,7 @@ class ImageComponent2DRetained extends ImageComponentRetained {
 	
 	if (source.isLive()) {
 
-	    //TODO: check whether this is needed
+	    //XXXX: check whether this is needed
 	    freeSurface();
 
 	    // send a SUBIMAGE_CHANGED message in order to 
@@ -1336,7 +1336,7 @@ class ImageComponent2DRetained extends ImageComponentRetained {
 
         if (source.isLive()) {
 
-            // TODO: check whether this is needed
+            // XXXX: check whether this is needed
             freeSurface();
 
             // send a SUBIMAGE_CHANGED message in order to
diff --git a/src/classes/share/javax/media/j3d/ImageComponent3D.java b/src/classes/share/javax/media/j3d/ImageComponent3D.java
index 1cb4d9b..224d1f5 100644
--- a/src/classes/share/javax/media/j3d/ImageComponent3D.java
+++ b/src/classes/share/javax/media/j3d/ImageComponent3D.java
@@ -622,7 +622,7 @@ public class ImageComponent3D extends ImageComponent {
 						    rt.height,
 						    rt.depth);
 
-	// TODO : replace by this to duplicate other attributes
+	// XXXX : replace by this to duplicate other attributes
 	/*
 	ImageComponent3D img = new ImageComponent3D(rt.format,
 	                                            rt.width,
diff --git a/src/classes/share/javax/media/j3d/IndexedGeometryArray.java b/src/classes/share/javax/media/j3d/IndexedGeometryArray.java
index c1c4254..00a9aa8 100644
--- a/src/classes/share/javax/media/j3d/IndexedGeometryArray.java
+++ b/src/classes/share/javax/media/j3d/IndexedGeometryArray.java
@@ -17,7 +17,8 @@ import javax.vecmath.*;
 /**
  * The IndexedGeometryArray object contains separate integer arrays
  * that index into the arrays of positional coordinates, colors,
- * normals, and texture coordinates.  These index arrays specify how
+ * normals, texture coordinates, and vertex attributes.
+ * These index arrays specify how
  * vertices are connected to form geometry primitives.  This class is
  * extended to create the various indexed primitive types (e.g.,
  * lines, triangle strips, etc.).
@@ -26,7 +27,10 @@ import javax.vecmath.*;
 public abstract class IndexedGeometryArray extends GeometryArray {
 
     // non-public, no parameter constructor
-    IndexedGeometryArray() {}
+    IndexedGeometryArray() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+    }
     
   /**
    * Specifies that this IndexedGeometryArray allows reading the array of
@@ -84,6 +88,33 @@ public abstract class IndexedGeometryArray extends GeometryArray {
   public static final int
     ALLOW_TEXCOORD_INDEX_WRITE = CapabilityBits.INDEXED_GEOMETRY_ARRAY_ALLOW_TEXCOORD_INDEX_WRITE;
 
+    /**
+     * Specifies that this IndexedGeometryArray allows reading the array of
+     * vertex attribute indices.
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int
+        ALLOW_VERTEX_ATTR_INDEX_READ = CapabilityBits.INDEXED_GEOMETRY_ARRAY_ALLOW_VERTEX_ATTR_INDEX_READ;
+
+    /**
+     * Specifies that this IndexedGeometryArray allows writing the array of
+     * vertex attribute indices.
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int
+        ALLOW_VERTEX_ATTR_INDEX_WRITE = CapabilityBits.INDEXED_GEOMETRY_ARRAY_ALLOW_VERTEX_ATTR_INDEX_WRITE;
+
+    // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_COLOR_INDEX_READ,
+        ALLOW_COORDINATE_INDEX_READ,
+        ALLOW_NORMAL_INDEX_READ,
+        ALLOW_TEXCOORD_INDEX_READ,
+        ALLOW_VERTEX_ATTR_INDEX_READ
+    };
+
     /**
      * Constructs an empty IndexedGeometryArray object with the specified
      * number of vertices, vertex format, and number of indices.
@@ -96,28 +127,31 @@ public abstract class IndexedGeometryArray extends GeometryArray {
      * all index array values : 0<br>
      * </ul>
      *
-     * @param vertexCount the number of vertex elements in this
-     * IndexedGeometryArray
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or
-     * TEXTURE_COORDINATE_4, to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
      * @param indexCount the number of indices in this object.  This
      * count is the maximum number of vertices that will be rendered.
+     *
+     * @exception IllegalArgumentException if <code>indexCount &lt; 0</code>
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int)}
+     * for more exceptions that can be thrown
      */
     public IndexedGeometryArray(int vertexCount,
 				int vertexFormat,
 				int indexCount) {
 	super(vertexCount, vertexFormat);
-	((IndexedGeometryArrayRetained)this.retained).createIndexedGeometryArrayData(indexCount);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((IndexedGeometryArrayRetained)this.retained).createIndexedGeometryArrayData(indexCount);
     }
 
     /**
@@ -126,58 +160,30 @@ public abstract class IndexedGeometryArray extends GeometryArray {
      * sets, texture coordinate mapping array, and number of indices.
      * Defaults are used for all other parameters.
      *
-     * @param vertexCount the number of vertex elements in this
-     * IndexedGeometryArray<p>
-     *
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D , 3D or 4D.<p>
-     *
-     * @param texCoordSetCount the number of texture coordinate sets
-     * in this GeometryArray object.  If <code>vertexFormat</code>
-     * does not include one of <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetCount</code> parameter is not used.<p>
-     *
-     * @param texCoordSetMap an array that maps texture coordinate
-     * sets to texture units.  The array is indexed by texture unit
-     * number for each texture unit in the associated Appearance
-     * object.  The values in the array specify the texture coordinate
-     * set within this GeometryArray object that maps to the
-     * corresponding texture
-     * unit.  All elements within the array must be less than
-     * <code>texCoordSetCount</code>.  A negative value specifies that
-     * no texture coordinate set maps to the texture unit
-     * corresponding to the index.  If there are more texture units in
-     * any associated Appearance object than elements in the mapping
-     * array, the extra elements are assumed to be -1.  The same
-     * texture coordinate set may be used for more than one texture
-     * unit.  Each texture unit in every associated Appearance must
-     * have a valid source of texture coordinates: either a
-     * non-negative texture coordinate set must be specified in the
-     * mapping array or texture coordinate generation must be enabled.
-     * Texture coordinate generation will take precedence for those
-     * texture units for which a texture coordinate set is specified
-     * and texture coordinate generation is enabled.  If
-     * <code>vertexFormat</code> does not include one of
-     * <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetMap</code> array is not used.<p>
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
      *
      * @param indexCount the number of indices in this object.  This
      * count is the maximum number of vertices that will be rendered.
      *
+     * @exception IllegalArgumentException if <code>indexCount &lt; 0</code>
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for more exceptions that can be thrown
+     *
      * @since Java 3D 1.2
      */
     public IndexedGeometryArray(int vertexCount,
@@ -185,7 +191,61 @@ public abstract class IndexedGeometryArray extends GeometryArray {
 				int texCoordSetCount,
 				int[] texCoordSetMap,
 				int indexCount) {
-	super(vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap);
+	this(vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap, 0, null, indexCount);
+    }
+
+    /**
+     * Constructs an empty IndexedGeometryArray object with the
+     * specified number of vertices, vertex format, number of texture
+     * coordinate sets, texture coordinate mapping array, vertex
+     * attribute count, vertex attribute sizes array, and number of
+     * indices.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrSizes
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param indexCount the number of indices in this object.  This
+     * count is the maximum number of vertices that will be rendered.
+     *
+     * @exception IllegalArgumentException if <code>indexCount &lt; 0</code>
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for more exceptions that can be thrown
+     *
+     * @since Java 3D 1.4
+     */
+    public IndexedGeometryArray(int vertexCount,
+				int vertexFormat,
+				int texCoordSetCount,
+				int[] texCoordSetMap,
+				int vertexAttrCount,
+				int[] vertexAttrSizes,
+				int indexCount) {
+
+	super(vertexCount, vertexFormat,
+	      texCoordSetCount, texCoordSetMap,
+	      vertexAttrCount, vertexAttrSizes);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
 	((IndexedGeometryArrayRetained)this.retained).createIndexedGeometryArrayData(indexCount);
     }
 
@@ -380,6 +440,20 @@ public abstract class IndexedGeometryArray extends GeometryArray {
 	throw new UnsupportedOperationException();
     }
 
+    /**
+     * This method is not supported for indexed geometry arrays.
+     * Indexed primitives use an array of indices to determine how
+     * to access the vertex array.
+     *
+     * @exception UnsupportedOperationException this method is not supported
+     *
+     * @since Java 3D 1.4
+     */
+    public void setInitialVertexAttrIndex(int vertexAttrNum,
+					  int initialVertexAttrIndex) {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * This method is not supported for indexed geometry arrays.
      * Indexed primitives use an array of indices to determine how
@@ -468,6 +542,9 @@ public abstract class IndexedGeometryArray extends GeometryArray {
      * colorIndex is out of range if it is less than 0 or is
      * greater than or equal to the number of vertices actually
      * defined for the color array.
+     *
+     * @exception NullPointerException if the <code>USE_COORD_INDEX_ONLY</code>
+     * bit is set in <code>vertexFormat</code>.
      */
   public void setColorIndex(int index, int colorIndex) {
     if (isLiveOrCompiled())
@@ -495,6 +572,9 @@ public abstract class IndexedGeometryArray extends GeometryArray {
      * is out of range.  An element is out of range if it is less than 0
      * or is greater than or equal to the number of vertices actually
      * defined for the color array.
+     *
+     * @exception NullPointerException if the <code>USE_COORD_INDEX_ONLY</code>
+     * bit is set in <code>vertexFormat</code>.
      */
   public void setColorIndices(int index, int colorIndices[]) {
     if (isLiveOrCompiled())
@@ -522,6 +602,9 @@ public abstract class IndexedGeometryArray extends GeometryArray {
      * normalIndex is out of range if it is less than 0 or is
      * greater than or equal to the number of vertices actually
      * defined for the normal array.
+     *
+     * @exception NullPointerException if the <code>USE_COORD_INDEX_ONLY</code>
+     * bit is set in <code>vertexFormat</code>.
      */
   public void setNormalIndex(int index, int normalIndex) {
     if (isLiveOrCompiled())
@@ -549,6 +632,9 @@ public abstract class IndexedGeometryArray extends GeometryArray {
      * is out of range.  An element is out of range if it is less than 0
      * or is greater than or equal to the number of vertices actually
      * defined for the normal array.
+     *
+     * @exception NullPointerException if the <code>USE_COORD_INDEX_ONLY</code>
+     * bit is set in <code>vertexFormat</code>.
      */
   public void setNormalIndices(int index, int normalIndices[]) {
     if (isLiveOrCompiled())
@@ -590,13 +676,16 @@ public abstract class IndexedGeometryArray extends GeometryArray {
      * greater than or equal to the number of vertices actually
      * defined for the texture coordinate array.
      *
+     * @exception NullPointerException if the <code>USE_COORD_INDEX_ONLY</code>
+     * bit is set in <code>vertexFormat</code>.
+     *
      * @since Java 3D 1.2
      */
     public void setTextureCoordinateIndex(int texCoordSet,
 					  int index,
 					  int texCoordIndex) {
 	if (isLiveOrCompiled())
-	    if(!this.getCapability(ALLOW_COORDINATE_INDEX_WRITE))
+	    if(!this.getCapability(ALLOW_TEXCOORD_INDEX_WRITE))
 		throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray7"));
   
 	((IndexedGeometryArrayRetained)this.retained).setTextureCoordinateIndex(texCoordSet, index, texCoordIndex);
@@ -634,18 +723,99 @@ public abstract class IndexedGeometryArray extends GeometryArray {
      * or is greater than or equal to the number of vertices actually
      * defined for the texture coordinate array.
      *
+     * @exception NullPointerException if the <code>USE_COORD_INDEX_ONLY</code>
+     * bit is set in <code>vertexFormat</code>.
+     *
      * @since Java 3D 1.2
      */
     public void setTextureCoordinateIndices(int texCoordSet,
 					    int index,
 					    int texCoordIndices[]) {
 	if (isLiveOrCompiled())
-	    if(!this.getCapability(ALLOW_COORDINATE_INDEX_WRITE))
+	    if(!this.getCapability(ALLOW_TEXCOORD_INDEX_WRITE))
 		throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray7"));
   
 	((IndexedGeometryArrayRetained)this.retained).setTextureCoordinateIndices(texCoordSet, index, texCoordIndices);
     }
 
+    /**
+     * Sets the vertex attribute index associated with the vertex at
+     * the specified index for the specified vertex attribute number
+     * for this object.
+     *
+     * @param vertexAttrNum vertex attribute number in this geometry array
+     * @param index the vertex index
+     * @param vertexAttrIndex the new vertex attribute index
+     *
+     * @exception CapabilityNotSetException if appropriate capability is
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range.
+     *
+     * @exception ArrayIndexOutOfBoundsException if index is in the range
+     * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+     * and the specified vertexAttrIndex is out of range.  The
+     * vertexAttrIndex is out of range if it is less than 0 or is
+     * greater than or equal to the number of vertices actually
+     * defined for the vertex attribute array.
+     *
+     * @exception NullPointerException if the <code>USE_COORD_INDEX_ONLY</code>
+     * bit is set in <code>vertexFormat</code>.
+     *
+     * @since Java 3D 1.4
+     */
+    public void setVertexAttrIndex(int vertexAttrNum,
+                                   int index,
+                                   int vertexAttrIndex) {
+	if (isLiveOrCompiled()) {
+	    if(!this.getCapability(ALLOW_VERTEX_ATTR_INDEX_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray28"));
+            }
+        }
+
+        ((IndexedGeometryArrayRetained)this.retained).setVertexAttrIndex(vertexAttrNum, index, vertexAttrIndex);
+    }
+
+    /**
+     * Sets the vertex attribute indices associated with the vertices
+     * starting at the specified index for the specified vertex attribute number
+     * for this object.
+     *
+     * @param vertexAttrNum vertex attribute number in this geometry array
+     * @param index the vertex index
+     * @param vertexAttrIndices an array of vertex attribute indices
+     *
+     * @exception CapabilityNotSetException if appropriate capability is
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range.
+     *
+     * @exception ArrayIndexOutOfBoundsException if any element of the
+     * vertexAttrIndices array whose destination position is in the range
+     * <code>[initialIndexIndex, initialIndexIndex+validIndexCount-1]</code>
+     * is out of range.  An element is out of range if it is less than 0
+     * or is greater than or equal to the number of vertices actually
+     * defined for the vertex attribute array.
+     *
+     * @exception NullPointerException if the <code>USE_COORD_INDEX_ONLY</code>
+     * bit is set in <code>vertexFormat</code>.
+     *
+     * @since Java 3D 1.4
+     */
+    public void setVertexAttrIndices(int vertexAttrNum,
+                                     int index,
+                                     int[] vertexAttrIndices) {
+	if (isLiveOrCompiled()) {
+	    if(!this.getCapability(ALLOW_VERTEX_ATTR_INDEX_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray28"));
+            }
+        }
+  
+	((IndexedGeometryArrayRetained)this.retained).setVertexAttrIndices(vertexAttrNum, index, vertexAttrIndices);
+    }
+
   /**
    * Retrieves the coordinate index associated with the vertex at
    * the specified index for this object.
@@ -685,6 +855,9 @@ public abstract class IndexedGeometryArray extends GeometryArray {
    * @return the color index
      * @exception CapabilityNotSetException if appropriate capability is
      * not set and this object is part of live or compiled scene graph
+     *
+     * @exception NullPointerException if the <code>USE_COORD_INDEX_ONLY</code>
+     * bit is set in <code>vertexFormat</code>.
    */
   public int getColorIndex(int index) {
     if (isLiveOrCompiled())
@@ -703,6 +876,9 @@ public abstract class IndexedGeometryArray extends GeometryArray {
    * @param colorIndices array that will receive the color indices
      * @exception CapabilityNotSetException if appropriate capability is
      * not set and this object is part of live or compiled scene graph
+     *
+     * @exception NullPointerException if the <code>USE_COORD_INDEX_ONLY</code>
+     * bit is set in <code>vertexFormat</code>.
    */
   public void getColorIndices(int index, int colorIndices[]) {
     if (isLiveOrCompiled())
@@ -719,6 +895,9 @@ public abstract class IndexedGeometryArray extends GeometryArray {
    * @return the normal index
      * @exception CapabilityNotSetException if appropriate capability is
      * not set and this object is part of live or compiled scene graph
+     *
+     * @exception NullPointerException if the <code>USE_COORD_INDEX_ONLY</code>
+     * bit is set in <code>vertexFormat</code>.
    */
   public int getNormalIndex(int index) {
     if (isLiveOrCompiled())
@@ -738,6 +917,9 @@ public abstract class IndexedGeometryArray extends GeometryArray {
    * @param normalIndices array that will receive the normal indices
      * @exception CapabilityNotSetException if appropriate capability is
      * not set and this object is part of live or compiled scene graph
+     *
+     * @exception NullPointerException if the <code>USE_COORD_INDEX_ONLY</code>
+     * bit is set in <code>vertexFormat</code>.
    */
   public void getNormalIndices(int index, int normalIndices[]) {
     if (isLiveOrCompiled())
@@ -773,6 +955,9 @@ public abstract class IndexedGeometryArray extends GeometryArray {
      * <code>vertexFormat</code> or if the index or
      * texCoordSet is out of range.
      *
+     * @exception NullPointerException if the <code>USE_COORD_INDEX_ONLY</code>
+     * bit is set in <code>vertexFormat</code>.
+     *
      * @since Java 3D 1.2
      */
     public int getTextureCoordinateIndex(int texCoordSet, int index) {
@@ -812,6 +997,9 @@ public abstract class IndexedGeometryArray extends GeometryArray {
      * <code>vertexFormat</code> or if the index or
      * texCoordSet is out of range.
      *
+     * @exception NullPointerException if the <code>USE_COORD_INDEX_ONLY</code>
+     * bit is set in <code>vertexFormat</code>.
+     *
      * @since Java 3D 1.2
      */
     public void getTextureCoordinateIndices(int texCoordSet,
@@ -824,6 +1012,72 @@ public abstract class IndexedGeometryArray extends GeometryArray {
 	((IndexedGeometryArrayRetained)this.retained).getTextureCoordinateIndices(texCoordSet, index, texCoordIndices);
     }
 
+    /**
+     * Retrieves the vertex attribute index associated with the vertex at
+     * the specified index for the specified vertex attribute number
+     * for this object.
+     *
+     * @param vertexAttrNum vertex attribute number in this geometry array
+     * @param index the vertex index
+     *
+     * @return the vertex attribute index
+     *
+     * @exception CapabilityNotSetException if appropriate capability is
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range.
+     *
+     * @exception NullPointerException if the <code>USE_COORD_INDEX_ONLY</code>
+     * bit is set in <code>vertexFormat</code>.
+     *
+     * @since Java 3D 1.4
+     */
+    public int getVertexAttrIndex(int vertexAttrNum,
+                                  int index) {
+	if (isLiveOrCompiled()) {
+	    if(!this.getCapability(ALLOW_VERTEX_ATTR_INDEX_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray29"));
+            }
+        }
+
+	return ((IndexedGeometryArrayRetained)this.retained).getVertexAttrIndex(vertexAttrNum, index);
+    }
+
+    /**
+     * Retrieves the vertex attribute indices associated with the vertices
+     * starting at the specified index for the specified vertex attribute number
+     * for this object. The vertex attribute indices
+     * are copied into the specified array. The array
+     * must be large enough to hold all of the indices.
+     *
+     * @param vertexAttrNum vertex attribute number in this geometry array
+     * @param index the vertex index
+     * @param vertexAttrIndices array that will receive the vertex attribute indices
+     *
+     * @exception CapabilityNotSetException if appropriate capability is
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @exception ArrayIndexOutOfBoundsException if the index or
+     * vertexAttrNum is out of range.
+     *
+     * @exception NullPointerException if the <code>USE_COORD_INDEX_ONLY</code>
+     * bit is set in <code>vertexFormat</code>.
+     *
+     * @since Java 3D 1.4
+     */
+    public void getVertexAttrIndices(int vertexAttrNum,
+                                     int index,
+                                     int[] vertexAttrIndices) {
+	if (isLiveOrCompiled()) {
+	    if(!this.getCapability(ALLOW_VERTEX_ATTR_INDEX_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("IndexedGeometryArray29"));
+            }
+        }
+  
+	((IndexedGeometryArrayRetained)this.retained).getVertexAttrIndices(vertexAttrNum, index, vertexAttrIndices);
+    }
+
    /**
      * Copies all node information from <code>originalNodeComponent</code> into
      * the current node.  This method is called from the
@@ -854,30 +1108,39 @@ public abstract class IndexedGeometryArray extends GeometryArray {
 	IndexedGeometryArrayRetained rt = 
 	    (IndexedGeometryArrayRetained) retained;
 
-	int vformat = ga.getVertexFormat();
-	int buffer[] = new int[ga.getIndexCount()];
-
-	if ((vformat & GeometryArray.COORDINATES) != 0) {
-	    ga.getCoordinateIndices(0, buffer);
-	    rt.setCoordinateIndices(0, buffer);
-	}
-	
-	if ((vformat & GeometryArray.NORMALS) != 0) {
-	    ga.getNormalIndices(0, buffer);
-	    rt.setNormalIndices(0, buffer);
-	}
-	
-	if ((vformat & GeometryArray.COLOR) != 0) {
-	    ga.getColorIndices(0, buffer);
-	    rt.setColorIndices(0, buffer);
-	}
-	
-	if ((vformat & GeometryArray.TEXTURE_COORDINATE) != 0) {
-	    for (int i = 0; i < ga.texCoordSetCount; i++) {
-	         ga.getTextureCoordinateIndices(i, 0, buffer);
-	         rt.setTextureCoordinateIndices(i, 0, buffer);
-	    }
-	}
+        int vformat = ga.getVertexFormat();
+        int buffer[] = new int[ga.getIndexCount()];
+
+        if ((vformat & COORDINATES) != 0) {
+            ga.getCoordinateIndices(0, buffer);
+            rt.setCoordinateIndices(0, buffer);
+        }
+
+        if ((vformat & USE_COORD_INDEX_ONLY) == 0) {
+            if ((vformat & NORMALS) != 0) {
+                ga.getNormalIndices(0, buffer);
+                rt.setNormalIndices(0, buffer);
+            }
+
+            if ((vformat & COLOR) != 0) {
+                ga.getColorIndices(0, buffer);
+                rt.setColorIndices(0, buffer);
+            }
+
+            if ((vformat & VERTEX_ATTRIBUTES) != 0) {
+                for (int i = 0; i < ga.vertexAttrCount; i++) {
+                    ga.getVertexAttrIndices(i, 0, buffer);
+                    rt.setVertexAttrIndices(i, 0, buffer);
+                }
+            }
+
+            if ((vformat & TEXTURE_COORDINATE) != 0) {
+                for (int i = 0; i < ga.texCoordSetCount; i++) {
+                    ga.getTextureCoordinateIndices(i, 0, buffer);
+                    rt.setTextureCoordinateIndices(i, 0, buffer);
+                }
+            }
+        }
     }
 
 }
diff --git a/src/classes/share/javax/media/j3d/IndexedGeometryArrayRetained.java b/src/classes/share/javax/media/j3d/IndexedGeometryArrayRetained.java
index 090de29..f6d443b 100644
--- a/src/classes/share/javax/media/j3d/IndexedGeometryArrayRetained.java
+++ b/src/classes/share/javax/media/j3d/IndexedGeometryArrayRetained.java
@@ -30,11 +30,14 @@ import com.sun.j3d.internal.DoubleBufferWrapper;
 
 abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 
-    // arrays to save indices for coord, color, normal, texcoord
-    int		indexCoord[], indexColor[], indexNormal[];
-    Object	indexTexCoord[];
+    // arrays to save indices for coord, color, normal, texcoord, vertexAttr
+    int[] indexCoord;
+    int[] indexColor;
+    int[] indexNormal;
+    int[][] indexTexCoord;
+    int[][] indexVertexAttr;
 
-    int		indexCount;
+    int indexCount = 0;
 
     int initialIndexIndex = 0;
     int validIndexCount = 0;
@@ -47,65 +50,77 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
     int maxColorIndex = 0;
     int maxNormalIndex = 0;
     int[] maxTexCoordIndices = null;
-    
-  void createIndexedGeometryArrayData(int indexCount) {
-    this.indexCount    = indexCount;
-    this.validIndexCount    = indexCount;
+    int[] maxVertexAttrIndices = null;
 
+    void createIndexedGeometryArrayData(int indexCount) {
+        this.indexCount = indexCount;
+        this.validIndexCount = indexCount;
 
-    boolean notUCIO = (this.vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0;
-    
-    if((this.vertexFormat & GeometryArray.COORDINATES) != 0)
-    	this.indexCoord    = new int[indexCount];
-    
-    if(((this.vertexFormat & GeometryArray.NORMALS) != 0) && notUCIO)
-    	this.indexNormal    = new int[indexCount];
-    
-    if(((this.vertexFormat & GeometryArray.COLOR) != 0) && notUCIO)
-    	this.indexColor   = new int[indexCount];
-    
-    if((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
-	this.indexTexCoord = new Object[this.texCoordSetCount];
-        if(notUCIO) {
-            for (int i = 0; i < this.texCoordSetCount; i++) {
-                this.indexTexCoord[i] = new int[indexCount];
+        // Only allocate color, normal, texCoord, and vertexAttr
+        // index arrays if USE_COORD_INDEX_ONLY is not set
+        boolean notUCIO = (this.vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0;
+
+        if((this.vertexFormat & GeometryArray.COORDINATES) != 0)
+            this.indexCoord    = new int[indexCount];
+
+        if(((this.vertexFormat & GeometryArray.NORMALS) != 0) && notUCIO)
+            this.indexNormal    = new int[indexCount];
+
+        if(((this.vertexFormat & GeometryArray.COLOR) != 0) && notUCIO)
+            this.indexColor   = new int[indexCount];
+
+        if((this.vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
+            this.indexTexCoord = new int[this.texCoordSetCount][];
+            if(notUCIO) {
+                for (int i = 0; i < this.texCoordSetCount; i++) {
+                    this.indexTexCoord[i] = new int[indexCount];
+                }
+            }
+            maxTexCoordIndices = new int[texCoordSetCount];
+        }
+
+        if ((this.vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
+            this.indexVertexAttr = new int[this.vertexAttrCount][];
+            if (notUCIO) {
+                for (int i = 0; i < this.vertexAttrCount; i++) {
+                    this.indexVertexAttr[i] = new int[indexCount];
+                }
             }
+            this.maxVertexAttrIndices = new int[this.vertexAttrCount];
         }
-	maxTexCoordIndices = new int[texCoordSetCount];
     }
-  }
 
-  
-  Object cloneNonIndexedGeometry() {
-     GeometryArrayRetained obj = null;
-     int vOffset;
-     
-     switch (this.geoType) {
-     case GEO_TYPE_INDEXED_LINE_SET:
-        obj = new LineArrayRetained();
-        break;
-     case GEO_TYPE_INDEXED_POINT_SET:
-        obj = new PointArrayRetained();
-        break;
-     case GEO_TYPE_INDEXED_QUAD_SET:
-        obj = new QuadArrayRetained();
-        break;
-     case GEO_TYPE_INDEXED_TRI_SET:
-        obj = new TriangleArrayRetained();
-        break;
-     }
-     obj.createGeometryArrayData(validIndexCount, (vertexFormat & ~(GeometryArray.BY_REFERENCE|GeometryArray.INTERLEAVED|GeometryArray.USE_NIO_BUFFER)),
-				 texCoordSetCount, texCoordSetMap);
-     obj.cloneSourceArray = this;
-     obj.unIndexify(this);
- 
-     return (Object)obj;
-  }
 
-  void execute(long ctx, RenderAtom ra, boolean isNonUniformScale,
-		boolean updateAlpha, float alpha) {
-        throw new RuntimeException(J3dI18N.getString("IndexedGeometryArrayRetained0"));
-  }
+    GeometryArrayRetained cloneNonIndexedGeometry() {
+        GeometryArrayRetained obj = null;
+        int vOffset;
+
+        switch (this.geoType) {
+        case GEO_TYPE_INDEXED_LINE_SET:
+            obj = new LineArrayRetained();
+            break;
+        case GEO_TYPE_INDEXED_POINT_SET:
+            obj = new PointArrayRetained();
+            break;
+        case GEO_TYPE_INDEXED_QUAD_SET:
+            obj = new QuadArrayRetained();
+            break;
+        case GEO_TYPE_INDEXED_TRI_SET:
+            obj = new TriangleArrayRetained();
+            break;
+        default:
+            assert false; // Should never get here
+        }
+
+        obj.createGeometryArrayData(validIndexCount,
+                (vertexFormat & ~(GeometryArray.BY_REFERENCE|GeometryArray.INTERLEAVED|GeometryArray.USE_NIO_BUFFER)),
+                texCoordSetCount, texCoordSetMap,
+                vertexAttrCount, vertexAttrSizes);
+        obj.cloneSourceArray = this;
+        obj.unIndexify(this);
+
+        return obj;
+    }
 
 
   /**
@@ -127,14 +142,19 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 		    doTexCoordCheck(newMax, i);
 		}
 	    }
+            if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
+		for (int i = 0; i < vertexAttrCount; i++) {
+		    doVertexAttrCheck(newMax, i);
+		}
+            }
 	    if ((vertexFormat & GeometryArray.NORMALS) != 0) {
 		doNormalCheck(newMax);
-	    }	    
-	}	
+	    }
+	}
     }
-    
+
     void doCoordCheck(int newMax) {
-	// Check to make sure that the array length defined by the user is ateast maxCoordIndex long
+        // Check to make sure that the array length defined by the user is ateast maxCoordIndex long
 	if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
 	    if (newMax >= vertexCount) {
 		throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray23"));
@@ -404,6 +424,43 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 
     }
 
+    void doVertexAttrCheck(int newMax, int vertexAttrNum) {
+
+        // Check to make sure that the array length defined by the user is ateast maxVertexAttrIndex long
+        if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) == 0) {
+            return;
+        }
+
+        // Vertex attributes must not be interleaved
+        assert (vertexFormat & GeometryArray.INTERLEAVED) == 0;
+
+        if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
+            if (newMax >= vertexCount) {
+                throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray30"));
+            }
+        } else {
+            int multiplier = vertexAttrSizes[vertexAttrNum];
+
+            if(( vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) {
+                switch (vertexType & VATTR_DEFINED) {
+                case AF:
+                    if(multiplier * newMax >= floatBufferRefVertexAttrs[vertexAttrNum].limit()) {
+                        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray30"));
+                    }
+                    break;
+                }
+            } else {
+                switch (vertexType & VATTR_DEFINED) {
+                case AF:
+                    if (multiplier * newMax >= floatRefVertexAttrs[vertexAttrNum].length) {
+                        throw new ArrayIndexOutOfBoundsException(J3dI18N.getString("IndexedGeometryArray30"));
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
 
     /**
      * Sets the coordinate index associated with the vertex at
@@ -426,6 +483,11 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 		    maxTexCoordIndices[i] = newMax;
 		}
 	    }
+	    if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
+		for (int i = 0; i < vertexAttrCount; i++) {
+		    maxVertexAttrIndices[i] = newMax;
+		}
+	    }
 	    if ((vertexFormat & GeometryArray.NORMALS) != 0) {
 		maxNormalIndex = newMax;
 	    }	    
@@ -542,6 +604,11 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 		    maxTexCoordIndices[i] = newMax;
 		}
 	    }
+	    if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
+		for (i = 0; i < vertexAttrCount; i++) {
+		    maxVertexAttrIndices[i] = newMax;
+		}
+	    }
 	    if ((vertexFormat & GeometryArray.NORMALS) != 0) {
 		maxNormalIndex = newMax;
 	    }	    
@@ -568,33 +635,21 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
     final void setColorIndex(int index, int colorIndex) {
 	int newMax = maxColorIndex;
 
-	if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
-	    newMax = doIndexCheck(index, maxColorIndex, indexColor, colorIndex);
-	    if (newMax > maxColorIndex) {
-		doColorCheck(newMax);
-	    }
-	    geomLock.getLock();
-	    // No need to set INDEX_CHANGED since IndexBuffer
-	    // is used only when USE_COORD_INDEX_ONLY specified.
-	    // In this case only coordinate index array is 
-	    // considered.
-	    this.indexColor[index] = colorIndex;
-	    maxColorIndex = newMax;
-	    geomLock.unLock();
-	    if (!inUpdater && source != null && source.isLive()) {
-		sendDataChangedMessage(false);
-	    }
-	}
-	else {
-	    if ((vertexFormat & GeometryArray.COLOR) != 0) {
-                if (this.indexColor == null) {
-                    this.indexColor = new int[indexCount];
-        	    System.err.println(J3dI18N.getString("IndexedGeometryArrayRetained1"));
-                }
-            }
-            this.indexColor[index] = colorIndex;
-	}
-
+        newMax = doIndexCheck(index, maxColorIndex, indexColor, colorIndex);
+        if (newMax > maxColorIndex) {
+            doColorCheck(newMax);
+        }
+        geomLock.getLock();
+        // No need to set INDEX_CHANGED since IndexBuffer
+        // is used only when USE_COORD_INDEX_ONLY specified.
+        // In this case only coordinate index array is 
+        // considered.
+        this.indexColor[index] = colorIndex;
+        maxColorIndex = newMax;
+        geomLock.unLock();
+        if (!inUpdater && source != null && source.isLive()) {
+            sendDataChangedMessage(false);
+        }
     }
 
     /**
@@ -606,34 +661,20 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
     final void setColorIndices(int index, int colorIndices[]) {
 	int i, j, num = colorIndices.length;
 	int newMax;
-	
-	if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
-	    newMax = doIndicesCheck(index, maxColorIndex, indexColor, colorIndices);
-	    if (newMax > maxColorIndex) {
-		doColorCheck(newMax);
-	    }
-	    geomLock.getLock();
-	    maxColorIndex = newMax;
-	    for (i=0, j = index; i < num;i++, j++) {
-		this.indexColor[j] = colorIndices[i];
-	    }
-	    geomLock.unLock();
-	    if (!inUpdater && source != null && source.isLive()) {
-		sendDataChangedMessage(false);
-	    }
-	}
-	else {
-	    if ((vertexFormat & GeometryArray.COLOR) != 0) {
-                if (this.indexColor == null) {
-                    this.indexColor = new int[indexCount];
-        	    System.err.println(J3dI18N.getString("IndexedGeometryArrayRetained1"));
-                }
-            }
-	    for (i=0, j = index; i < num;i++, j++) {
-		this.indexColor[j] = colorIndices[i];
-	    }
-	}
 
+        newMax = doIndicesCheck(index, maxColorIndex, indexColor, colorIndices);
+        if (newMax > maxColorIndex) {
+            doColorCheck(newMax);
+        }
+        geomLock.getLock();
+        maxColorIndex = newMax;
+        for (i=0, j = index; i < num;i++, j++) {
+            this.indexColor[j] = colorIndices[i];
+        }
+        geomLock.unLock();
+        if (!inUpdater && source != null && source.isLive()) {
+            sendDataChangedMessage(false);
+        }
     }
 
     /**
@@ -644,30 +685,18 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
      */
     final void setNormalIndex(int index, int normalIndex) {
 	int newMax;
-	
-	if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
-	    newMax = doIndexCheck(index, maxNormalIndex, indexNormal, normalIndex);
-	    if (newMax > maxNormalIndex) {
-		doNormalCheck(newMax);
-	    }
-	    geomLock.getLock();
-	    maxNormalIndex = newMax;
-	    this.indexNormal[index] = normalIndex;
-	    geomLock.unLock();
-	    if (!inUpdater && source != null && source.isLive()) {
-		sendDataChangedMessage(false);
-	    }
-	}
-	else {
-	    if ((vertexFormat & GeometryArray.NORMALS) != 0) { 
-                if (this.indexNormal == null) {
-                    this.indexNormal = new int[indexCount];
-        	    System.err.println(J3dI18N.getString("IndexedGeometryArrayRetained2"));
-                }
-            }
-	    this.indexNormal[index] = normalIndex;
-	}
 
+        newMax = doIndexCheck(index, maxNormalIndex, indexNormal, normalIndex);
+        if (newMax > maxNormalIndex) {
+            doNormalCheck(newMax);
+        }
+        geomLock.getLock();
+        maxNormalIndex = newMax;
+        this.indexNormal[index] = normalIndex;
+        geomLock.unLock();
+        if (!inUpdater && source != null && source.isLive()) {
+            sendDataChangedMessage(false);
+        }
     }
 
     /**
@@ -679,34 +708,20 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
     final void setNormalIndices(int index, int normalIndices[]) {
 	int i, j, num = normalIndices.length;
 	int newMax;
-	
-	if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
-	    newMax = doIndicesCheck(index, maxNormalIndex, indexNormal, normalIndices);
-	    if (newMax > maxNormalIndex) {
-		doNormalCheck(newMax);
-	    }
-	    geomLock.getLock();
-	    for (i=0, j = index; i < num;i++, j++) {
-		this.indexNormal[j] = normalIndices[i];
-	    }
-	    maxNormalIndex = newMax;
-	    geomLock.unLock();
-	    if (!inUpdater && source != null && source.isLive()) {
-		sendDataChangedMessage(false);
-	    }
-	}
-	else {
-	    if ((vertexFormat & GeometryArray.NORMALS) != 0) {
-                if (this.indexNormal == null) {
-                    this.indexNormal = new int[indexCount];
-        	    System.err.println(J3dI18N.getString("IndexedGeometryArrayRetained2"));
-                }
-            }
-	    for (i=0, j = index; i < num;i++, j++) {
-		this.indexNormal[j] = normalIndices[i];
-	    }
-	}
 
+        newMax = doIndicesCheck(index, maxNormalIndex, indexNormal, normalIndices);
+        if (newMax > maxNormalIndex) {
+            doNormalCheck(newMax);
+        }
+        geomLock.getLock();
+        for (i=0, j = index; i < num;i++, j++) {
+            this.indexNormal[j] = normalIndices[i];
+        }
+        maxNormalIndex = newMax;
+        geomLock.unLock();
+        if (!inUpdater && source != null && source.isLive()) {
+            sendDataChangedMessage(false);
+        }
     }
 
     /**
@@ -718,33 +733,19 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
      */
     final void setTextureCoordinateIndex(int texCoordSet, int index, int texCoordIndex) {
 	int newMax;
-	int [] indices = (int[])this.indexTexCoord[texCoordSet];
-	
-	if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
-	    newMax = doIndexCheck(index, maxTexCoordIndices[texCoordSet],indices, texCoordIndex);
-	    if (newMax > maxTexCoordIndices[texCoordSet]) {
-		doTexCoordCheck(newMax, texCoordSet);
-	    }
-	    geomLock.getLock();
-	    maxTexCoordIndices[texCoordSet] = newMax;
-	    indices[index] = texCoordIndex;
-	    geomLock.unLock();
-	    if (!inUpdater && source != null && source.isLive()) {
-		sendDataChangedMessage(false);
-	    }
-	}
-	else {
-	    if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
-                if (indices == null) {
-                    indices = new int[indexCount];
-                    this.indexTexCoord[texCoordSet] = indices;
-        	    System.err.println(J3dI18N.getString("IndexedGeometryArrayRetained3"));
-                }
-            }
-	    indices[index] = texCoordIndex;
-	}
-
+	int [] indices = this.indexTexCoord[texCoordSet];
 
+        newMax = doIndexCheck(index, maxTexCoordIndices[texCoordSet],indices, texCoordIndex);
+        if (newMax > maxTexCoordIndices[texCoordSet]) {
+            doTexCoordCheck(newMax, texCoordSet);
+        }
+        geomLock.getLock();
+        maxTexCoordIndices[texCoordSet] = newMax;
+        indices[index] = texCoordIndex;
+        geomLock.unLock();
+        if (!inUpdater && source != null && source.isLive()) {
+            sendDataChangedMessage(false);
+        }
     }
 
     /**
@@ -754,42 +755,80 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
      * @param index the vertex index
      * @param texCoordIndices an array of texture coordinate indices
      */
-  final void setTextureCoordinateIndices(int texCoordSet, int index, int texCoordIndices[]) {
-      int i, j, num = texCoordIndices.length;
-      int [] indices = (int[])this.indexTexCoord[texCoordSet];
+    final void setTextureCoordinateIndices(int texCoordSet, int index, int texCoordIndices[]) {
+        int i, j, num = texCoordIndices.length;
+        int [] indices = this.indexTexCoord[texCoordSet];
 
-      int newMax;
-	
-      if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
-	  newMax = doIndicesCheck(index, maxTexCoordIndices[texCoordSet], indices, texCoordIndices);
-	  if (newMax > maxTexCoordIndices[texCoordSet]) {
-	      doTexCoordCheck(newMax, texCoordSet);
-	  }
-	  geomLock.getLock();
-	  maxTexCoordIndices[texCoordSet] = newMax;
-	  for (i=0, j = index; i < num;i++, j++) {
-	      indices[j] = texCoordIndices[i];
-	  }
-	  geomLock.unLock();
-	  if (!inUpdater && source != null && source.isLive()) {
-	      sendDataChangedMessage(false);
-	  }
-
-      }
-      else {
-	  if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
-              if (indices == null) {
-                  indices = new int[indexCount];
-                  this.indexTexCoord[texCoordSet] = indices;
-        	  System.err.println(J3dI18N.getString("IndexedGeometryArrayRetained3"));
-              }
-          }
-	  for (i=0, j = index; i < num;i++, j++) {
-	      indices[j] = texCoordIndices[i];
-	  }
-      }
+        int newMax;
 
-  }
+        newMax = doIndicesCheck(index, maxTexCoordIndices[texCoordSet], indices, texCoordIndices);
+        if (newMax > maxTexCoordIndices[texCoordSet]) {
+            doTexCoordCheck(newMax, texCoordSet);
+        }
+        geomLock.getLock();
+        maxTexCoordIndices[texCoordSet] = newMax;
+        for (i=0, j = index; i < num;i++, j++) {
+            indices[j] = texCoordIndices[i];
+        }
+        geomLock.unLock();
+        if (!inUpdater && source != null && source.isLive()) {
+            sendDataChangedMessage(false);
+        }
+    }
+
+    /**
+     * Sets the vertex attribute index associated with the vertex at
+     * the specified index for the specified vertex attribute number
+     * for this object.
+     */
+    public void setVertexAttrIndex(int vertexAttrNum,
+                                   int index,
+                                   int vertexAttrIndex) {
+
+	int newMax;
+	int [] indices = this.indexVertexAttr[vertexAttrNum];
+
+        newMax = doIndexCheck(index, maxVertexAttrIndices[vertexAttrNum],indices, vertexAttrIndex);
+        if (newMax > maxVertexAttrIndices[vertexAttrNum]) {
+            doVertexAttrCheck(newMax, vertexAttrNum);
+        }
+        geomLock.getLock();
+        maxVertexAttrIndices[vertexAttrNum] = newMax;
+        indices[index] = vertexAttrIndex;
+        geomLock.unLock();
+        if (!inUpdater && source != null && source.isLive()) {
+            sendDataChangedMessage(false);
+        }
+    }
+
+    /**
+     * Sets the vertex attribute indices associated with the vertices
+     * starting at the specified index for the specified vertex attribute number
+     * for this object.
+     */
+    public void setVertexAttrIndices(int vertexAttrNum,
+                                     int index,
+                                     int[] vertexAttrIndices) {
+  
+        int i, j, num = vertexAttrIndices.length;
+        int [] indices = this.indexVertexAttr[vertexAttrNum];
+
+        int newMax;
+
+        newMax = doIndicesCheck(index, maxVertexAttrIndices[vertexAttrNum], indices, vertexAttrIndices);
+        if (newMax > maxVertexAttrIndices[vertexAttrNum]) {
+            doVertexAttrCheck(newMax, vertexAttrNum);
+        }
+        geomLock.getLock();
+        maxVertexAttrIndices[vertexAttrNum] = newMax;
+        for (i=0, j = index; i < num;i++, j++) {
+            indices[j] = vertexAttrIndices[i];
+        }
+        geomLock.unLock();
+        if (!inUpdater && source != null && source.isLive()) {
+            sendDataChangedMessage(false);
+        }
+    }
 
     /**
      * Retrieves the coordinate index associated with the vertex at
@@ -807,14 +846,13 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
      * @param index the vertex index
      * @param coordinateIndices array that will receive the coordinate indices
      */
-  final void getCoordinateIndices(int index, int coordinateIndices[]) {
-    int i, j, num = coordinateIndices.length;
+    final void getCoordinateIndices(int index, int coordinateIndices[]) {
+        int i, j, num = coordinateIndices.length;
 
-    for (i=0, j = index;i < num;i++, j++)
-      {
-	coordinateIndices[i] = this.indexCoord[j];
-      }
-  }
+        for (i=0, j = index;i < num;i++, j++) {
+            coordinateIndices[i] = this.indexCoord[j];
+        }
+    }
 
     /**
      * Retrieves the color index associated with the vertex at
@@ -823,13 +861,6 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
      * @return the color index
      */
     final int getColorIndex(int index) {
-	if (((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) &&
-	   ((vertexFormat & GeometryArray.COLOR) != 0)) {
-            if (this.indexColor == null) {
-                this.indexColor = new int[indexCount];
-                System.err.println(J3dI18N.getString("IndexedGeometryArrayRetained1"));
-            }
-        }
 	return this.indexColor[index];
     }
 
@@ -839,22 +870,14 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
      * @param index the vertex index
      * @param colorIndices array that will receive the color indices
      */
-  final void getColorIndices(int index, int colorIndices[]) {
-    int i, j, num = colorIndices.length;
-    if (((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) &&
-       ((vertexFormat & GeometryArray.COLOR) != 0)) {
-        if (this.indexColor == null) {
-            this.indexColor = new int[indexCount];
-            System.err.println(J3dI18N.getString("IndexedGeometryArrayRetained1"));
+    final void getColorIndices(int index, int colorIndices[]) {
+        int i, j, num = colorIndices.length;
+
+        for (i=0, j = index;i < num;i++, j++) {
+            colorIndices[i] = this.indexColor[j];
         }
     }
 
-    for (i=0, j = index;i < num;i++, j++)
-      {
-	colorIndices[i] = this.indexColor[j];
-      }
-  }
-
     /**
      * Retrieves the normal index associated with the vertex at
      * the specified index for this object.
@@ -862,13 +885,6 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
      * @return the normal index
      */
     final int getNormalIndex(int index) {
-	if (((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) &&
-           ((vertexFormat & GeometryArray.NORMALS) != 0)) {
-            if (this.indexNormal == null) {
-                this.indexNormal = new int[indexCount];
-                System.err.println(J3dI18N.getString("IndexedGeometryArrayRetained2"));
-            }
-        }
 	return this.indexNormal[index];
     }
 
@@ -878,22 +894,14 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
      * @param index the vertex index
      * @param normalIndices array that will receive the normal indices
      */
-  final void getNormalIndices(int index, int normalIndices[]) {
-    int i, j, num = normalIndices.length;
-    if (((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) &&
-       ((vertexFormat & GeometryArray.NORMALS) != 0)) {
-        if (this.indexNormal == null) {
-            this.indexNormal = new int[indexCount];
-            System.err.println(J3dI18N.getString("IndexedGeometryArrayRetained2"));
+    final void getNormalIndices(int index, int normalIndices[]) {
+        int i, j, num = normalIndices.length;
+
+        for (i=0, j = index;i < num;i++, j++) {
+            normalIndices[i] = this.indexNormal[j];
         }
     }
 
-    for (i=0, j = index;i < num;i++, j++)
-      {
-	normalIndices[i] = this.indexNormal[j];
-      }
-  }
-
     /**
      * Retrieves the texture coordinate index associated with the vertex at
      * the specified index for this object.
@@ -902,16 +910,9 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
      * @return the texture coordinate index
      */
     final int getTextureCoordinateIndex(int texCoordSet, int index) {
-	int [] indices = (int[])this.indexTexCoord[texCoordSet];
-        if (((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) &&
-	   ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0)) {
-            if (indices == null) {
-                indices = new int[indexCount];
-                this.indexTexCoord[texCoordSet] = indices;
-                System.err.println(J3dI18N.getString("IndexedGeometryArrayRetained3"));
-            }
-        }
-	return indices[index];
+	int [] indices = this.indexTexCoord[texCoordSet];
+
+        return indices[index];
     }
 
     /**
@@ -921,120 +922,147 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
      * @param index the vertex index
      * @param texCoordIndices array that will receive the texture coordinate indices
      */
-  final void getTextureCoordinateIndices(int texCoordSet, int index, int texCoordIndices[]) {
-    int i, j, num = texCoordIndices.length;
-    int [] indices = (int[])this.indexTexCoord[texCoordSet];
-    if (((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) &&
-       ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0)) {
-        if (indices == null) {
-            indices = new int[indexCount];
-            this.indexTexCoord[texCoordSet] = indices;
-            System.err.println(J3dI18N.getString("IndexedGeometryArrayRetained3"));
+    final void getTextureCoordinateIndices(int texCoordSet, int index, int texCoordIndices[]) {
+        int i, j, num = texCoordIndices.length;
+        int [] indices = this.indexTexCoord[texCoordSet];
+
+        for (i=0, j = index;i < num;i++, j++) {
+            texCoordIndices[i] = indices[j];
         }
     }
 
-    for (i=0, j = index;i < num;i++, j++)
-      {
-	texCoordIndices[i] = indices[j];
-      }
-  }
+    /**
+     * Retrieves the vertex attribute index associated with the vertex at
+     * the specified index for the specified vertex attribute number
+     * for this object.
+     */
+    public int getVertexAttrIndex(int vertexAttrNum,
+                                  int index) {
+
+        int [] indices = this.indexVertexAttr[vertexAttrNum];
 
-    // used for GeometryArrays
-    native void executeIndexedGeometry(long ctx,
-			GeometryArrayRetained geo, int geo_type, 
-			boolean isNonUniformScale,
-			boolean useAlpha,
-			boolean multiScreen,
-			boolean ignoreVertexColors,
-		        int initialIndexIndex,
-			int indexCount,
-			int vertexCount, int vformat, 
-			int texCoordSetCount, int texCoordSetMap[],
-			int texCoordSetMapLen,
-		        int[] texCoordSetOffset,
-			int numActiveTexUnitState,
-			int[] texUnitStateMap,
-			float[] varray, float[] cdata, 
-		        int texUnitIndex, int cdirty, 
-			int[] indexCoord);
-
-    // used for interleaved, by reference, nio buffer 
-    native void executeIndexedGeometryBuffer(long ctx,
-				       GeometryArrayRetained geo, int geo_type, 
-				       boolean isNonUniformScale,
-				       boolean useAlpha,
-				       boolean multiScreen,
-				       boolean ignoreVertexColors,
-				       int initialIndexIndex,
-				       int indexCount,
-				       int vertexCount, int vformat, 
-				       int texCoordSetCount, int texCoordSetMap[],
-				       int texCoordSetMapLen,
-				       int[] texCoordSetOffset,
-				       int numActiveTexUnitState,
-				       int[] texUnitStateMap,
-				       Object varray, float[] cdata, 
-				       int texUnitIndex, int cdirty, 
-				       int[] indexCoord);
+        return indices[index];
+    }
 
+    /**
+     * Retrieves the vertex attribute indices associated with the vertices
+     * starting at the specified index for the specified vertex attribute number
+     * for this object.
+     */
+    public void getVertexAttrIndices(int vertexAttrNum,
+                                     int index,
+                                     int[] vertexAttrIndices) {
 
-	
-    native void executeIndexedGeometryVA(long ctx,
-					 GeometryArrayRetained geo, int geo_type, 
-					 boolean isNonUniformScale, 
-					 boolean multiScreen,
-					 boolean ignoreVertexColors,
-					 int initialIndexIndex,
-					 int validIndexCount,
-					 int vertexCount,		 
-					 int vformat,
-					 int vdefined,
-					 float[] vfcoords, double[] vdcoords,
-					 float[] cfdata, byte[] cbdata,
-					 float[] ndata,
-					 int pass, int texcoordmaplength, 
-					 int[] texcoordoffset, 
-					 int numActiveTexUnitState, int[] texunitstatemap,
-					 int texstride, Object[] texCoords,
-					 int cdirty, 
-					 int[] indexCoord);
+        int i, j, num = vertexAttrIndices.length;
+        int [] indices = this.indexVertexAttr[vertexAttrNum];
+
+        for (i=0, j = index;i < num;i++, j++) {
+            vertexAttrIndices[i] = indices[j];
+        }
+    }
+
+    // by-copy or interleaved, by reference, Java arrays
+    private native void executeIndexedGeometry(long ctx,
+            GeometryArrayRetained geo, int geo_type,
+            boolean isNonUniformScale,
+            boolean useAlpha,
+            boolean multiScreen,
+            boolean ignoreVertexColors,
+            int initialIndexIndex,
+            int indexCount,
+            int vertexCount, int vformat,
+            int vertexAttrCount, int[] vertexAttrSizes,
+            int texCoordSetCount, int texCoordSetMap[],
+            int texCoordSetMapLen,
+            int[] texCoordSetOffset,
+            int numActiveTexUnitState,
+            int[] texUnitStateMap,
+            float[] varray, float[] cdata,
+            int texUnitIndex, int cdirty,
+            int[] indexCoord);
+
+    // interleaved, by reference, nio buffer
+    private native void executeIndexedGeometryBuffer(long ctx,
+            GeometryArrayRetained geo, int geo_type,
+            boolean isNonUniformScale,
+            boolean useAlpha,
+            boolean multiScreen,
+            boolean ignoreVertexColors,
+            int initialIndexIndex,
+            int indexCount,
+            int vertexCount, int vformat,
+            int texCoordSetCount, int texCoordSetMap[],
+            int texCoordSetMapLen,
+            int[] texCoordSetOffset,
+            int numActiveTexUnitState,
+            int[] texUnitStateMap,
+            Object varray, float[] cdata,
+            int texUnitIndex, int cdirty,
+            int[] indexCoord);
+
+    // non interleaved, by reference, Java arrays
+    private native void executeIndexedGeometryVA(long ctx,
+            GeometryArrayRetained geo, int geo_type,
+            boolean isNonUniformScale,
+            boolean multiScreen,
+            boolean ignoreVertexColors,
+            int initialIndexIndex,
+            int validIndexCount,
+            int vertexCount,
+            int vformat,
+            int vdefined,
+            float[] vfcoords, double[] vdcoords,
+            float[] cfdata, byte[] cbdata,
+            float[] ndata,
+            int vertexAttrCount, int[] vertexAttrSizes,
+            float[][] vertexAttrData,
+            int pass, int texcoordmaplength,
+            int[] texcoordoffset,
+            int numActiveTexUnitState, int[] texunitstatemap,
+            int texstride, Object[] texCoords,
+            int cdirty,
+            int[] indexCoord);
 
     // non interleaved, by reference, nio buffer
-    native void executeIndexedGeometryVABuffer(long ctx,
-					       GeometryArrayRetained geo, int geo_type, 
-					       boolean isNonUniformScale, 
-					       boolean multiScreen,
-					       boolean ignoreVertexColors,
-					       int initialIndexIndex,
-					       int validIndexCount,
-					       int vertexCount,		 
-					       int vformat,
-					       int vdefined,
-					       Object vcoords,
-					       Object cdataBuffer,	       
-					       float[] cfdata, byte[] cbdata,
-					       Object normal,
-					       int pass, int texcoordmaplength, 
-					       int[] texcoordoffset, 
-					       int numActiveTexUnitState, int[] texunitstatemap,
-					       int texstride, Object[] texCoords,
-					       int cdirty, 
-					       int[] indexCoord);
-
-    // used for IndexedGeometry
-    native void buildIndexedGeometry(long ctx, GeometryArrayRetained geo, int geo_type, 
-				     boolean isNonUniformScale, boolean updateAlpha,
-				     float alpha,
-				     boolean ignoreVertexColors,
-				     int initialIndexIndex,
-				     int validIndexCount,
-				     int vertexCount,
-				     int vformat, 
-				     int texCoordSetCount, int texCoordSetMap[],
-				     int texCoordSetMapLen,
-				     int[] texCoordSetMapOffset, 
-				     double[] xform, double[] nxform,
-				     float[] varray, int[] indexCoord);
+    private native void executeIndexedGeometryVABuffer(long ctx,
+            GeometryArrayRetained geo, int geo_type,
+            boolean isNonUniformScale,
+            boolean multiScreen,
+            boolean ignoreVertexColors,
+            int initialIndexIndex,
+            int validIndexCount,
+            int vertexCount,
+            int vformat,
+            int vdefined,
+            Object vcoords,
+            Object cdataBuffer,
+            float[] cfdata, byte[] cbdata,
+            Object normal,
+            int vertexAttrCount, int[] vertexAttrSizes,
+            Object[] vertexAttrData,
+            int pass, int texcoordmaplength,
+            int[] texcoordoffset,
+            int numActiveTexUnitState, int[] texunitstatemap,
+            int texstride, Object[] texCoords,
+            int cdirty,
+            int[] indexCoord);
+
+    // by-copy geometry
+    private native void buildIndexedGeometry(long ctx,
+            GeometryArrayRetained geo, int geo_type,
+            boolean isNonUniformScale, boolean updateAlpha,
+            float alpha,
+            boolean ignoreVertexColors,
+            int initialIndexIndex,
+            int validIndexCount,
+            int vertexCount,
+            int vformat,
+            int vertexAttrCount, int[] vertexAttrSizes,
+            int texCoordSetCount, int texCoordSetMap[],
+            int texCoordSetMapLen,
+            int[] texCoordSetMapOffset,
+            double[] xform, double[] nxform,
+            float[] varray, int[] indexCoord);
 
 
     void execute(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale, 
@@ -1081,23 +1109,24 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 		    // RenderBin render the geometry. So it is safe
 		    // just to set the dirty flag here
 		    dirtyFlag = 0;
-		}
-	    
-		executeIndexedGeometry(cv.ctx, this, geoType, isNonUniformScale, 
-				       useAlpha,
-				       multiScreen,
-				       ignoreVertexColors,
-				       initialIndexIndex, 
-				       validIndexCount,
-                // Vertex Count is maxCoordIndex + 1
-				       maxCoordIndex + 1, 
-				       ((vertexFormat & GeometryArray.COLOR) != 0)?(vertexFormat|GeometryArray.COLOR_4):vertexFormat,
-				       texCoordSetCount, texCoordSetMap,
-				       (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
-				       texCoordSetMapOffset, 
-				       cv.numActiveTexUnit, cv.texUnitStateMap, 
-				       vdata, null,
-				       pass, cdirty, indexCoord);
+                }
+
+                executeIndexedGeometry(cv.ctx, this, geoType, isNonUniformScale,
+                        useAlpha,
+                        multiScreen,
+                        ignoreVertexColors,
+                        initialIndexIndex,
+                        validIndexCount,
+                        // Vertex Count is maxCoordIndex + 1
+                        maxCoordIndex + 1,
+                        ((vertexFormat & GeometryArray.COLOR) != 0)?(vertexFormat|GeometryArray.COLOR_4):vertexFormat,
+                        vertexAttrCount, vertexAttrSizes,
+                        texCoordSetCount, texCoordSetMap,
+                        (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
+                        texCoordSetMapOffset,
+                        cv.numActiveTexUnit, cv.texUnitStateMap,
+                        vdata, null,
+                        pass, cdirty, indexCoord);
 
 
 	    } // end of non by reference
@@ -1127,31 +1156,34 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 		    }
 		    dirtyFlag = 0;
 		}
-		
-		executeIndexedGeometry(cv.ctx, this, geoType, isNonUniformScale, 
-				       useAlpha,
-				       multiScreen,
-				       ignoreVertexColors,
-				       initialIndexIndex, 
-				       validIndexCount, 
-				       maxCoordIndex + 1,
-				       vertexFormat,
-				       texCoordSetCount, texCoordSetMap,
-				       (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
-				       texCoordSetMapOffset, 
-				       cv.numActiveTexUnit, cv.texUnitStateMap, 
-				       interLeavedVertexData, cdata,
-				       pass, cdirty, indexCoord);
+
+                executeIndexedGeometry(cv.ctx, this, geoType, isNonUniformScale,
+                        useAlpha,
+                        multiScreen,
+                        ignoreVertexColors,
+                        initialIndexIndex,
+                        validIndexCount,
+                        maxCoordIndex + 1,
+                        vertexFormat,
+                        vertexAttrCount, vertexAttrSizes,
+                        texCoordSetCount, texCoordSetMap,
+                        (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
+                        texCoordSetMapOffset,
+                        cv.numActiveTexUnit, cv.texUnitStateMap,
+                        interLeavedVertexData, cdata,
+                        pass, cdirty, indexCoord);
 	    }  //end of interleaved
-	    else { 
-		// Check if a vertexformat is set, but the array is null
+	    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) || 
+		     (vertexType & NORMAL_DEFINED) == 0) ||
+		    (((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) &&
+		     (vertexType & VATTR_DEFINED) == 0) ||
 		    (((vertexFormat& GeometryArray.TEXTURE_COORDINATE) != 0) &&
 		     (vertexType & TEXCOORD_DEFINED) == 0)) {
 		    return;  
@@ -1215,29 +1247,32 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 			vdefined |= COLOR_BYTE;
 		    if((vertexType & NORMAL_DEFINED) != 0)
 			vdefined |= NORMAL_FLOAT;
-		    if((vertexType & TEXCOORD_DEFINED) != 0)
-			vdefined |= TEXCOORD_FLOAT;
-		    
-		    executeIndexedGeometryVA(cv.ctx, this, geoType, isNonUniformScale, 
-					     multiScreen, 
-					     ignoreVertexColors,
-					     initialIndexIndex,
-					     validIndexCount,
-					     maxCoordIndex + 1,
-					     (vertexFormat | c4fAllocated),
-					     vdefined,
-					     mirrorFloatRefCoords, mirrorDoubleRefCoords,
-					     cfdata, cbdata,
-					     mirrorFloatRefNormals,
-					     pass,
-					     ((texCoordSetMap == null) ? 0:texCoordSetMap.length),
-					     texCoordSetMap,
-					     cv.numActiveTexUnit,
-					     cv.texUnitStateMap,
-					     texCoordStride,
-					     mirrorRefTexCoords, cdirty, indexCoord);
-
-		}
+                    if((vertexType & VATTR_DEFINED) != 0)
+                        vdefined |= VATTR_FLOAT;
+                    if((vertexType & TEXCOORD_DEFINED) != 0)
+                        vdefined |= TEXCOORD_FLOAT;
+
+                    executeIndexedGeometryVA(cv.ctx, this, geoType, isNonUniformScale,
+                            multiScreen,
+                            ignoreVertexColors,
+                            initialIndexIndex,
+                            validIndexCount,
+                            maxCoordIndex + 1,
+                            (vertexFormat | c4fAllocated),
+                            vdefined,
+                            mirrorFloatRefCoords, mirrorDoubleRefCoords,
+                            cfdata, cbdata,
+                            mirrorFloatRefNormals,
+                            vertexAttrCount, vertexAttrSizes,
+                            mirrorFloatRefVertexAttrs,
+                            pass,
+                            ((texCoordSetMap == null) ? 0:texCoordSetMap.length),
+                            texCoordSetMap,
+                            cv.numActiveTexUnit,
+                            cv.texUnitStateMap,
+                            texCoordStride,
+                            mirrorRefTexCoords, cdirty, indexCoord);
+                }
 	    } // end of non interleaved and by reference
 	}//end of non io buffer
 
@@ -1284,15 +1319,17 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 				       interleavedFloatBufferImpl.getBufferAsObject(), cdata,
 				       pass, cdirty, indexCoord);
 	    }  //end of interleaved
-	    else { 
-		// Check if a vertexformat is set, but the array is null
+	    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) || 
+		     (vertexType & NORMAL_DEFINED) == 0) ||
+		    (((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) &&
+		     (vertexType & VATTR_DEFINED) == 0) ||
 		    (((vertexFormat& GeometryArray.TEXTURE_COORDINATE) != 0) &&
 		     (vertexType & TEXCOORD_DEFINED) == 0)) {
 		    return;  
@@ -1311,7 +1348,7 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 				    cdirty |= COLOR_CHANGED;
 				}
 			    } else {
-				// TODO: handle transparency case
+				// XXXX: handle transparency case
 				//cfdata = null;
 				cfdata = mirrorFloatRefColors[0];
 				// if transparency switch between on/off
@@ -1334,7 +1371,7 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 				    cdirty |= COLOR_CHANGED;
 				}
 			    } else {
-				// TODO: handle transparency case
+				// XXXX: handle transparency case
 				// cbdata = null;
 				cbdata = mirrorUnsignedByteRefColors[0];
 				// if transparency switch between on/off
@@ -1371,29 +1408,37 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 			vdefined |= NORMAL_FLOAT;
 			normal = floatBufferRefNormals.getBufferAsObject();
 		    }
-		    
-		    if((vertexType & TEXCOORD_DEFINED) != 0)
-		       vdefined |= TEXCOORD_FLOAT;
-
-		    executeIndexedGeometryVABuffer(cv.ctx, this, geoType, isNonUniformScale, 
-						   multiScreen, 
-						   ignoreVertexColors,
-						   initialIndexIndex,
-						   validIndexCount,
-						   maxCoordIndex + 1,
-						   (vertexFormat | c4fAllocated),
-						   vdefined,
-						   vcoord,
-						   cdataBuffer,
-						   cfdata, cbdata,
-						   normal,
-						   pass,
-						   ((texCoordSetMap == null) ? 0:texCoordSetMap.length),
-						   texCoordSetMap,
-						   cv.numActiveTexUnit,
-						   cv.texUnitStateMap,
-						   texCoordStride,
-						   refTexCoords, cdirty, indexCoord);
+
+                    if ((vertexType & VATTR_DEFINED) != 0) {
+                       vdefined |= VATTR_FLOAT;
+                    }
+
+                    if ((vertexType & TEXCOORD_DEFINED) != 0) {
+                       vdefined |= TEXCOORD_FLOAT;
+                    }
+
+                    executeIndexedGeometryVABuffer(cv.ctx,
+                            this, geoType, isNonUniformScale,
+                            multiScreen,
+                            ignoreVertexColors,
+                            initialIndexIndex,
+                            validIndexCount,
+                            maxCoordIndex + 1,
+                            (vertexFormat | c4fAllocated),
+                            vdefined,
+                            vcoord,
+                            cdataBuffer,
+                            cfdata, cbdata,
+                            normal,
+                            vertexAttrCount, vertexAttrSizes,
+                            nioFloatBufferRefVertexAttrs,
+                            pass,
+                            ((texCoordSetMap == null) ? 0:texCoordSetMap.length),
+                            texCoordSetMap,
+                            cv.numActiveTexUnit,
+                            cv.texUnitStateMap,
+                            texCoordStride,
+                            refTexCoords, cdirty, indexCoord);
 
 		}
 	    } // end of non interleaved and by reference
@@ -1413,7 +1458,7 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 	else {
 	    
 	    if ((vertexFormat & GeometryArray.BY_REFERENCE) == 0) {
-		float[] vdata;
+                float[] vdata;
 		//	    System.out.println("by-copy");
 		synchronized (this) {
 		    cdirty = dirtyFlag;
@@ -1443,19 +1488,26 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 		    dirtyFlag = 0;
 		}
 
-		buildIndexedGeometry(cv.ctx, this, geoType, isNonUniformScale, 
-				     updateAlpha, alpha, ignoreVertexColors,
-				     initialIndexIndex,
-				     validIndexCount,
-				     maxCoordIndex + 1,
-				     vertexFormat, 
-				     texCoordSetCount, texCoordSetMap,
-				     (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
-				     texCoordSetMapOffset, 
-				     (xform == null) ? null : xform.mat,
-				     (nxform == null) ? null : nxform.mat,
-				     vdata, indexCoord);
+                buildIndexedGeometry(cv.ctx, this, geoType, isNonUniformScale,
+                        updateAlpha, alpha, ignoreVertexColors,
+                        initialIndexIndex,
+                        validIndexCount,
+                        maxCoordIndex + 1,
+                        vertexFormat,
+                        vertexAttrCount, vertexAttrSizes,
+                        texCoordSetCount, texCoordSetMap,
+                        (texCoordSetMap == null) ? 0 : texCoordSetMap.length,
+                        texCoordSetMapOffset,
+                        (xform == null) ? null : xform.mat,
+                        (nxform == null) ? null : nxform.mat,
+                        vdata, indexCoord);
 	    }
+            // XXXX: Note that there is no "else" clause here, and no
+            // buildIndexedGeometryForByRef() method.
+            // We would need to create one if we ever wanted to support by-ref
+            // indexed geometry in display lists. Better yet, we could fix
+            // canBeInDisplayList so that unindexified by-ref geometry could
+            // go into a display list.
 	}
     }
     
@@ -1478,11 +1530,12 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
                 indexColor = new int[indexCount];
             if ((vertexFormat  &  GeometryArray.NORMALS) != 0)
                 indexNormal = new int[indexCount];
-            // We only merge if texCoordSetCount = 1
+            // We only merge if the texCoordSetCount is 1 and there are no
+            // vertex attrs
             if ((vertexFormat  &  GeometryArray.TEXTURE_COORDINATE) != 0) {
-                indexTexCoord = new Object[1];
+                indexTexCoord = new int[1][];
                 indexTexCoord[0] = new int[indexCount];
-                texCoord = (int[])indexTexCoord[0];
+                texCoord = indexTexCoord[0];
             }
         }
 	int curDataOffset = 0;
@@ -1500,7 +1553,7 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 	            if ((vertexFormat  &  GeometryArray.NORMALS) != 0) 
 	                indexNormal[j+curIndexOffset] = geo.indexNormal[j+geo.initialIndexIndex]+curDataOffset;
 	            if ((vertexFormat  &  GeometryArray.TEXTURE_COORDINATE) != 0) 
-	                texCoord[j+curIndexOffset] = ((int[])geo.indexTexCoord[0])[j+geo.initialIndexIndex]+curDataOffset;
+	                texCoord[j+curIndexOffset] = geo.indexTexCoord[0][j+geo.initialIndexIndex]+curDataOffset;
                 }
 	    }
 	    maxCoordIndex = geo.maxCoordIndex +curDataOffset;
@@ -1517,12 +1570,14 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 
     boolean isWriteStatic() {
 
-	if (!super.isWriteStatic() ||
-	    source.getCapability(IndexedGeometryArray.ALLOW_COORDINATE_INDEX_WRITE ) ||
-	    source.getCapability(IndexedGeometryArray.ALLOW_COLOR_INDEX_WRITE) || 
-	    source.getCapability(IndexedGeometryArray.ALLOW_NORMAL_INDEX_WRITE) ||
-	    source.getCapability(IndexedGeometryArray.ALLOW_TEXCOORD_INDEX_WRITE)) 
-	    return false;
+        if (!super.isWriteStatic() ||
+                source.getCapability(IndexedGeometryArray.ALLOW_COORDINATE_INDEX_WRITE ) ||
+                source.getCapability(IndexedGeometryArray.ALLOW_COLOR_INDEX_WRITE) ||
+                source.getCapability(IndexedGeometryArray.ALLOW_NORMAL_INDEX_WRITE) ||
+                source.getCapability(IndexedGeometryArray.ALLOW_VERTEX_ATTR_INDEX_WRITE) ||
+                source.getCapability(IndexedGeometryArray.ALLOW_TEXCOORD_INDEX_WRITE)) {
+            return false;
+        }
 
 	return true;
     }
@@ -1556,8 +1611,9 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 	int newCoordMax =0;
 	int newColorIndex=0;
 	int newNormalIndex=0;
-	int newTexCoordIndex[]=null;
-	
+	int[] newTexCoordIndex = null;
+        int[] newVertexAttrIndex = null;
+
 	newCoordMax = computeMaxIndex(initialIndexIndex, validIndexCount,indexCoord );
 	doErrorCheck(newCoordMax);
 	if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
@@ -1569,10 +1625,19 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 		newTexCoordIndex = new int[texCoordSetCount];
 		for (int i = 0; i < texCoordSetCount; i++) {
 		   newTexCoordIndex[i] =  computeMaxIndex(initialIndexIndex,validIndexCount,
-								  (int[])indexTexCoord[i]);
+								  indexTexCoord[i]);
 		   doTexCoordCheck(newTexCoordIndex[i], i);
 		}
 	    }
+	    if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
+		newVertexAttrIndex = new int[vertexAttrCount];
+		for (int i = 0; i < vertexAttrCount; i++) {
+		   newVertexAttrIndex[i] = computeMaxIndex(initialIndexIndex,
+                                                           validIndexCount,
+                                                           indexVertexAttr[i]);
+		   doVertexAttrCheck(newVertexAttrIndex[i], i);
+		}
+	    }
 	    if ((vertexFormat & GeometryArray.NORMALS) != 0) {
 		newNormalIndex = computeMaxIndex(initialIndexIndex, validIndexCount, indexNormal);
 		doNormalCheck(newNormalIndex);
@@ -1589,6 +1654,11 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 		    maxTexCoordIndices[i] = newTexCoordIndex[i];
 		}
 	    }
+	    if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
+		for (int i = 0; i < vertexAttrCount; i++) {
+		    maxVertexAttrIndices[i] = newVertexAttrIndex[i];
+		}
+	    }
 	    maxNormalIndex = newNormalIndex;
 	}
 	else {
@@ -1599,6 +1669,11 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 		    maxTexCoordIndices[i] = maxCoordIndex;
 		}
 	    }
+	    if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
+		for (int i = 0; i < vertexAttrCount; i++) {
+		    maxVertexAttrIndices[i] = maxCoordIndex;
+		}
+	    }
 	}
 	geomLock.unLock();
 	// bbox is computed for the entries list.
@@ -1616,8 +1691,9 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 	int newCoordMax =0;
 	int newColorIndex=0;
 	int newNormalIndex=0;
-	int newTexCoordIndex[]=null;
-	
+	int[] newTexCoordIndex = null;
+        int[] newVertexAttrIndex = null;
+
 	newCoordMax = computeMaxIndex(initialIndexIndex, validIndexCount, indexCoord);
 	doErrorCheck(newCoordMax);
 	if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) {
@@ -1629,10 +1705,19 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 		newTexCoordIndex = new int[texCoordSetCount];
 		for (int i = 0; i < texCoordSetCount; i++) {
 		   newTexCoordIndex[i] =  computeMaxIndex(initialIndexIndex,validIndexCount,
-							  (int[])indexTexCoord[i]);
+							  indexTexCoord[i]);
 		   doTexCoordCheck(newTexCoordIndex[i], i);
 		}
 	    }
+	    if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
+		newVertexAttrIndex = new int[vertexAttrCount];
+		for (int i = 0; i < vertexAttrCount; i++) {
+		   newVertexAttrIndex[i] = computeMaxIndex(initialIndexIndex,
+                                                           validIndexCount,
+                                                           indexVertexAttr[i]);
+		   doVertexAttrCheck(newVertexAttrIndex[i], i);
+		}
+	    }
 	    if ((vertexFormat & GeometryArray.NORMALS) != 0) {
 		newNormalIndex = computeMaxIndex(initialIndexIndex, validIndexCount, indexNormal);
 		doNormalCheck(newNormalIndex);
@@ -1650,6 +1735,11 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 		    maxTexCoordIndices[i] = newTexCoordIndex[i];
 		}
 	    }
+	    if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
+		for (int i = 0; i < vertexAttrCount; i++) {
+		    maxVertexAttrIndices[i] = newVertexAttrIndex[i];
+		}
+	    }
 	    maxNormalIndex = newNormalIndex;
 	}
 	else {
@@ -1660,6 +1750,11 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 		    maxTexCoordIndices[i] = maxCoordIndex;
 		}
 	    }
+	    if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
+		for (int i = 0; i < vertexAttrCount; i++) {
+		    maxVertexAttrIndices[i] = maxCoordIndex;
+		}
+	    }
 	}
 	geomLock.unLock();
 	// bbox is computed for the entries list.
@@ -1676,6 +1771,7 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
     int getValidIndexCount() {
 	return validIndexCount;
     }
+
     void handleFrequencyChange(int bit) {
 	if ((bit == IndexedGeometryArray.ALLOW_COORDINATE_INDEX_WRITE) ||
 	    (((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) &&
@@ -1684,10 +1780,14 @@ abstract class IndexedGeometryArrayRetained extends GeometryArrayRetained {
 	    (((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0) &&
 	     ((vertexFormat & GeometryArray.NORMALS) != 0) &&
 	     bit == IndexedGeometryArray.ALLOW_NORMAL_INDEX_WRITE) ||
+            (((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0)&&
+             ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0)&&
+             bit == IndexedGeometryArray.ALLOW_VERTEX_ATTR_INDEX_WRITE) ||
 	    (((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) == 0)&&
 	     ((vertexFormat & GeometryArray.TEXTURE_COORDINATE) != 0)&&
 	     bit == IndexedGeometryArray.ALLOW_TEXCOORD_INDEX_WRITE)) {
-	    setFrequencyChangeMask(bit, 0x1);
+
+            setFrequencyChangeMask(bit, 0x1);
 	}
 	else {
 	    super.handleFrequencyChange(bit);
diff --git a/src/classes/share/javax/media/j3d/IndexedGeometryStripArray.java b/src/classes/share/javax/media/j3d/IndexedGeometryStripArray.java
index adce5e3..8f9073d 100644
--- a/src/classes/share/javax/media/j3d/IndexedGeometryStripArray.java
+++ b/src/classes/share/javax/media/j3d/IndexedGeometryStripArray.java
@@ -27,21 +27,19 @@ public abstract class IndexedGeometryStripArray extends IndexedGeometryArray {
      * Constructs an empty IndexedGeometryStripArray object with the specified
      * number of vertices, vertex format, number of indices, and
      * array of per-strip index counts.
-     * @param vertexCount the number of vertex elements in this array
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
-     * @param indexCount the number of indices in this object.  This
-     * count is the maximum number of vertices that will be rendered.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int)}
+     * for a description of this parameter.
+     *
      * @param stripIndexCounts array that specifies
      * the count of the number of indices for each separate strip.
      * The length of this array is the number of separate strips.
@@ -49,7 +47,10 @@ public abstract class IndexedGeometryStripArray extends IndexedGeometryArray {
      * of valid indexed vertices that are rendered (validIndexCount).
      *
      * @exception IllegalArgumentException if
-     * <code>validIndexCount > indexCount</code>
+     * <code>validIndexCount &gt; indexCount</code>
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int)}
+     * for more exceptions that can be thrown
      */
     public IndexedGeometryStripArray(int vertexCount,
 				     int vertexFormat,
@@ -67,56 +68,25 @@ public abstract class IndexedGeometryStripArray extends IndexedGeometryArray {
      * sets, texture coordinate mapping array, number of indices, and
      * array of per-strip index counts.
      *
-     * @param vertexCount the number of vertex elements in this object<p>
-     *
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
-     *
-     * @param texCoordSetCount the number of texture coordinate sets
-     * in this GeometryArray object.  If <code>vertexFormat</code>
-     * does not include one of <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetCount</code> parameter is not used.<p>
-     *
-     * @param texCoordSetMap an array that maps texture coordinate
-     * sets to texture units.  The array is indexed by texture unit
-     * number for each texture unit in the associated Appearance
-     * object.  The values in the array specify the texture coordinate
-     * set within this GeometryArray object that maps to the
-     * corresponding texture
-     * unit.  All elements within the array must be less than
-     * <code>texCoordSetCount</code>.  A negative value specifies that
-     * no texture coordinate set maps to the texture unit
-     * corresponding to the index.  If there are more texture units in
-     * any associated Appearance object than elements in the mapping
-     * array, the extra elements are assumed to be -1.  The same
-     * texture coordinate set may be used for more than one texture
-     * unit.  Each texture unit in every associated Appearance must
-     * have a valid source of texture coordinates: either a
-     * non-negative texture coordinate set must be specified in the
-     * mapping array or texture coordinate generation must be enabled.
-     * Texture coordinate generation will take precedence for those
-     * texture units for which a texture coordinate set is specified
-     * and texture coordinate generation is enabled.  If
-     * <code>vertexFormat</code> does not include one of
-     * <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetMap</code> array is not used.<p>
-     *
-     * @param indexCount the number of indices in this object.  This
-     * count is the maximum number of vertices that will be rendered.<p>
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int)}
+     * for a description of this parameter.
      *
      * @param stripIndexCounts array that specifies
      * the count of the number of indices for each separate strip.
@@ -125,7 +95,10 @@ public abstract class IndexedGeometryStripArray extends IndexedGeometryArray {
      * of valid indexed vertices that are rendered (validIndexCount).
      *
      * @exception IllegalArgumentException if
-     * <code>validIndexCount > indexCount</code>
+     * <code>validIndexCount &gt; indexCount</code>
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for more exceptions that can be thrown
      *
      * @since Java 3D 1.2
      */
@@ -143,6 +116,69 @@ public abstract class IndexedGeometryStripArray extends IndexedGeometryArray {
 	    setStripIndexCounts(stripIndexCounts);
     }
 
+    /**
+     * Constructs an empty IndexedGeometryStripArray object with the
+     * specified number of vertices, vertex format, number of texture
+     * coordinate sets, texture coordinate mapping array, vertex
+     * attribute count, vertex attribute sizes array, number of
+     * indices, and array of per-strip index counts.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrSizes
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int,int[],int)}
+     * for a description of this parameter.
+     *
+     * @param stripIndexCounts array that specifies
+     * the count of the number of indices for each separate strip.
+     * The length of this array is the number of separate strips.
+     * The sum of the elements in this array defines the total number
+     * of valid indexed vertices that are rendered (validIndexCount).
+     *
+     * @exception IllegalArgumentException if
+     * <code>validIndexCount &gt; indexCount</code>
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for more exceptions that can be thrown
+     *
+     * @since Java 3D 1.4
+     */
+    public IndexedGeometryStripArray(int vertexCount,
+				     int vertexFormat,
+				     int texCoordSetCount,
+				     int[] texCoordSetMap,
+				     int vertexAttrCount,
+				     int[] vertexAttrSizes,
+				     int indexCount,
+				     int[] stripIndexCounts) {
+
+	super(vertexCount, vertexFormat,
+	      texCoordSetCount, texCoordSetMap,
+	      vertexAttrCount, vertexAttrSizes,
+	      indexCount);
+
+	((IndexedGeometryStripArrayRetained)this.retained).
+	    setStripIndexCounts(stripIndexCounts);
+    }
+
   /**
    * Get number of strips in the GeometryStripArray
    * @return numStrips number of strips
diff --git a/src/classes/share/javax/media/j3d/IndexedGeometryStripArrayRetained.java b/src/classes/share/javax/media/j3d/IndexedGeometryStripArrayRetained.java
index 0b7958a..7e6c09d 100644
--- a/src/classes/share/javax/media/j3d/IndexedGeometryStripArrayRetained.java
+++ b/src/classes/share/javax/media/j3d/IndexedGeometryStripArrayRetained.java
@@ -32,15 +32,10 @@ abstract class IndexedGeometryStripArrayRetained extends IndexedGeometryArrayRet
    /**
     * Set stripIndexCount data into local array
     */
-   void setStripIndexCounts(int stripIndexCounts[]){
-
+   void setStripIndexCounts(int stripIndexCounts[]) {
 	int i, num = stripIndexCounts.length, total = 0;
 
-	
-
-
-
-	for (i=0; i < num; i++) {
+        for (i=0; i < num; i++) {
 	    total += stripIndexCounts[i];
 	    if (this instanceof IndexedLineStripArrayRetained) {
 		if (stripIndexCounts[i] < 2) {
@@ -65,7 +60,8 @@ abstract class IndexedGeometryStripArrayRetained extends IndexedGeometryArrayRet
 	int newCoordMax =0;
 	int newColorIndex=0;
 	int newNormalIndex=0;
-	int newTexCoordIndex[]=null;
+	int[] newTexCoordIndex = null;
+        int[] newVertexAttrIndex = null;
 	
 	newCoordMax = computeMaxIndex(initialIndexIndex, total, indexCoord);
 	doErrorCheck(newCoordMax);
@@ -78,10 +74,19 @@ abstract class IndexedGeometryStripArrayRetained extends IndexedGeometryArrayRet
 		newTexCoordIndex = new int[texCoordSetCount];
 		for (i = 0; i < texCoordSetCount; i++) {
 		   newTexCoordIndex[i] =  computeMaxIndex(initialIndexIndex,total,
-								  (int[])indexTexCoord[i]);
+								  indexTexCoord[i]);
 		   doTexCoordCheck(newTexCoordIndex[i], i);
 		}
 	    }
+	    if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
+		newVertexAttrIndex = new int[vertexAttrCount];
+		for (i = 0; i < vertexAttrCount; i++) {
+		   newVertexAttrIndex[i] = computeMaxIndex(initialIndexIndex,
+                                                           total,
+                                                           indexVertexAttr[i]);
+		   doTexCoordCheck(newVertexAttrIndex[i], i);
+		}
+	    }
 	    if ((vertexFormat & GeometryArray.NORMALS) != 0) {
 		newNormalIndex = computeMaxIndex(initialIndexIndex, total, indexNormal);
 		doNormalCheck(newNormalIndex);
@@ -103,6 +108,11 @@ abstract class IndexedGeometryStripArrayRetained extends IndexedGeometryArrayRet
 		    maxTexCoordIndices[i] = newTexCoordIndex[i];
 		}
 	    }
+	    if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
+		for (i = 0; i < vertexAttrCount; i++) {
+		    maxVertexAttrIndices[i] = newVertexAttrIndex[i];
+		}
+	    }
 	    maxNormalIndex = newNormalIndex;
 	}
 	else {
@@ -113,6 +123,11 @@ abstract class IndexedGeometryStripArrayRetained extends IndexedGeometryArrayRet
 		    maxTexCoordIndices[i] = maxCoordIndex;
 		}
 	    }
+	    if ((vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
+		for (i = 0; i < vertexAttrCount; i++) {
+		    maxVertexAttrIndices[i] = maxCoordIndex;
+		}
+	    }
 	}
 	geomLock.unLock();
 	// bbox is computed for the entries list.
@@ -123,7 +138,7 @@ abstract class IndexedGeometryStripArrayRetained extends IndexedGeometryArrayRet
 
     }
 
-  Object cloneNonIndexedGeometry() {
+  GeometryArrayRetained cloneNonIndexedGeometry() {
       GeometryStripArrayRetained obj = null;
       int i;
       switch (this.geoType) {
@@ -137,11 +152,14 @@ abstract class IndexedGeometryStripArrayRetained extends IndexedGeometryArrayRet
           obj = new TriangleStripArrayRetained();
           break;
       }
-      obj.createGeometryArrayData(validIndexCount, (vertexFormat & ~(GeometryArray.BY_REFERENCE|GeometryArray.INTERLEAVED|GeometryArray.USE_NIO_BUFFER)),  texCoordSetCount, texCoordSetMap);
+      obj.createGeometryArrayData(validIndexCount,
+              (vertexFormat & ~(GeometryArray.BY_REFERENCE|GeometryArray.INTERLEAVED|GeometryArray.USE_NIO_BUFFER)),
+              texCoordSetCount, texCoordSetMap,
+              vertexAttrCount, vertexAttrSizes);
       obj.unIndexify(this); 
       obj.setStripVertexCounts(stripIndexCounts);
 
-      return (Object)obj;
+      return obj;
     }    
 
 
diff --git a/src/classes/share/javax/media/j3d/IndexedLineArray.java b/src/classes/share/javax/media/j3d/IndexedLineArray.java
index a28e613..28d267a 100644
--- a/src/classes/share/javax/media/j3d/IndexedLineArray.java
+++ b/src/classes/share/javax/media/j3d/IndexedLineArray.java
@@ -25,113 +25,142 @@ public class IndexedLineArray extends IndexedGeometryArray {
     }
 
     /**
-     * Constructs an empty IndexedLineArray object with the specified
-     * number of vertices, vertex format, and number of indices.
-     * @param vertexCount the number of vertex elements in this object
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
-     * @param indexCount the number of indices in this object.  This
-     * count is the maximum number of vertices that will be rendered.
+     * Constructs an empty IndexedLineArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int)}
+     * for a description of this parameter.
+     *
      * @exception IllegalArgumentException if vertexCount is less than 1,
      * or indexCount is less than 2, or indexCount is <i>not</i>
      * a multiple of 2
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int)}
+     * for more exceptions that can be thrown
      */
     public IndexedLineArray(int vertexCount, int vertexFormat, int indexCount) {
 	super(vertexCount,vertexFormat, indexCount);
 
-	if (vertexCount < 1)
-	    throw new IllegalArgumentException(J3dI18N.getString("IndexedLineArray0"));
+        if (vertexCount < 1) 
+	    throw new IllegalArgumentException(J3dI18N.getString("IndexedLineArray0")); 
 
-	if (indexCount < 2 || ((indexCount%2) != 0))
+        if (indexCount < 2 || ((indexCount%2) != 0))
 	    throw new IllegalArgumentException(J3dI18N.getString("IndexedLineArray1"));
     }
 
     /**
-     * Constructs an empty IndexedLineArray object with the specified
-     * number of vertices, vertex format, number of texture coordinate
-     * sets, texture coordinate mapping array, and number of indices.
-     *
-     * @param vertexCount the number of vertex elements in this object<p>
-     *
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
-     *
-     * @param texCoordSetCount the number of texture coordinate sets
-     * in this GeometryArray object.  If <code>vertexFormat</code>
-     * does not include one of <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3 or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetCount</code> parameter is not used.<p>
-     *
-     * @param texCoordSetMap an array that maps texture coordinate
-     * sets to texture units.  The array is indexed by texture unit
-     * number for each texture unit in the associated Appearance
-     * object.  The values in the array specify the texture coordinate
-     * set within this GeometryArray object that maps to the
-     * corresponding texture
-     * unit.  All elements within the array must be less than
-     * <code>texCoordSetCount</code>.  A negative value specifies that
-     * no texture coordinate set maps to the texture unit
-     * corresponding to the index.  If there are more texture units in
-     * any associated Appearance object than elements in the mapping
-     * array, the extra elements are assumed to be -1.  The same
-     * texture coordinate set may be used for more than one texture
-     * unit.  Each texture unit in every associated Appearance must
-     * have a valid source of texture coordinates: either a
-     * non-negative texture coordinate set must be specified in the
-     * mapping array or texture coordinate generation must be enabled.
-     * Texture coordinate generation will take precedence for those
-     * texture units for which a texture coordinate set is specified
-     * and texture coordinate generation is enabled.  If
-     * <code>vertexFormat</code> does not include one of
-     * <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetMap</code> array is not used.<p>
-     *
-     * @param indexCount the number of indices in this object.  This
-     * count is the maximum number of vertices that will be rendered.
+     * Constructs an empty IndexedLineArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int)}
+     * for a description of this parameter.
      *
      * @exception IllegalArgumentException if vertexCount is less than 1,
      * or indexCount is less than 2, or indexCount is <i>not</i>
      * a multiple of 2
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for more exceptions that can be thrown
      *
      * @since Java 3D 1.2
      */
+    public IndexedLineArray(int vertexCount,
+			     int vertexFormat,
+			     int texCoordSetCount,
+			     int[] texCoordSetMap,
+			     int indexCount) {
+
+	super(vertexCount, vertexFormat,
+	      texCoordSetCount, texCoordSetMap,
+	      indexCount);
+
+        if (vertexCount < 1) 
+	    throw new IllegalArgumentException(J3dI18N.getString("IndexedLineArray0")); 
+
+        if (indexCount < 2 || ((indexCount%2) != 0))
+	    throw new IllegalArgumentException(J3dI18N.getString("IndexedLineArray1"));
+    }
+
+    /**
+     * Constructs an empty IndexedLineArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrSizes
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int,int[],int)}
+     * for a description of this parameter.
+     *
+     * @exception IllegalArgumentException if vertexCount is less than 1,
+     * or indexCount is less than 2, or indexCount is <i>not</i>
+     * a multiple of 2
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for more exceptions that can be thrown
+     *
+     * @since Java 3D 1.4
+     */
     public IndexedLineArray(int vertexCount,
 			    int vertexFormat,
 			    int texCoordSetCount,
 			    int[] texCoordSetMap,
+			    int vertexAttrCount,
+			    int[] vertexAttrSizes,
 			    int indexCount) {
 
 	super(vertexCount, vertexFormat,
 	      texCoordSetCount, texCoordSetMap,
+	      vertexAttrCount, vertexAttrSizes,
 	      indexCount);
 
-	if (vertexCount < 1)
-	    throw new IllegalArgumentException(J3dI18N.getString("IndexedLineArray0"));
+        if (vertexCount < 1) 
+	    throw new IllegalArgumentException(J3dI18N.getString("IndexedLineArray0")); 
 
-	if (indexCount < 2 || ((indexCount%2) != 0))
+        if (indexCount < 2 || ((indexCount%2) != 0))
 	    throw new IllegalArgumentException(J3dI18N.getString("IndexedLineArray1"));
     }
 
@@ -148,22 +177,26 @@ public class IndexedLineArray extends IndexedGeometryArray {
      * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
      */
     public NodeComponent cloneNodeComponent() {
-	IndexedLineArrayRetained rt = (IndexedLineArrayRetained) retained;
-	int texSetCount = rt.getTexCoordSetCount();	
-        IndexedLineArray l;
-	if (texSetCount == 0) {
-	    l = new IndexedLineArray(rt.getVertexCount(),
-				     rt.getVertexFormat(),
-				     rt.getIndexCount());
-	} else {
-	    int texMap[] = new int[rt.getTexCoordSetMapLength()];
-	    rt.getTexCoordSetMap(texMap);	    
-	    l = new IndexedLineArray(rt.getVertexCount(),
-				     rt.getVertexFormat(),
-				     texSetCount,
-				     texMap,
-				     rt.getIndexCount());
-	}
+        IndexedLineArrayRetained rt = (IndexedLineArrayRetained) retained;
+        int texSetCount = rt.getTexCoordSetCount();
+        int[] texMap = null;
+        int vertexAttrCount = rt.getVertexAttrCount();
+        int[] vertexAttrSizes = null;
+        if (texSetCount > 0) {
+            texMap = new int[rt.getTexCoordSetMapLength()];
+            rt.getTexCoordSetMap(texMap);
+        }
+        if (vertexAttrCount > 0) {
+            vertexAttrSizes = new int[vertexAttrCount];
+            rt.getVertexAttrSizes(vertexAttrSizes);
+        }
+        IndexedLineArray l = new IndexedLineArray(rt.getVertexCount(),
+                rt.getVertexFormat(),
+                texSetCount,
+                texMap,
+                vertexAttrCount,
+                vertexAttrSizes,
+                rt.getIndexCount());
         l.duplicateNodeComponent(this);
         return l;
     }
diff --git a/src/classes/share/javax/media/j3d/IndexedLineArrayRetained.java b/src/classes/share/javax/media/j3d/IndexedLineArrayRetained.java
index 54b4f14..bb9731f 100644
--- a/src/classes/share/javax/media/j3d/IndexedLineArrayRetained.java
+++ b/src/classes/share/javax/media/j3d/IndexedLineArrayRetained.java
@@ -25,15 +25,16 @@ class IndexedLineArrayRetained extends IndexedGeometryArrayRetained {
     IndexedLineArrayRetained() {
         this.geoType = GEO_TYPE_INDEXED_LINE_SET;
     }
-
-    boolean intersect(PickShape pickShape, double dist[],  Point3d iPnt) {
-	Point3d pnts[] = new Point3d[2];
+    
+    boolean intersect(PickShape pickShape, PickInfo.IntersectionInfo iInfo,  int flags, Point3d iPnt) {
+        Point3d pnts[] = new Point3d[2];
 	double sdist[] = new double[1];
 	double minDist = Double.MAX_VALUE;
 	double x = 0, y = 0, z = 0;
-	int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
+        int count = 0;
+        int minICount = 0; 
+        int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
 		 initialVertexIndex : initialCoordIndex);
-
 	pnts[0] = new Point3d();
 	pnts[1] = new Point3d();
     
@@ -44,15 +45,16 @@ class IndexedLineArrayRetained extends IndexedGeometryArrayRetained {
 	    while (i < validVertexCount) {
 		getVertexData(indexCoord[i++], pnts[0]);
 		getVertexData(indexCoord[i++], pnts[1]);
-
+                count += 2;
 		if (intersectLineAndRay(pnts[0], pnts[1], pickRay.origin,
 					pickRay.direction, sdist,
 					iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -70,20 +72,21 @@ class IndexedLineArrayRetained extends IndexedGeometryArrayRetained {
 	    while (i < validVertexCount) {
 		getVertexData(indexCoord[i++], pnts[0]);
 		getVertexData(indexCoord[i++], pnts[1]);
+                count += 2;
 		if (intersectLineAndRay(pnts[0], pnts[1],
 					pickSegment.start, 
 					dir, sdist, iPnt) &&
 		    (sdist[0] <= 1.0)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
 		    }
-
 		}
 	    }
 	    break;
@@ -94,12 +97,14 @@ class IndexedLineArrayRetained extends IndexedGeometryArrayRetained {
 	    while (i < validVertexCount) {
 		getVertexData(indexCoord[i++], pnts[0]);
 		getVertexData(indexCoord[i++], pnts[1]);
+                count += 2;
 		if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -115,12 +120,14 @@ class IndexedLineArrayRetained extends IndexedGeometryArrayRetained {
 	    while (i < validVertexCount) {
 		getVertexData(indexCoord[i++], pnts[0]);
 		getVertexData(indexCoord[i++], pnts[1]);
+                count += 2;
 		if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -135,12 +142,14 @@ class IndexedLineArrayRetained extends IndexedGeometryArrayRetained {
 	    while (i < validVertexCount) {
 		getVertexData(indexCoord[i++], pnts[0]);
 		getVertexData(indexCoord[i++], pnts[1]);
+                count += 2;
 		if (intersectBoundingPolytope(pnts, bpolytope, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -154,12 +163,14 @@ class IndexedLineArrayRetained extends IndexedGeometryArrayRetained {
 	    while (i < validVertexCount) {
 		getVertexData(indexCoord[i++], pnts[0]);
 		getVertexData(indexCoord[i++], pnts[1]);
+                count += 2;
 		if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -173,12 +184,14 @@ class IndexedLineArrayRetained extends IndexedGeometryArrayRetained {
 	    while (i < validVertexCount) {
 		getVertexData(indexCoord[i++], pnts[0]);
 		getVertexData(indexCoord[i++], pnts[1]);
+                count += 2;
 		if (intersectCone(pnts, pickCone, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -194,15 +207,22 @@ class IndexedLineArrayRetained extends IndexedGeometryArrayRetained {
 	} 
 
 	if (minDist < Double.MAX_VALUE) {
-	    dist[0] = minDist;
+            assert(minICount >=2);
+            int[] vertexIndices = iInfo.getVertexIndices();
+            if (vertexIndices == null) {
+                vertexIndices = new int[2];
+                iInfo.setVertexIndices(vertexIndices);
+            }
+            vertexIndices[0] = minICount - 2;
+            vertexIndices[1] = minICount - 1;
 	    iPnt.x = x;
 	    iPnt.y = y;
 	    iPnt.z = z;
-	    return true;
+ 	    return true;
 	}
 	return false;
    
-    }
+    }    
   
     boolean intersect(Point3d[] pnts) {
 	Point3d[] points = new Point3d[2];
diff --git a/src/classes/share/javax/media/j3d/IndexedLineStripArray.java b/src/classes/share/javax/media/j3d/IndexedLineStripArray.java
index 6a32086..34dbf3b 100644
--- a/src/classes/share/javax/media/j3d/IndexedLineStripArray.java
+++ b/src/classes/share/javax/media/j3d/IndexedLineStripArray.java
@@ -30,35 +30,36 @@ public class IndexedLineStripArray extends IndexedGeometryStripArray {
     }
 
     /**
-     * Constructs an empty IndexedLineStripArray object with the specified
-     * number of vertices, vertex format, number of indices, and
-     * array of per-strip index counts.
-     * @param vertexCount the number of vertex elements in this object
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
-     * @param indexCount the number of indices in this object.  This
-     * count is the maximum number of vertices that will be rendered.
-     * @param stripIndexCounts array that specifies
-     * the count of the number of indices for each separate strip.
-     * The length of this array is the number of separate strips.
+     * Constructs an empty IndexedLineStripArray object using the
+     * specified parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int)}
+     * for a description of this parameter.
+     *
+     * @param stripIndexCounts
+     * see {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
      * @exception IllegalArgumentException if vertexCount is less than 1,
      * or indexCount is less than 2,
      * or any element in the stripIndexCounts array is less than 2
+     * ;<br>
+     * See {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[])}
+     * for more exceptions that can be thrown
      */
     public IndexedLineStripArray(int vertexCount,
-			int vertexFormat,
-			int indexCount,
-			int stripIndexCounts[]) {
+				     int vertexFormat,
+				     int indexCount,
+				     int[] stripIndexCounts) {
 
 	super(vertexCount, vertexFormat, indexCount, stripIndexCounts);
 
@@ -70,81 +71,113 @@ public class IndexedLineStripArray extends IndexedGeometryStripArray {
     }
 
     /**
-     * Constructs an empty IndexedLineStripArray object with the specified
-     * number of vertices, vertex format, number of texture coordinate
-     * sets, texture coordinate mapping array, number of indices, and
-     * array of per-strip index counts.
-     *
-     * @param vertexCount the number of vertex elements in this object<p>
-     *
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
-     *
-     * @param texCoordSetCount the number of texture coordinate sets
-     * in this GeometryArray object.  If <code>vertexFormat</code>
-     * does not include one of <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetCount</code> parameter is not used.<p>
-     *
-     * @param texCoordSetMap an array that maps texture coordinate
-     * sets to texture units.  The array is indexed by texture unit
-     * number for each texture unit in the associated Appearance
-     * object.  The values in the array specify the texture coordinate
-     * set within this GeometryArray object that maps to the
-     * corresponding texture
-     * unit.  All elements within the array must be less than
-     * <code>texCoordSetCount</code>.  A negative value specifies that
-     * no texture coordinate set maps to the texture unit
-     * corresponding to the index.  If there are more texture units in
-     * any associated Appearance object than elements in the mapping
-     * array, the extra elements are assumed to be -1.  The same
-     * texture coordinate set may be used for more than one texture
-     * unit.  Each texture unit in every associated Appearance must
-     * have a valid source of texture coordinates: either a
-     * non-negative texture coordinate set must be specified in the
-     * mapping array or texture coordinate generation must be enabled.
-     * Texture coordinate generation will take precedence for those
-     * texture units for which a texture coordinate set is specified
-     * and texture coordinate generation is enabled.  If
-     * <code>vertexFormat</code> does not include one of
-     * <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetMap</code> array is not used.<p>
-     *
-     * @param indexCount the number of indices in this object.  This
-     * count is the maximum number of vertices that will be rendered.<p>
-     *
-     * @param stripIndexCounts array that specifies
-     * the count of the number of indices for each separate strip.
-     * The length of this array is the number of separate strips.
+     * Constructs an empty IndexedLineStripArray object using the
+     * specified parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int)}
+     * for a description of this parameter.
+     *
+     * @param stripIndexCounts
+     * see {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
      *
      * @exception IllegalArgumentException if vertexCount is less than 1,
      * or indexCount is less than 2,
      * or any element in the stripIndexCounts array is less than 2
+     * ;<br>
+     * See {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[])}
+     * for more exceptions that can be thrown
      *
      * @since Java 3D 1.2
      */
     public IndexedLineStripArray(int vertexCount,
-				 int vertexFormat,
-				 int texCoordSetCount,
-				 int[] texCoordSetMap,
-				 int indexCount,
-				 int stripIndexCounts[]) {
+				     int vertexFormat,
+				     int texCoordSetCount,
+				     int[] texCoordSetMap,
+				     int indexCount,
+				     int[] stripIndexCounts) {
+
+	super(vertexCount, vertexFormat,
+	      texCoordSetCount, texCoordSetMap,
+	      indexCount, stripIndexCounts);
+
+        if (vertexCount < 1) 
+	    throw new IllegalArgumentException(J3dI18N.getString("IndexedLineStripArray0")); 
+
+        if (indexCount < 2 )
+	    throw new IllegalArgumentException(J3dI18N.getString("IndexedLineStripArray1"));
+    }
+
+    /**
+     * Constructs an empty IndexedLineStripArray object using the
+     * specified parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrSizes
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int,int[],int)}
+     * for a description of this parameter.
+     *
+     * @param stripIndexCounts
+     * see {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @exception IllegalArgumentException if vertexCount is less than 1,
+     * or indexCount is less than 2,
+     * or any element in the stripIndexCounts array is less than 2
+     * ;<br>
+     * See {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[],int,int[])}
+     * for more exceptions that can be thrown
+     *
+     * @since Java 3D 1.4
+     */
+    public IndexedLineStripArray(int vertexCount,
+				     int vertexFormat,
+				     int texCoordSetCount,
+				     int[] texCoordSetMap,
+				     int vertexAttrCount,
+				     int[] vertexAttrSizes,
+				     int indexCount,
+				     int[] stripIndexCounts) {
 
 	super(vertexCount, vertexFormat,
 	      texCoordSetCount, texCoordSetMap,
+	      vertexAttrCount, vertexAttrSizes,
 	      indexCount, stripIndexCounts);
 
         if (vertexCount < 1) 
@@ -168,29 +201,30 @@ public class IndexedLineStripArray extends IndexedGeometryStripArray {
      * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
      */
     public NodeComponent cloneNodeComponent() {
-	IndexedLineStripArrayRetained rt =
-	    (IndexedLineStripArrayRetained) retained;
+        IndexedLineStripArrayRetained rt =
+                (IndexedLineStripArrayRetained) retained;
         int stripIndexCounts[] = new int[rt.getNumStrips()];
-	rt.getStripIndexCounts(stripIndexCounts);
-	int texSetCount = rt.getTexCoordSetCount();
-        IndexedLineStripArray l; 
-
-	if (texSetCount == 0) {
-	    l = new IndexedLineStripArray(rt.getVertexCount(),
-					  rt.getVertexFormat(),
-					  rt.getIndexCount(),
-					  stripIndexCounts);
-	} else {
-	    int texMap[] = new int[rt.getTexCoordSetMapLength()];
-	    rt.getTexCoordSetMap(texMap);
-	    l = new IndexedLineStripArray(rt.getVertexCount(),
-					  rt.getVertexFormat(),
-					  texSetCount,
-					  texMap,
-					  rt.getIndexCount(),
-					  stripIndexCounts);
-	    
-	}
+        rt.getStripIndexCounts(stripIndexCounts);
+        int texSetCount = rt.getTexCoordSetCount();
+        int[] texMap = null;
+        int vertexAttrCount = rt.getVertexAttrCount();
+        int[] vertexAttrSizes = null;
+        if (texSetCount > 0) {
+            texMap = new int[rt.getTexCoordSetMapLength()];
+            rt.getTexCoordSetMap(texMap);
+        }
+        if (vertexAttrCount > 0) {
+            vertexAttrSizes = new int[vertexAttrCount];
+            rt.getVertexAttrSizes(vertexAttrSizes);
+        }
+        IndexedLineStripArray l = new IndexedLineStripArray(rt.getVertexCount(),
+                rt.getVertexFormat(),
+                texSetCount,
+                texMap,
+                vertexAttrCount,
+                vertexAttrSizes,
+                rt.getIndexCount(),
+                stripIndexCounts);
         l.duplicateNodeComponent(this);
         return l;
     }
diff --git a/src/classes/share/javax/media/j3d/IndexedLineStripArrayRetained.java b/src/classes/share/javax/media/j3d/IndexedLineStripArrayRetained.java
index 7a91bd5..8791b9f 100644
--- a/src/classes/share/javax/media/j3d/IndexedLineStripArrayRetained.java
+++ b/src/classes/share/javax/media/j3d/IndexedLineStripArrayRetained.java
@@ -29,14 +29,14 @@ class IndexedLineStripArrayRetained extends IndexedGeometryStripArrayRetained {
         geoType = GEO_TYPE_INDEXED_LINE_STRIP_SET;
     }
 
-
-    boolean intersect(PickShape pickShape, double dist[],  Point3d iPnt) {
+    boolean intersect(PickShape pickShape, PickInfo.IntersectionInfo iInfo,  int flags, Point3d iPnt) {
 	Point3d pnts[] = new Point3d[2];
 	double sdist[] = new double[1];
 	double minDist = Double.MAX_VALUE;
 	double x = 0, y = 0, z = 0;
 	int scount, j, i = 0;
 	int count = 0;
+        int minICount = 0; 
 
 	pnts[0] = new Point3d();
 	pnts[1] = new Point3d();
@@ -54,14 +54,15 @@ class IndexedLineStripArrayRetained extends IndexedGeometryStripArrayRetained {
 		    if (intersectLineAndRay(pnts[0], pnts[1], pickRay.origin,
 					    pickRay.direction, sdist,
 					    iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);
@@ -85,14 +86,15 @@ class IndexedLineStripArrayRetained extends IndexedGeometryStripArrayRetained {
 					    pickSegment.start, 
 					    dir, sdist, iPnt) &&
 			(sdist[0] <= 1.0)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);			
@@ -111,14 +113,15 @@ class IndexedLineStripArrayRetained extends IndexedGeometryStripArrayRetained {
 		    getVertexData(indexCoord[count++], pnts[1]);
 
 		    if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);			   
@@ -138,14 +141,15 @@ class IndexedLineStripArrayRetained extends IndexedGeometryStripArrayRetained {
 		    getVertexData(indexCoord[count++], pnts[1]);
 
 		    if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);
@@ -164,14 +168,15 @@ class IndexedLineStripArrayRetained extends IndexedGeometryStripArrayRetained {
 		    getVertexData(indexCoord[count++], pnts[1]);
 
 		    if (intersectBoundingPolytope(pnts, bpolytope, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);
@@ -189,14 +194,15 @@ class IndexedLineStripArrayRetained extends IndexedGeometryStripArrayRetained {
 		    getVertexData(indexCoord[count++], pnts[1]);
 
 		    if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);
@@ -214,14 +220,15 @@ class IndexedLineStripArrayRetained extends IndexedGeometryStripArrayRetained {
 		    getVertexData(indexCoord[count++], pnts[1]);
 
 		    if (intersectCone(pnts, pickCone, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);
@@ -236,14 +243,20 @@ class IndexedLineStripArrayRetained extends IndexedGeometryStripArrayRetained {
 	} 
 
 	if (minDist < Double.MAX_VALUE) {
-	    dist[0] = minDist;
+            assert(minICount >=2);
+            int[] vertexIndices = iInfo.getVertexIndices();
+            if (vertexIndices == null) {
+                vertexIndices = new int[2];
+                iInfo.setVertexIndices(vertexIndices);
+            }
+            vertexIndices[0] = minICount - 2;
+            vertexIndices[1] = minICount - 1;
 	    iPnt.x = x;
 	    iPnt.y = y;
 	    iPnt.z = z;
 	    return true;
 	}
 	return false;
-   
     }
       
     // intersect pnts[] with every triangle in this object
diff --git a/src/classes/share/javax/media/j3d/IndexedPointArray.java b/src/classes/share/javax/media/j3d/IndexedPointArray.java
index c780d34..7f01bd2 100644
--- a/src/classes/share/javax/media/j3d/IndexedPointArray.java
+++ b/src/classes/share/javax/media/j3d/IndexedPointArray.java
@@ -26,25 +26,26 @@ public class IndexedPointArray extends IndexedGeometryArray {
     }
 
     /**
-     * Constructs an empty IndexedPointArray object with the specified
-     * number of vertices, vertex format, and number of indices.
-     * @param vertexCount the number of vertex elements in this object
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
-     * @param indexCount the number of indices in this object.  This
-     * count is the maximum number of vertices that will be rendered.
+     * Constructs an empty IndexedPointArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int)}
+     * for a description of this parameter.
+     *
      * @exception IllegalArgumentException if vertexCount is less than 1
      * or indexCount is less than 1
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int)}
+     * for more exceptions that can be thrown
      */
     public IndexedPointArray(int vertexCount, int vertexFormat, int indexCount) {
 	super(vertexCount,vertexFormat, indexCount);
@@ -57,63 +58,34 @@ public class IndexedPointArray extends IndexedGeometryArray {
     }
 
     /**
-     * Constructs an empty IndexedPointArray object with the specified
-     * number of vertices, vertex format, number of texture coordinate
-     * sets, texture coordinate mapping array, and number of indices.
-     *
-     * @param vertexCount the number of vertex elements in this object<p>
-     *
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
-     *
-     * @param texCoordSetCount the number of texture coordinate sets
-     * in this GeometryArray object.  If <code>vertexFormat</code>
-     * does not include one of <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3 or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetCount</code> parameter is not used.<p>
-     *
-     * @param texCoordSetMap an array that maps texture coordinate
-     * sets to texture units.  The array is indexed by texture unit
-     * number for each texture unit in the associated Appearance
-     * object.  The values in the array specify the texture coordinate
-     * set within this GeometryArray object that maps to the
-     * corresponding texture
-     * unit.  All elements within the array must be less than
-     * <code>texCoordSetCount</code>.  A negative value specifies that
-     * no texture coordinate set maps to the texture unit
-     * corresponding to the index.  If there are more texture units in
-     * any associated Appearance object than elements in the mapping
-     * array, the extra elements are assumed to be -1.  The same
-     * texture coordinate set may be used for more than one texture
-     * unit.  Each texture unit in every associated Appearance must
-     * have a valid source of texture coordinates: either a
-     * non-negative texture coordinate set must be specified in the
-     * mapping array or texture coordinate generation must be enabled.
-     * Texture coordinate generation will take precedence for those
-     * texture units for which a texture coordinate set is specified
-     * and texture coordinate generation is enabled.  If
-     * <code>vertexFormat</code> does not include one of
-     * <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetMap</code> array is not used.<p>
-     *
-     * @param indexCount the number of indices in this object.  This
-     * count is the maximum number of vertices that will be rendered.
+     * Constructs an empty IndexedPointArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int)}
+     * for a description of this parameter.
      *
      * @exception IllegalArgumentException if vertexCount is less than 1
      * or indexCount is less than 1
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for more exceptions that can be thrown
      *
      * @since Java 3D 1.2
      */
@@ -134,6 +106,62 @@ public class IndexedPointArray extends IndexedGeometryArray {
 	    throw new IllegalArgumentException(J3dI18N.getString("IndexedPointArray1"));
     }
 
+    /**
+     * Constructs an empty IndexedPointArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrSizes
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int,int[],int)}
+     * for a description of this parameter.
+     *
+     * @exception IllegalArgumentException if vertexCount is less than 1
+     * or indexCount is less than 1
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for more exceptions that can be thrown
+     *
+     * @since Java 3D 1.4
+     */
+    public IndexedPointArray(int vertexCount,
+			     int vertexFormat,
+			     int texCoordSetCount,
+			     int[] texCoordSetMap,
+			     int vertexAttrCount,
+			     int[] vertexAttrSizes,
+			     int indexCount) {
+
+	super(vertexCount, vertexFormat,
+	      texCoordSetCount, texCoordSetMap,
+	      vertexAttrCount, vertexAttrSizes,
+	      indexCount);
+
+        if (vertexCount < 1) 
+	    throw new IllegalArgumentException(J3dI18N.getString("IndexedPointArray0")); 
+
+        if (indexCount < 1 )
+	    throw new IllegalArgumentException(J3dI18N.getString("IndexedPointArray1"));
+    }
+
     /**
      * Creates the retained mode IndexedPointArrayRetained object that this
      * IndexedPointArray object will point to.
@@ -148,22 +176,26 @@ public class IndexedPointArray extends IndexedGeometryArray {
      * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
      */
     public NodeComponent cloneNodeComponent() {
-	IndexedPointArrayRetained rt = (IndexedPointArrayRetained) retained;
-	int texSetCount = rt.getTexCoordSetCount();	
-        IndexedPointArray p;
-	if (texSetCount == 0) {
-	    p = new IndexedPointArray(rt.getVertexCount(),
-				      rt.getVertexFormat(),
-				      rt.getIndexCount());
-	} else {
-	    int texMap[] = new int[rt.getTexCoordSetMapLength()];
-	    rt.getTexCoordSetMap(texMap);	    
-	    p = new IndexedPointArray(rt.getVertexCount(),
-				      rt.getVertexFormat(),
-				      texSetCount,
-				      texMap,
-				      rt.getIndexCount());
-	}
+        IndexedPointArrayRetained rt = (IndexedPointArrayRetained) retained;
+        int texSetCount = rt.getTexCoordSetCount();
+        int[] texMap = null;
+        int vertexAttrCount = rt.getVertexAttrCount();
+        int[] vertexAttrSizes = null;
+        if (texSetCount > 0) {
+            texMap = new int[rt.getTexCoordSetMapLength()];
+            rt.getTexCoordSetMap(texMap);
+        }
+        if (vertexAttrCount > 0) {
+            vertexAttrSizes = new int[vertexAttrCount];
+            rt.getVertexAttrSizes(vertexAttrSizes);
+        }
+        IndexedPointArray p = new IndexedPointArray(rt.getVertexCount(),
+                rt.getVertexFormat(),
+                texSetCount,
+                texMap,
+                vertexAttrCount,
+                vertexAttrSizes,
+                rt.getIndexCount());
         p.duplicateNodeComponent(this);
         return p;
     }
diff --git a/src/classes/share/javax/media/j3d/IndexedPointArrayRetained.java b/src/classes/share/javax/media/j3d/IndexedPointArrayRetained.java
index 8c63a73..f7289c5 100644
--- a/src/classes/share/javax/media/j3d/IndexedPointArrayRetained.java
+++ b/src/classes/share/javax/media/j3d/IndexedPointArrayRetained.java
@@ -25,10 +25,12 @@ class IndexedPointArrayRetained extends IndexedGeometryArrayRetained {
         this.geoType = GEO_TYPE_INDEXED_POINT_SET;
     } 
 
-    boolean intersect(PickShape pickShape, double dist[],  Point3d iPnt) {
-	double sdist[] = new double[1];
+    boolean intersect(PickShape pickShape, PickInfo.IntersectionInfo iInfo,  int flags, Point3d iPnt) {
+ 	double sdist[] = new double[1];
 	double minDist = Double.MAX_VALUE;
 	double x = 0, y = 0, z = 0;
+        int count = 0;
+        int minICount = 0; 
 	Point3d pnt = new Point3d();
 	int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
 		 initialVertexIndex : initialCoordIndex);
@@ -39,13 +41,15 @@ class IndexedPointArrayRetained extends IndexedGeometryArrayRetained {
 
 	    while (i < validVertexCount) {
 		getVertexData(indexCoord[i++], pnt);
+                count++;
 		if (intersectPntAndRay(pnt, pickRay.origin,
 				       pickRay.direction, sdist)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = pnt.x;
 			y = pnt.y;
 			z = pnt.z;
@@ -62,19 +66,20 @@ class IndexedPointArrayRetained extends IndexedGeometryArrayRetained {
 	    
 	    while (i < validVertexCount) {
 		getVertexData(indexCoord[i++], pnt);
+                count++;
 		if (intersectPntAndRay(pnt, pickSegment.start, 
 					dir, sdist) &&
 		    (sdist[0] <= 1.0)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = pnt.x;
 			y = pnt.y;
 			z = pnt.z;
 		    }
-
 		}
 	    }
 	    break;
@@ -85,33 +90,35 @@ class IndexedPointArrayRetained extends IndexedGeometryArrayRetained {
 
 	    while (i < validVertexCount) {
 		getVertexData(indexCoord[i++], pnt);
+                count++;
 		if (bounds.intersect(pnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    sdist[0] = pickShape.distance(pnt);
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = pnt.x;
 			y = pnt.y;
 			z = pnt.z;
 		    }
 		}
 	    }
-
 	    break;
 	case PickShape.PICKCYLINDER:
 	    PickCylinder pickCylinder= (PickCylinder) pickShape;
 
 	    while (i < validVertexCount) {
 		getVertexData(indexCoord[i++], pnt);
-
+                count++;
 		if (intersectCylinder(pnt, pickCylinder, sdist)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = pnt.x;
 			y = pnt.y;
 			z = pnt.z;
@@ -124,13 +131,14 @@ class IndexedPointArrayRetained extends IndexedGeometryArrayRetained {
 
 	    while (i < validVertexCount) {
 		getVertexData(indexCoord[i++], pnt);
-
+                count++;
 		if (intersectCone(pnt, pickCone, sdist)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = pnt.x;
 			y = pnt.y;
 			z = pnt.z;
@@ -146,7 +154,13 @@ class IndexedPointArrayRetained extends IndexedGeometryArrayRetained {
 	} 
 
 	if (minDist < Double.MAX_VALUE) {
-	    dist[0] = minDist;
+            assert(minICount >=1);
+            int[] vertexIndices = iInfo.getVertexIndices();
+            if (vertexIndices == null) {
+                vertexIndices = new int[1];
+                iInfo.setVertexIndices(vertexIndices);
+            }
+            vertexIndices[0] = minICount - 1;
 	    iPnt.x = x;
 	    iPnt.y = y;
 	    iPnt.z = z;
diff --git a/src/classes/share/javax/media/j3d/IndexedQuadArray.java b/src/classes/share/javax/media/j3d/IndexedQuadArray.java
index 4368340..1c94c4b 100644
--- a/src/classes/share/javax/media/j3d/IndexedQuadArray.java
+++ b/src/classes/share/javax/media/j3d/IndexedQuadArray.java
@@ -27,26 +27,27 @@ public class IndexedQuadArray extends IndexedGeometryArray {
     }
 
     /**
-     * Constructs an empty IndexedQuadArray object with the specified
-     * number of vertices, vertex format, and number of indices.
-     * @param vertexCount the number of vertex elements in this object
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
-     * @param indexCount the number of indices in this object.  This
-     * count is the maximum number of vertices that will be rendered.
+     * Constructs an empty IndexedQuadArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int)}
+     * for a description of this parameter.
+     *
      * @exception IllegalArgumentException if vertexCount is less than 1,
      * or indexCount is less than 4, or indexCount is <i>not</i>
      * a multiple of 4
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int)}
+     * for more exceptions that can be thrown
      */
     public IndexedQuadArray(int vertexCount, int vertexFormat, int indexCount) {
 	super(vertexCount,vertexFormat, indexCount);
@@ -59,64 +60,35 @@ public class IndexedQuadArray extends IndexedGeometryArray {
     }
 
     /**
-     * Constructs an empty IndexedQuadArray object with the specified
-     * number of vertices, vertex format, number of texture coordinate
-     * sets, texture coordinate mapping array, and number of indices.
-     *
-     * @param vertexCount the number of vertex elements in this object<p>
-     *
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
-     *
-     * @param texCoordSetCount the number of texture coordinate sets
-     * in this GeometryArray object.  If <code>vertexFormat</code>
-     * does not include one of <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetCount</code> parameter is not used.<p>
-     *
-     * @param texCoordSetMap an array that maps texture coordinate
-     * sets to texture units.  The array is indexed by texture unit
-     * number for each texture unit in the associated Appearance
-     * object.  The values in the array specify the texture coordinate
-     * set within this GeometryArray object that maps to the
-     * corresponding texture
-     * unit.  All elements within the array must be less than
-     * <code>texCoordSetCount</code>.  A negative value specifies that
-     * no texture coordinate set maps to the texture unit
-     * corresponding to the index.  If there are more texture units in
-     * any associated Appearance object than elements in the mapping
-     * array, the extra elements are assumed to be -1.  The same
-     * texture coordinate set may be used for more than one texture
-     * unit.  Each texture unit in every associated Appearance must
-     * have a valid source of texture coordinates: either a
-     * non-negative texture coordinate set must be specified in the
-     * mapping array or texture coordinate generation must be enabled.
-     * Texture coordinate generation will take precedence for those
-     * texture units for which a texture coordinate set is specified
-     * and texture coordinate generation is enabled.  If
-     * <code>vertexFormat</code> does not include one of
-     * <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetMap</code> array is not used.<p>
-     *
-     * @param indexCount the number of indices in this object.  This
-     * count is the maximum number of vertices that will be rendered.
+     * Constructs an empty IndexedQuadArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int)}
+     * for a description of this parameter.
      *
      * @exception IllegalArgumentException if vertexCount is less than 1,
      * or indexCount is less than 4, or indexCount is <i>not</i>
      * a multiple of 4
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for more exceptions that can be thrown
      *
      * @since Java 3D 1.2
      */
@@ -137,6 +109,63 @@ public class IndexedQuadArray extends IndexedGeometryArray {
 	    throw new IllegalArgumentException(J3dI18N.getString("IndexedQuadArray1"));
     }
 
+    /**
+     * Constructs an empty IndexedQuadArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrSizes
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int,int[],int)}
+     * for a description of this parameter.
+     *
+     * @exception IllegalArgumentException if vertexCount is less than 1,
+     * or indexCount is less than 4, or indexCount is <i>not</i>
+     * a multiple of 4
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for more exceptions that can be thrown
+     *
+     * @since Java 3D 1.4
+     */
+    public IndexedQuadArray(int vertexCount,
+			    int vertexFormat,
+			    int texCoordSetCount,
+			    int[] texCoordSetMap,
+			    int vertexAttrCount,
+			    int[] vertexAttrSizes,
+			    int indexCount) {
+
+	super(vertexCount, vertexFormat,
+	      texCoordSetCount, texCoordSetMap,
+	      vertexAttrCount, vertexAttrSizes,
+	      indexCount);
+
+        if (vertexCount < 1) 
+	    throw new IllegalArgumentException(J3dI18N.getString("IndexedQuadArray0")); 
+
+        if (indexCount < 4 || ((indexCount%4) != 0))
+	    throw new IllegalArgumentException(J3dI18N.getString("IndexedQuadArray1"));
+    }
+
     /**
      * Creates the retained mode IndexedQuadArrayRetained object that this
      * IndexedQuadArray object will point to.
@@ -151,23 +180,26 @@ public class IndexedQuadArray extends IndexedGeometryArray {
      * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
      */
     public NodeComponent cloneNodeComponent() {
-	IndexedQuadArrayRetained rt = (IndexedQuadArrayRetained) retained;
-	int texSetCount = rt.getTexCoordSetCount();	
-        IndexedQuadArray q;
-
-	if (texSetCount == 0) {
-	    q = new IndexedQuadArray(rt.getVertexCount(),
-				     rt.getVertexFormat(),
-				     rt.getIndexCount());
-	} else {
-	    int texMap[] = new int[rt.getTexCoordSetMapLength()];
-	    rt.getTexCoordSetMap(texMap);	    
-	    q = new IndexedQuadArray(rt.getVertexCount(),
-				     rt.getVertexFormat(),
-				     texSetCount,
-				     texMap,
-				     rt.getIndexCount());
-	}
+        IndexedQuadArrayRetained rt = (IndexedQuadArrayRetained) retained;
+        int texSetCount = rt.getTexCoordSetCount();
+        int[] texMap = null;
+        int vertexAttrCount = rt.getVertexAttrCount();
+        int[] vertexAttrSizes = null;
+        if (texSetCount > 0) {
+            texMap = new int[rt.getTexCoordSetMapLength()];
+            rt.getTexCoordSetMap(texMap);
+        }
+        if (vertexAttrCount > 0) {
+            vertexAttrSizes = new int[vertexAttrCount];
+            rt.getVertexAttrSizes(vertexAttrSizes);
+        }
+        IndexedQuadArray q = new IndexedQuadArray(rt.getVertexCount(),
+                rt.getVertexFormat(),
+                texSetCount,
+                texMap,
+                vertexAttrCount,
+                vertexAttrSizes,
+                rt.getIndexCount());
         q.duplicateNodeComponent(this);
         return q;
     }
diff --git a/src/classes/share/javax/media/j3d/IndexedQuadArrayRetained.java b/src/classes/share/javax/media/j3d/IndexedQuadArrayRetained.java
index f06c62d..355f471 100644
--- a/src/classes/share/javax/media/j3d/IndexedQuadArrayRetained.java
+++ b/src/classes/share/javax/media/j3d/IndexedQuadArrayRetained.java
@@ -27,15 +27,15 @@ class IndexedQuadArrayRetained extends IndexedGeometryArrayRetained {
 	this.geoType = GEO_TYPE_INDEXED_QUAD_SET;
     }
 
-
-    boolean intersect(PickShape pickShape, double dist[],  Point3d iPnt) {
+    boolean intersect(PickShape pickShape, PickInfo.IntersectionInfo iInfo,  int flags, Point3d iPnt) {
 	Point3d pnts[] = new Point3d[4];
 	double sdist[] = new double[1];
 	double minDist = Double.MAX_VALUE;
 	double x = 0, y = 0, z = 0;
+        int count = 0;
+        int minICount = 0; 
 	int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
-		 initialVertexIndex : initialCoordIndex);
-
+		 initialVertexIndex : initialCoordIndex);        
 	pnts[0] = new Point3d();
 	pnts[1] = new Point3d();
 	pnts[2] = new Point3d();
@@ -50,12 +50,14 @@ class IndexedQuadArrayRetained extends IndexedGeometryArrayRetained {
 		getVertexData(indexCoord[i++], pnts[1]);
 		getVertexData(indexCoord[i++], pnts[2]);
 		getVertexData(indexCoord[i++], pnts[3]);
+                count += 4;
 		if (intersectRay(pnts, pickRay, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -71,18 +73,19 @@ class IndexedQuadArrayRetained extends IndexedGeometryArrayRetained {
 		getVertexData(indexCoord[i++], pnts[1]);
 		getVertexData(indexCoord[i++], pnts[2]);
 		getVertexData(indexCoord[i++], pnts[3]);
+                count += 4;
 		if (intersectSegment(pnts, pickSegment.start,
 				     pickSegment.end, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
-		    }
-
+                    }
 		}
 	    }
 	    break;
@@ -94,108 +97,108 @@ class IndexedQuadArrayRetained extends IndexedGeometryArrayRetained {
 		getVertexData(indexCoord[i++], pnts[1]);
 		getVertexData(indexCoord[i++], pnts[2]);
 		getVertexData(indexCoord[i++], pnts[3]);
-
+                count += 4;
 		if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
-		    }
+                    }
 		}
 	    }
-
 	    break;
 	case PickShape.PICKBOUNDINGSPHERE:
 	    BoundingSphere bsphere = (BoundingSphere) 
 		                     ((PickBounds) pickShape).bounds;
-
 	    while (i < validVertexCount) {
 		getVertexData(indexCoord[i++], pnts[0]);
 		getVertexData(indexCoord[i++], pnts[1]);
 		getVertexData(indexCoord[i++], pnts[2]);
 		getVertexData(indexCoord[i++], pnts[3]);
-
+                count += 4;
 		if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
-		    }
+                    }
 		}
 	    }
 	    break;
 	case PickShape.PICKBOUNDINGPOLYTOPE:
-
 	    BoundingPolytope bpolytope = (BoundingPolytope) 
 		                      ((PickBounds) pickShape).bounds;
-
 	    while (i < validVertexCount) {
 		getVertexData(indexCoord[i++], pnts[0]);
 		getVertexData(indexCoord[i++], pnts[1]);
 		getVertexData(indexCoord[i++], pnts[2]);
 		getVertexData(indexCoord[i++], pnts[3]);
-
+                count += 4;
 		if (intersectBoundingPolytope(pnts, bpolytope, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
-		    }
+                    }
 		}
 	    }
 	    break;
 	case PickShape.PICKCYLINDER:
 	    PickCylinder pickCylinder= (PickCylinder) pickShape;
-
 	    while (i < validVertexCount) {
 		getVertexData(indexCoord[i++], pnts[0]);
 		getVertexData(indexCoord[i++], pnts[1]);
 		getVertexData(indexCoord[i++], pnts[2]);
 		getVertexData(indexCoord[i++], pnts[3]);
-
+                count += 4;
 		if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
-		    }
+                    }
 		}
 	    }
 	    break;
 	case PickShape.PICKCONE:
 	    PickCone pickCone= (PickCone) pickShape;
-
 	    while (i < validVertexCount) {
 		getVertexData(indexCoord[i++], pnts[0]);
 		getVertexData(indexCoord[i++], pnts[1]);
 		getVertexData(indexCoord[i++], pnts[2]);
 		getVertexData(indexCoord[i++], pnts[3]);
+                count += 4;
 		if (intersectCone(pnts, pickCone, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
-		    }
+                    }
 		}
 	    }
 	    break;
@@ -207,7 +210,16 @@ class IndexedQuadArrayRetained extends IndexedGeometryArrayRetained {
 	} 
 
 	if (minDist < Double.MAX_VALUE) {
-	    dist[0] = minDist;
+            assert(minICount >= 4);
+            int[] vertexIndices = iInfo.getVertexIndices();
+            if (vertexIndices == null) {
+                vertexIndices = new int[4];
+                iInfo.setVertexIndices(vertexIndices);
+            }
+            vertexIndices[0] = minICount - 4;
+            vertexIndices[1] = minICount - 3;
+            vertexIndices[2] = minICount - 2;
+            vertexIndices[3] = minICount - 1;
 	    iPnt.x = x;
 	    iPnt.y = y;
 	    iPnt.z = z;
@@ -215,8 +227,7 @@ class IndexedQuadArrayRetained extends IndexedGeometryArrayRetained {
 	}
 	return false;
    
-    }
-    
+    }    
 
     // intersect pnts[] with every quad in this object
     boolean intersect(Point3d[] pnts) {
diff --git a/src/classes/share/javax/media/j3d/IndexedTriangleArray.java b/src/classes/share/javax/media/j3d/IndexedTriangleArray.java
index 300a812..3f923a4 100644
--- a/src/classes/share/javax/media/j3d/IndexedTriangleArray.java
+++ b/src/classes/share/javax/media/j3d/IndexedTriangleArray.java
@@ -27,32 +27,32 @@ public class IndexedTriangleArray extends IndexedGeometryArray {
     }
 
     /**
-     * Constructs an empty IndexedTriangleArray object with the specified
-     * number of vertices, vertex format, and number of indices.
-     * @param vertexCount the number of vertex elements in this object
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
-     * @param indexCount the number of indices in this object.  This
-     * count is the maximum number of vertices that will be rendered.
+     * Constructs an empty IndexedTriangleArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int)}
+     * for a description of this parameter.
+     *
      * @exception IllegalArgumentException if vertexCount is less than 1,
      * or indexCount is less than 3, or indexCount is <i>not</i>
      * a multiple of 3
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int)}
+     * for more exceptions that can be thrown
      */
-    public IndexedTriangleArray(int vertexCount, int vertexFormat,
-				int indexCount) {
+    public IndexedTriangleArray(int vertexCount, int vertexFormat, int indexCount) {
 	super(vertexCount,vertexFormat, indexCount);
 
-        if (vertexCount < 1)
+        if (vertexCount < 1) 
 	    throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleArray0")); 
 
         if (indexCount < 3 || ((indexCount%3) != 0))
@@ -60,64 +60,35 @@ public class IndexedTriangleArray extends IndexedGeometryArray {
     }
 
     /**
-     * Constructs an empty IndexedTriangleArray object with the specified
-     * number of vertices, vertex format, number of texture coordinate
-     * sets, texture coordinate mapping array, and number of indices.
-     *
-     * @param vertexCount the number of vertex elements in this object<p>
-     *
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
-     *
-     * @param texCoordSetCount the number of texture coordinate sets
-     * in this GeometryArray object.  If <code>vertexFormat</code>
-     * does not include one of <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetCount</code> parameter is not used.<p>
-     *
-     * @param texCoordSetMap an array that maps texture coordinate
-     * sets to texture units.  The array is indexed by texture unit
-     * number for each texture unit in the associated Appearance
-     * object.  The values in the array specify the texture coordinate
-     * set within this GeometryArray object that maps to the
-     * corresponding texture
-     * unit.  All elements within the array must be less than
-     * <code>texCoordSetCount</code>.  A negative value specifies that
-     * no texture coordinate set maps to the texture unit
-     * corresponding to the index.  If there are more texture units in
-     * any associated Appearance object than elements in the mapping
-     * array, the extra elements are assumed to be -1.  The same
-     * texture coordinate set may be used for more than one texture
-     * unit.  Each texture unit in every associated Appearance must
-     * have a valid source of texture coordinates: either a
-     * non-negative texture coordinate set must be specified in the
-     * mapping array or texture coordinate generation must be enabled.
-     * Texture coordinate generation will take precedence for those
-     * texture units for which a texture coordinate set is specified
-     * and texture coordinate generation is enabled.  If
-     * <code>vertexFormat</code> does not include one of
-     * <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetMap</code> array is not used.<p>
-     *
-     * @param indexCount the number of indices in this object.  This
-     * count is the maximum number of vertices that will be rendered.
+     * Constructs an empty IndexedTriangleArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int)}
+     * for a description of this parameter.
      *
      * @exception IllegalArgumentException if vertexCount is less than 1,
      * or indexCount is less than 3, or indexCount is <i>not</i>
      * a multiple of 3
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for more exceptions that can be thrown
      *
      * @since Java 3D 1.2
      */
@@ -131,7 +102,64 @@ public class IndexedTriangleArray extends IndexedGeometryArray {
 	      texCoordSetCount, texCoordSetMap,
 	      indexCount);
 
-        if (vertexCount < 1)
+        if (vertexCount < 1) 
+	    throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleArray0")); 
+
+        if (indexCount < 3 || ((indexCount%3) != 0))
+	    throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleArray1"));
+    }
+
+    /**
+     * Constructs an empty IndexedTriangleArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrSizes
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int,int[],int)}
+     * for a description of this parameter.
+     *
+     * @exception IllegalArgumentException if vertexCount is less than 1,
+     * or indexCount is less than 3, or indexCount is <i>not</i>
+     * a multiple of 3
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for more exceptions that can be thrown
+     *
+     * @since Java 3D 1.4
+     */
+    public IndexedTriangleArray(int vertexCount,
+				int vertexFormat,
+				int texCoordSetCount,
+				int[] texCoordSetMap,
+				int vertexAttrCount,
+				int[] vertexAttrSizes,
+				int indexCount) {
+
+	super(vertexCount, vertexFormat,
+	      texCoordSetCount, texCoordSetMap,
+	      vertexAttrCount, vertexAttrSizes,
+	      indexCount);
+
+        if (vertexCount < 1) 
 	    throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleArray0")); 
 
         if (indexCount < 3 || ((indexCount%3) != 0))
@@ -152,23 +180,26 @@ public class IndexedTriangleArray extends IndexedGeometryArray {
      * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
      */
     public NodeComponent cloneNodeComponent() {
-	IndexedTriangleArrayRetained rt = (IndexedTriangleArrayRetained) retained;
-	int texSetCount = rt.getTexCoordSetCount();	
-        IndexedTriangleArray t;
-
-	if (texSetCount == 0) {
-	    t = new IndexedTriangleArray(rt.getVertexCount(),
-					 rt.getVertexFormat(),
-					 rt.getIndexCount());
-	} else {
-	    int texMap[] = new int[rt.getTexCoordSetMapLength()];
-	    rt.getTexCoordSetMap(texMap);	    	    
-	    t = new IndexedTriangleArray(rt.getVertexCount(),
-					 rt.getVertexFormat(),
-					 texSetCount,
-					 texMap,
-					 rt.getIndexCount());
-	}
+        IndexedTriangleArrayRetained rt = (IndexedTriangleArrayRetained) retained;
+        int texSetCount = rt.getTexCoordSetCount();
+        int[] texMap = null;
+        int vertexAttrCount = rt.getVertexAttrCount();
+        int[] vertexAttrSizes = null;
+        if (texSetCount > 0) {
+            texMap = new int[rt.getTexCoordSetMapLength()];
+            rt.getTexCoordSetMap(texMap);
+        }
+        if (vertexAttrCount > 0) {
+            vertexAttrSizes = new int[vertexAttrCount];
+            rt.getVertexAttrSizes(vertexAttrSizes);
+        }
+        IndexedTriangleArray t = new IndexedTriangleArray(rt.getVertexCount(),
+                rt.getVertexFormat(),
+                texSetCount,
+                texMap,
+                vertexAttrCount, 
+                vertexAttrSizes,
+                rt.getIndexCount());
         t.duplicateNodeComponent(this);
         return t;
     }
diff --git a/src/classes/share/javax/media/j3d/IndexedTriangleArrayRetained.java b/src/classes/share/javax/media/j3d/IndexedTriangleArrayRetained.java
index f76dc69..d977894 100644
--- a/src/classes/share/javax/media/j3d/IndexedTriangleArrayRetained.java
+++ b/src/classes/share/javax/media/j3d/IndexedTriangleArrayRetained.java
@@ -27,14 +27,15 @@ class IndexedTriangleArrayRetained extends IndexedGeometryArrayRetained {
 	this.geoType = GEO_TYPE_INDEXED_TRI_SET;
     }
 
-    boolean intersect(PickShape pickShape, double dist[],  Point3d iPnt) {
+    boolean intersect(PickShape pickShape, PickInfo.IntersectionInfo iInfo,  int flags, Point3d iPnt) {
 	Point3d pnts[] = new Point3d[3];
 	double sdist[] = new double[1];
 	double minDist = Double.MAX_VALUE;
 	double x = 0, y = 0, z = 0;
+        int count = 0;
+        int minICount = 0;         
 	int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
 		 initialVertexIndex : initialCoordIndex);
-
 	pnts[0] = new Point3d();
 	pnts[1] = new Point3d();
 	pnts[2] = new Point3d();
@@ -47,15 +48,17 @@ class IndexedTriangleArrayRetained extends IndexedGeometryArrayRetained {
 		getVertexData(indexCoord[i++], pnts[0]);
 		getVertexData(indexCoord[i++], pnts[1]);
 		getVertexData(indexCoord[i++], pnts[2]);
+                count += 3;
 		if (intersectRay(pnts, pickRay, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
-			x = iPnt.x;
-			y = iPnt.y;
-			z = iPnt.z;
+                        minICount = count;
+                        x = iPnt.x;
+                        y = iPnt.y;
+                        z = iPnt.z;
 		    }
 		}
 	    }
@@ -66,18 +69,19 @@ class IndexedTriangleArrayRetained extends IndexedGeometryArrayRetained {
 		getVertexData(indexCoord[i++], pnts[0]);
 		getVertexData(indexCoord[i++], pnts[1]);
 		getVertexData(indexCoord[i++], pnts[2]);
-		if (intersectSegment(pnts, pickSegment.start,
-				     pickSegment.end, sdist, iPnt)
-		    && (sdist[0] <= 1.0)) {
-		    if (dist == null) {
+                count += 3;
+                if (intersectSegment(pnts, pickSegment.start,
+				     pickSegment.end, sdist, iPnt)) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
-			x = iPnt.x;
-			y = iPnt.y;
-			z = iPnt.z;
-			}
+                        minICount = count;
+                        x = iPnt.x;
+                        y = iPnt.y;
+                        z = iPnt.z;
+                    }
 		}
 	    }
 	    break;
@@ -89,15 +93,17 @@ class IndexedTriangleArrayRetained extends IndexedGeometryArrayRetained {
 		getVertexData(indexCoord[i++], pnts[0]);
 		getVertexData(indexCoord[i++], pnts[1]);
 		getVertexData(indexCoord[i++], pnts[2]);
+                count += 3;
 		if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
-			x = iPnt.x;
-			y = iPnt.y;
-			    z = iPnt.z;
+                        minICount = count;
+                        x = iPnt.x;
+                        y = iPnt.y;
+                        z = iPnt.z;
 		    }
 		}
 	    }
@@ -110,15 +116,17 @@ class IndexedTriangleArrayRetained extends IndexedGeometryArrayRetained {
 		getVertexData(indexCoord[i++], pnts[0]);
 		getVertexData(indexCoord[i++], pnts[1]);
 		getVertexData(indexCoord[i++], pnts[2]);
+                count += 3;
 		if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
-			x = iPnt.x;
-			y = iPnt.y;
-			z = iPnt.z;
+                        minICount = count;
+                        x = iPnt.x;
+                        y = iPnt.y;
+                        z = iPnt.z;
 		    }
 		}
 	    }
@@ -131,16 +139,18 @@ class IndexedTriangleArrayRetained extends IndexedGeometryArrayRetained {
 		getVertexData(indexCoord[i++], pnts[0]);
 		getVertexData(indexCoord[i++], pnts[1]);
 		getVertexData(indexCoord[i++], pnts[2]);
-		if (intersectBoundingPolytope(pnts, bpolytope,
+                count += 3;
+                if (intersectBoundingPolytope(pnts, bpolytope,
 					      sdist,iPnt)) { 
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
-			x = iPnt.x;
-			y = iPnt.y;
-			z = iPnt.z;
+                        minICount = count;
+                        x = iPnt.x;
+                        y = iPnt.y;
+                        z = iPnt.z;
 		    }
 		}
 	    }
@@ -151,16 +161,18 @@ class IndexedTriangleArrayRetained extends IndexedGeometryArrayRetained {
 		getVertexData(indexCoord[i++], pnts[0]);
 		getVertexData(indexCoord[i++], pnts[1]);
 		getVertexData(indexCoord[i++], pnts[2]);
+                count += 3;
 		if (intersectCylinder(pnts, pickCylinder, sdist,
 				      iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
-			x = iPnt.x;
-			y = iPnt.y;
-			z = iPnt.z;
+                        minICount = count;
+                        x = iPnt.x;
+                        y = iPnt.y;
+                        z = iPnt.z;
 		    }
 		}
 	    }
@@ -172,15 +184,17 @@ class IndexedTriangleArrayRetained extends IndexedGeometryArrayRetained {
 		getVertexData(indexCoord[i++], pnts[0]);
 		getVertexData(indexCoord[i++], pnts[1]);
 		getVertexData(indexCoord[i++], pnts[2]);
+                count += 3;
 		if (intersectCone(pnts, pickCone, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
-			x = iPnt.x;
-			y = iPnt.y;
-			z = iPnt.z;
+                        minICount = count;
+                        x = iPnt.x;
+                        y = iPnt.y;
+                        z = iPnt.z;
 		    }
 		}
 	    }
@@ -194,7 +208,15 @@ class IndexedTriangleArrayRetained extends IndexedGeometryArrayRetained {
 
     
 	if (minDist < Double.MAX_VALUE) {
-	    dist[0] = minDist;
+            assert(minICount >= 3);
+            int[] vertexIndices = iInfo.getVertexIndices();
+            if (vertexIndices == null) {
+                vertexIndices = new int[3];
+                iInfo.setVertexIndices(vertexIndices);
+            }
+            vertexIndices[0] = minICount - 3;
+            vertexIndices[1] = minICount - 2;
+            vertexIndices[2] = minICount - 1;
 	    iPnt.x = x;
 	    iPnt.y = y;
 	    iPnt.z = z;
@@ -203,7 +225,6 @@ class IndexedTriangleArrayRetained extends IndexedGeometryArrayRetained {
 	return false;
     }
   
-  
     // intersect pnts[] with every triangle in this object
     boolean intersect(Point3d[] pnts) {
 	Point3d[] points = new Point3d[3];
diff --git a/src/classes/share/javax/media/j3d/IndexedTriangleFanArray.java b/src/classes/share/javax/media/j3d/IndexedTriangleFanArray.java
index 795de14..d094859 100644
--- a/src/classes/share/javax/media/j3d/IndexedTriangleFanArray.java
+++ b/src/classes/share/javax/media/j3d/IndexedTriangleFanArray.java
@@ -29,39 +29,40 @@ public class IndexedTriangleFanArray extends IndexedGeometryStripArray {
     IndexedTriangleFanArray() {}
 
     /**
-     * Constructs an empty IndexedTriangleFanArray object with the specified
-     * number of vertices, vertex format, number of indices, and
-     * array of per-strip index counts.
-     * @param vertexCount the number of vertex elements in this object
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
-     * @param indexCount the number of indices in this object.  This
-     * count is the maximum number of vertices that will be rendered.
-     * @param stripIndexCounts array that specifies
-     * the count of the number of indices for each separate strip.
-     * The length of this array is the number of separate strips.
+     * Constructs an empty IndexedTriangleFanArray object using the
+     * specified parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int)}
+     * for a description of this parameter.
+     *
+     * @param stripIndexCounts
+     * see {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
      * @exception IllegalArgumentException if vertexCount is less than 1,
      * or indexCount is less than 3,
      * or any element in the stripIndexCounts array is less than 3
+     * ;<br>
+     * See {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[])}
+     * for more exceptions that can be thrown
      */
     public IndexedTriangleFanArray(int vertexCount,
 				   int vertexFormat,
 				   int indexCount,
-				   int stripIndexCounts[]) {
+				   int[] stripIndexCounts) {
 
 	super(vertexCount, vertexFormat, indexCount, stripIndexCounts);
 
-        if (vertexCount < 1)
+        if (vertexCount < 1) 
 	    throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleFanArray0")); 
 
         if (indexCount < 3 )
@@ -69,69 +70,39 @@ public class IndexedTriangleFanArray extends IndexedGeometryStripArray {
     }
 
     /**
-     * Constructs an empty IndexedTriangleFanArray object with the specified
-     * number of vertices, vertex format, number of texture coordinate
-     * sets, texture coordinate mapping array, number of indices, and
-     * array of per-strip index counts.
-     *
-     * @param vertexCount the number of vertex elements in this object<p>
-     *
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
-     *
-     * @param texCoordSetCount the number of texture coordinate sets
-     * in this GeometryArray object.  If <code>vertexFormat</code>
-     * does not include one of <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3 or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetCount</code> parameter is not used.<p>
-     *
-     * @param texCoordSetMap an array that maps texture coordinate
-     * sets to texture units.  The array is indexed by texture unit
-     * number for each texture unit in the associated Appearance
-     * object.  The values in the array specify the texture coordinate
-     * set within this GeometryArray object that maps to the
-     * corresponding texture
-     * unit.  All elements within the array must be less than
-     * <code>texCoordSetCount</code>.  A negative value specifies that
-     * no texture coordinate set maps to the texture unit
-     * corresponding to the index.  If there are more texture units in
-     * any associated Appearance object than elements in the mapping
-     * array, the extra elements are assumed to be -1.  The same
-     * texture coordinate set may be used for more than one texture
-     * unit.  Each texture unit in every associated Appearance must
-     * have a valid source of texture coordinates: either a
-     * non-negative texture coordinate set must be specified in the
-     * mapping array or texture coordinate generation must be enabled.
-     * Texture coordinate generation will take precedence for those
-     * texture units for which a texture coordinate set is specified
-     * and texture coordinate generation is enabled.  If
-     * <code>vertexFormat</code> does not include one of
-     * <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetMap</code> array is not used.<p>
-     *
-     * @param indexCount the number of indices in this object.  This
-     * count is the maximum number of vertices that will be rendered.<p>
-     *
-     * @param stripIndexCounts array that specifies
-     * the count of the number of indices for each separate strip.
-     * The length of this array is the number of separate strips.
+     * Constructs an empty IndexedTriangleFanArray object using the
+     * specified parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int)}
+     * for a description of this parameter.
+     *
+     * @param stripIndexCounts
+     * see {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
      *
      * @exception IllegalArgumentException if vertexCount is less than 1,
      * or indexCount is less than 3,
      * or any element in the stripIndexCounts array is less than 3
+     * ;<br>
+     * See {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[])}
+     * for more exceptions that can be thrown
      *
      * @since Java 3D 1.2
      */
@@ -140,13 +111,75 @@ public class IndexedTriangleFanArray extends IndexedGeometryStripArray {
 				   int texCoordSetCount,
 				   int[] texCoordSetMap,
 				   int indexCount,
-				   int stripIndexCounts[]) {
+				   int[] stripIndexCounts) {
+
+	super(vertexCount, vertexFormat,
+	      texCoordSetCount, texCoordSetMap,
+	      indexCount, stripIndexCounts);
+
+        if (vertexCount < 1) 
+	    throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleFanArray0")); 
+
+        if (indexCount < 3 )
+	    throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleFanArray1"));
+    }
+
+    /**
+     * Constructs an empty IndexedTriangleFanArray object using the
+     * specified parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrSizes
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int,int[],int)}
+     * for a description of this parameter.
+     *
+     * @param stripIndexCounts
+     * see {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @exception IllegalArgumentException if vertexCount is less than 1,
+     * or indexCount is less than 3,
+     * or any element in the stripIndexCounts array is less than 3
+     * ;<br>
+     * See {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[],int,int[])}
+     * for more exceptions that can be thrown
+     *
+     * @since Java 3D 1.4
+     */
+    public IndexedTriangleFanArray(int vertexCount,
+				   int vertexFormat,
+				   int texCoordSetCount,
+				   int[] texCoordSetMap,
+				   int vertexAttrCount,
+				   int[] vertexAttrSizes,
+				   int indexCount,
+				   int[] stripIndexCounts) {
 
 	super(vertexCount, vertexFormat,
 	      texCoordSetCount, texCoordSetMap,
+	      vertexAttrCount, vertexAttrSizes,
 	      indexCount, stripIndexCounts);
 
-        if (vertexCount < 1)
+        if (vertexCount < 1) 
 	    throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleFanArray0")); 
 
         if (indexCount < 3 )
@@ -167,29 +200,30 @@ public class IndexedTriangleFanArray extends IndexedGeometryStripArray {
      * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
      */
     public NodeComponent cloneNodeComponent() {
-	IndexedTriangleFanArrayRetained rt = 
-	    (IndexedTriangleFanArrayRetained) retained;
-	int stripIndexCounts[] = new int[rt.getNumStrips()];
-	rt.getStripIndexCounts(stripIndexCounts);
-	int texSetCount = rt.getTexCoordSetCount();
-        IndexedTriangleFanArray t;
-
-	if (texSetCount == 0) {
-	    t = new IndexedTriangleFanArray(rt.getVertexCount(),
-					    rt.getVertexFormat(),
-					    rt.getIndexCount(),
-					    stripIndexCounts);
-	} else {
-	    int texMap[] = new int[rt.getTexCoordSetMapLength()];
-	    rt.getTexCoordSetMap(texMap);	    
-	    t = new IndexedTriangleFanArray(rt.getVertexCount(),
-					    rt.getVertexFormat(),
-					    texSetCount,
-					    texMap,
-					    rt.getIndexCount(),
-					    stripIndexCounts);
-	}
-
+        IndexedTriangleFanArrayRetained rt =
+                (IndexedTriangleFanArrayRetained) retained;
+        int stripIndexCounts[] = new int[rt.getNumStrips()];
+        rt.getStripIndexCounts(stripIndexCounts);
+        int texSetCount = rt.getTexCoordSetCount();
+        int[] texMap = null;
+        int vertexAttrCount = rt.getVertexAttrCount();
+        int[] vertexAttrSizes = null;
+        if (texSetCount > 0) {
+            texMap = new int[rt.getTexCoordSetMapLength()];
+            rt.getTexCoordSetMap(texMap);
+        }
+        if (vertexAttrCount > 0) {
+            vertexAttrSizes = new int[vertexAttrCount];
+            rt.getVertexAttrSizes(vertexAttrSizes);
+        }
+        IndexedTriangleFanArray t = new IndexedTriangleFanArray(rt.getVertexCount(),
+                rt.getVertexFormat(),
+                texSetCount,
+                texMap,
+                vertexAttrCount,
+                vertexAttrSizes,
+                rt.getIndexCount(),
+                stripIndexCounts);
         t.duplicateNodeComponent(this);
         return t;
     }
diff --git a/src/classes/share/javax/media/j3d/IndexedTriangleFanArrayRetained.java b/src/classes/share/javax/media/j3d/IndexedTriangleFanArrayRetained.java
index b4f0066..c6e37f7 100644
--- a/src/classes/share/javax/media/j3d/IndexedTriangleFanArrayRetained.java
+++ b/src/classes/share/javax/media/j3d/IndexedTriangleFanArrayRetained.java
@@ -32,15 +32,16 @@ class IndexedTriangleFanArrayRetained extends IndexedGeometryStripArrayRetained
 	geoType = GEO_TYPE_INDEXED_TRI_FAN_SET;
     }
 
-    boolean intersect(PickShape pickShape, double dist[],  Point3d iPnt) {
+    boolean intersect(PickShape pickShape, PickInfo.IntersectionInfo iInfo,  int flags, Point3d iPnt) {
 	Point3d pnts[] = new Point3d[3];
 	double sdist[] = new double[1];
 	double minDist = Double.MAX_VALUE;
 	double x = 0, y = 0, z = 0;
 	int i = 0;
-	int j, scount, count = 0;
-
-	pnts[0] = new Point3d();
+	int j, scount, count = 0, fSCount;
+        int minICount = 0; 
+        int minFSCount = 0;
+        pnts[0] = new Point3d();
 	pnts[1] = new Point3d();
 	pnts[2] = new Point3d();    
 
@@ -48,22 +49,25 @@ class IndexedTriangleFanArrayRetained extends IndexedGeometryStripArrayRetained
 	case PickShape.PICKRAY:
 	    PickRay pickRay= (PickRay) pickShape;
 	
-	    while (i < stripIndexCounts.length) {  
+	    while (i < stripIndexCounts.length) {
+                fSCount = count;
 		getVertexData(indexCoord[count++], pnts[0]);
 		getVertexData(indexCoord[count++], pnts[1]);
 		scount = stripIndexCounts[i++];
 
 		for (j=2; j < scount; j++) {
-		    getVertexData(count++, pnts[2]);
+		    getVertexData(indexCoord[count++], pnts[2]);
 		    if (intersectRay(pnts, pickRay, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minFSCount = fSCount;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[1].set(pnts[2]);
@@ -74,21 +78,24 @@ class IndexedTriangleFanArrayRetained extends IndexedGeometryStripArrayRetained
 	    PickSegment pickSegment = (PickSegment) pickShape;
 
 	    while (i < stripIndexCounts.length) {  
+                fSCount = count;
 		getVertexData(indexCoord[count++], pnts[0]);
 		getVertexData(indexCoord[count++], pnts[1]);
 		scount = stripIndexCounts[i++];
 		for (j=2; j < scount; j++) {
-		    getVertexData(j++, pnts[2]);
+		    getVertexData(indexCoord[count++], pnts[2]);
 		    if (intersectSegment(pnts, pickSegment.start, 
 					 pickSegment.end, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minFSCount = fSCount;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[1].set(pnts[2]);
@@ -100,20 +107,23 @@ class IndexedTriangleFanArrayRetained extends IndexedGeometryStripArrayRetained
 		((PickBounds) pickShape).bounds;
 
 	    while (i < stripIndexCounts.length) {  
+                fSCount = count;
 		getVertexData(indexCoord[count++], pnts[0]);
 		getVertexData(indexCoord[count++], pnts[1]);
 		scount = stripIndexCounts[i++];
 		for (j=2; j < scount; j++) {
-		    getVertexData(count++, pnts[2]);
+		    getVertexData(indexCoord[count++], pnts[2]);
 		    if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minFSCount = fSCount;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[1].set(pnts[2]);
@@ -125,22 +135,25 @@ class IndexedTriangleFanArrayRetained extends IndexedGeometryStripArrayRetained
 		                     ((PickBounds) pickShape).bounds;
 
 	    while (i < stripIndexCounts.length) {  
+                fSCount = count;
 		getVertexData(indexCoord[count++], pnts[0]);
 		getVertexData(indexCoord[count++], pnts[1]);
 		scount = stripIndexCounts[i++];
 
 		for (j=2; j < scount; j++) {
-		    getVertexData(count++, pnts[2]);
+		    getVertexData(indexCoord[count++], pnts[2]);
 		    if (intersectBoundingSphere(pnts, bsphere, sdist,
 						iPnt)) { 
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minFSCount = fSCount;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[1].set(pnts[2]);
@@ -152,22 +165,25 @@ class IndexedTriangleFanArrayRetained extends IndexedGeometryStripArrayRetained
 		((PickBounds) pickShape).bounds;
 
 	    while (i < stripIndexCounts.length) {  
+                fSCount = count;
 		getVertexData(indexCoord[count++], pnts[0]);
 		getVertexData(indexCoord[count++], pnts[1]);
 		scount = stripIndexCounts[i++];
 
 		for (j=2; j < scount; j++) {
-		    getVertexData(count++, pnts[2]);
+		    getVertexData(indexCoord[count++], pnts[2]);
 		    if (intersectBoundingPolytope(pnts, bpolytope,
 						  sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minFSCount = fSCount;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[1].set(pnts[2]);
@@ -178,21 +194,24 @@ class IndexedTriangleFanArrayRetained extends IndexedGeometryStripArrayRetained
 	    PickCylinder pickCylinder= (PickCylinder) pickShape;
 
 	    while (i < stripIndexCounts.length) {  
+                fSCount = count;
 		getVertexData(indexCoord[count++], pnts[0]);
 		getVertexData(indexCoord[count++], pnts[1]);
 		scount = stripIndexCounts[i++];
 
 		for (j=2; j < scount; j++) {
-		    getVertexData(count++, pnts[2]);
+		    getVertexData(indexCoord[count++], pnts[2]);
 		    if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minFSCount = fSCount;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[1].set(pnts[2]);
@@ -203,21 +222,24 @@ class IndexedTriangleFanArrayRetained extends IndexedGeometryStripArrayRetained
 	    PickCone pickCone= (PickCone) pickShape;
 
 	    while (i < stripIndexCounts.length) {  
+                fSCount = count;
 		getVertexData(indexCoord[count++], pnts[0]);
 		getVertexData(indexCoord[count++], pnts[1]);
 		scount = stripIndexCounts[i++];
 
 		for (j=2; j < scount; j++) {
-		    getVertexData(count++, pnts[2]);
+		    getVertexData(indexCoord[count++], pnts[2]);
 		    if (intersectCone(pnts, pickCone, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minFSCount = fSCount;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[1].set(pnts[2]);
@@ -232,14 +254,22 @@ class IndexedTriangleFanArrayRetained extends IndexedGeometryStripArrayRetained
 	} 
 
 	if (minDist < Double.MAX_VALUE) {
-	    dist[0] = minDist;
+            assert(minICount >= 3);
+            int[] vertexIndices = iInfo.getVertexIndices();
+            if (vertexIndices == null) {
+                vertexIndices = new int[3];
+                iInfo.setVertexIndices(vertexIndices);
+            }
+            vertexIndices[0] = minFSCount;
+            vertexIndices[1] = minICount - 2;
+            vertexIndices[2] = minICount - 1;
 	    iPnt.x = x;
 	    iPnt.y = y;
 	    iPnt.z = z;
-	    return true;
+            return true;
 	}
 	return false;
-    }
+    }    
  
     // intersect pnts[] with every triangle in this object
     boolean intersect(Point3d[] pnts) {
diff --git a/src/classes/share/javax/media/j3d/IndexedTriangleStripArray.java b/src/classes/share/javax/media/j3d/IndexedTriangleStripArray.java
index 13970bb..b5395db 100644
--- a/src/classes/share/javax/media/j3d/IndexedTriangleStripArray.java
+++ b/src/classes/share/javax/media/j3d/IndexedTriangleStripArray.java
@@ -31,35 +31,36 @@ public class IndexedTriangleStripArray extends IndexedGeometryStripArray {
     }
 
     /**
-     * Constructs an empty IndexedTriangleStripArray object with the specified
-     * number of vertices, vertex format, number of indices, and
-     * array of per-strip index counts.
-     * @param vertexCount the number of vertex elements in this object
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
-     * @param indexCount the number of indices in this object.  This
-     * count is the maximum number of vertices that will be rendered.
-     * @param stripIndexCounts array that specifies
-     * the count of the number of indices for each separate strip.
-     * The length of this array is the number of separate strips.
+     * Constructs an empty IndexedTriangleStripArray object using the
+     * specified parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int)}
+     * for a description of this parameter.
+     *
+     * @param stripIndexCounts
+     * see {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
      * @exception IllegalArgumentException if vertexCount is less than 1,
      * or indexCount is less than 3,
      * or any element in the stripIndexCounts array is less than 3
+     * ;<br>
+     * See {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[])}
+     * for more exceptions that can be thrown
      */
     public IndexedTriangleStripArray(int vertexCount,
 				     int vertexFormat,
 				     int indexCount,
-				     int stripIndexCounts[]) {
+				     int[] stripIndexCounts) {
 
 	super(vertexCount, vertexFormat, indexCount, stripIndexCounts);
 
@@ -71,69 +72,39 @@ public class IndexedTriangleStripArray extends IndexedGeometryStripArray {
     }
 
     /**
-     * Constructs an empty IndexedTriangleStripArray object with the specified
-     * number of vertices, vertex format, number of texture coordinate
-     * sets, texture coordinate mapping array, number of indices, and
-     * array of per-strip index counts.
-     *
-     * @param vertexCount the number of vertex elements in this object<p>
-     *
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
-     *
-     * @param texCoordSetCount the number of texture coordinate sets
-     * in this GeometryArray object.  If <code>vertexFormat</code>
-     * does not include one of <code>TEXTURE_COORDINATE_2</code> or
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetCount</code> parameter is not used.<p>
-     *
-     * @param texCoordSetMap an array that maps texture coordinate
-     * sets to texture units.  The array is indexed by texture unit
-     * number for each texture unit in the associated Appearance
-     * object.  The values in the array specify the texture coordinate
-     * set within this GeometryArray object that maps to the
-     * corresponding texture
-     * unit.  All elements within the array must be less than
-     * <code>texCoordSetCount</code>.  A negative value specifies that
-     * no texture coordinate set maps to the texture unit
-     * corresponding to the index.  If there are more texture units in
-     * any associated Appearance object than elements in the mapping
-     * array, the extra elements are assumed to be -1.  The same
-     * texture coordinate set may be used for more than one texture
-     * unit.  Each texture unit in every associated Appearance must
-     * have a valid source of texture coordinates: either a
-     * non-negative texture coordinate set must be specified in the
-     * mapping array or texture coordinate generation must be enabled.
-     * Texture coordinate generation will take precedence for those
-     * texture units for which a texture coordinate set is specified
-     * and texture coordinate generation is enabled.  If
-     * <code>vertexFormat</code> does not include one of
-     * <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetMap</code> array is not used.<p>
-     *
-     * @param indexCount the number of indices in this object.  This
-     * count is the maximum number of vertices that will be rendered.<p>
-     *
-     * @param stripIndexCounts array that specifies
-     * the count of the number of indices for each separate strip.
-     * The length of this array is the number of separate strips.
+     * Constructs an empty IndexedTriangleStripArray object using the
+     * specified parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int)}
+     * for a description of this parameter.
+     *
+     * @param stripIndexCounts
+     * see {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
      *
      * @exception IllegalArgumentException if vertexCount is less than 1,
      * or indexCount is less than 3,
      * or any element in the stripIndexCounts array is less than 3
+     * ;<br>
+     * See {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[])}
+     * for more exceptions that can be thrown
      *
      * @since Java 3D 1.2
      */
@@ -142,10 +113,72 @@ public class IndexedTriangleStripArray extends IndexedGeometryStripArray {
 				     int texCoordSetCount,
 				     int[] texCoordSetMap,
 				     int indexCount,
-				     int stripIndexCounts[]) {
+				     int[] stripIndexCounts) {
+
+	super(vertexCount, vertexFormat,
+	      texCoordSetCount, texCoordSetMap,
+	      indexCount, stripIndexCounts);
+
+        if (vertexCount < 1) 
+	    throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleStripArray0")); 
+
+        if (indexCount < 3 )
+	    throw new IllegalArgumentException(J3dI18N.getString("IndexedTriangleStripArray1"));
+    }
+
+    /**
+     * Constructs an empty IndexedTriangleStripArray object using the
+     * specified parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrSizes
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param indexCount
+     * see {@link IndexedGeometryArray#IndexedGeometryArray(int,int,int,int[],int,int[],int)}
+     * for a description of this parameter.
+     *
+     * @param stripIndexCounts
+     * see {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @exception IllegalArgumentException if vertexCount is less than 1,
+     * or indexCount is less than 3,
+     * or any element in the stripIndexCounts array is less than 3
+     * ;<br>
+     * See {@link IndexedGeometryStripArray#IndexedGeometryStripArray(int,int,int,int[],int,int[],int,int[])}
+     * for more exceptions that can be thrown
+     *
+     * @since Java 3D 1.4
+     */
+    public IndexedTriangleStripArray(int vertexCount,
+				     int vertexFormat,
+				     int texCoordSetCount,
+				     int[] texCoordSetMap,
+				     int vertexAttrCount,
+				     int[] vertexAttrSizes,
+				     int indexCount,
+				     int[] stripIndexCounts) {
 
 	super(vertexCount, vertexFormat,
 	      texCoordSetCount, texCoordSetMap,
+	      vertexAttrCount, vertexAttrSizes,
 	      indexCount, stripIndexCounts);
 
         if (vertexCount < 1) 
@@ -169,28 +202,31 @@ public class IndexedTriangleStripArray extends IndexedGeometryStripArray {
      * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
      */
     public NodeComponent cloneNodeComponent() {
-	IndexedTriangleStripArrayRetained rt =
-	    (IndexedTriangleStripArrayRetained) retained;
+        IndexedTriangleStripArrayRetained rt =
+                (IndexedTriangleStripArrayRetained) retained;
         int stripIndexCounts[] = new int[rt.getNumStrips()];
-	rt.getStripIndexCounts(stripIndexCounts);
-	int texSetCount = rt.getTexCoordSetCount();
-	IndexedTriangleStripArray l;
-	if (texSetCount == 0) {
-	    l = new IndexedTriangleStripArray(rt.getVertexCount(),
-					      rt.getVertexFormat(),
-					      rt.getIndexCount(),
-					      stripIndexCounts);
-	} else {
-	    int texMap[] = new int[rt.getTexCoordSetMapLength()];
-	    rt.getTexCoordSetMap(texMap);
-	    l = new IndexedTriangleStripArray(rt.getVertexCount(),
-					      rt.getVertexFormat(),
-					      texSetCount,
-					      texMap,
-					      rt.getIndexCount(),
-					      stripIndexCounts);
-	}
-        l.duplicateNodeComponent(this);
-        return l;
+        rt.getStripIndexCounts(stripIndexCounts);
+        int texSetCount = rt.getTexCoordSetCount();
+        int[] texMap = null;
+        int vertexAttrCount = rt.getVertexAttrCount();
+        int[] vertexAttrSizes = null;
+        if (texSetCount > 0) {
+            texMap = new int[rt.getTexCoordSetMapLength()];
+            rt.getTexCoordSetMap(texMap);
+        }
+        if (vertexAttrCount > 0) {
+            vertexAttrSizes = new int[vertexAttrCount];
+            rt.getVertexAttrSizes(vertexAttrSizes);
+        }
+        IndexedTriangleStripArray t = new IndexedTriangleStripArray(rt.getVertexCount(),
+                rt.getVertexFormat(),
+                texSetCount,
+                texMap,
+                vertexAttrCount,
+                vertexAttrSizes,
+                rt.getIndexCount(),
+                stripIndexCounts);
+        t.duplicateNodeComponent(this);
+        return t;
     }
 }
diff --git a/src/classes/share/javax/media/j3d/IndexedTriangleStripArrayRetained.java b/src/classes/share/javax/media/j3d/IndexedTriangleStripArrayRetained.java
index 5e2b017..888b0de 100644
--- a/src/classes/share/javax/media/j3d/IndexedTriangleStripArrayRetained.java
+++ b/src/classes/share/javax/media/j3d/IndexedTriangleStripArrayRetained.java
@@ -31,14 +31,14 @@ class IndexedTriangleStripArrayRetained extends IndexedGeometryStripArrayRetaine
 	geoType = GEO_TYPE_INDEXED_TRI_STRIP_SET;
     }
   
-
-    boolean intersect(PickShape pickShape, double dist[],  Point3d iPnt) {
+    boolean intersect(PickShape pickShape, PickInfo.IntersectionInfo iInfo,  int flags, Point3d iPnt) {
 	Point3d pnts[] = new Point3d[3];
 	double sdist[] = new double[1];
 	double minDist = Double.MAX_VALUE;
 	double x = 0, y = 0, z = 0;
 	int i = 0;
 	int j, scount, count = 0;
+        int minICount = 0;
 
 	pnts[0] = new Point3d();
 	pnts[1] = new Point3d();
@@ -54,17 +54,18 @@ class IndexedTriangleStripArrayRetained extends IndexedGeometryStripArrayRetaine
 		scount = stripIndexCounts[i++];
 
 		for (j=2; j < scount; j++) {
-		    getVertexData(count++, pnts[2]);
+		    getVertexData(indexCoord[count++], pnts[2]);
 		    if (intersectRay(pnts, pickRay, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
-			}
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
+                        }
 		    }
 		    pnts[0].set(pnts[1]);
 		    pnts[1].set(pnts[2]);
@@ -79,17 +80,18 @@ class IndexedTriangleStripArrayRetained extends IndexedGeometryStripArrayRetaine
 		getVertexData(indexCoord[count++], pnts[1]);
 		scount = stripIndexCounts[i++];
 		for (j=2; j < scount; j++) {
-		    getVertexData(j++, pnts[2]);
+		    getVertexData(indexCoord[count++], pnts[2]);
 		    if (intersectSegment(pnts, pickSegment.start, 
 					 pickSegment.end, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);
@@ -106,16 +108,17 @@ class IndexedTriangleStripArrayRetained extends IndexedGeometryStripArrayRetaine
 		getVertexData(indexCoord[count++], pnts[1]);
 		scount = stripIndexCounts[i++];
 		for (j=2; j < scount; j++) {
-		    getVertexData(count++, pnts[2]);
+		    getVertexData(indexCoord[count++], pnts[2]);
 		    if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);
@@ -133,17 +136,18 @@ class IndexedTriangleStripArrayRetained extends IndexedGeometryStripArrayRetaine
 		scount = stripIndexCounts[i++];
 
 		for (j=2; j < scount; j++) {
-		    getVertexData(count++, pnts[2]);
+		    getVertexData(indexCoord[count++], pnts[2]);
 		    if (intersectBoundingSphere(pnts, bsphere, sdist,
 						iPnt)) { 
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);
@@ -161,17 +165,18 @@ class IndexedTriangleStripArrayRetained extends IndexedGeometryStripArrayRetaine
 		scount = stripIndexCounts[i++];
 
 		for (j=2; j < scount; j++) {
-		    getVertexData(count++, pnts[2]);
+		    getVertexData(indexCoord[count++], pnts[2]);
 		    if (intersectBoundingPolytope(pnts, bpolytope,
 						  sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);
@@ -188,16 +193,17 @@ class IndexedTriangleStripArrayRetained extends IndexedGeometryStripArrayRetaine
 		scount = stripIndexCounts[i++];
 
 		for (j=2; j < scount; j++) {
-		    getVertexData(count++, pnts[2]);
+		    getVertexData(indexCoord[count++], pnts[2]);
 		    if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);
@@ -214,16 +220,17 @@ class IndexedTriangleStripArrayRetained extends IndexedGeometryStripArrayRetaine
 		scount = stripIndexCounts[i++];
 
 		for (j=2; j < scount; j++) {
-		    getVertexData(count++, pnts[2]);
+		    getVertexData(indexCoord[count++], pnts[2]);
 		    if (intersectCone(pnts, pickCone, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);
@@ -239,15 +246,23 @@ class IndexedTriangleStripArrayRetained extends IndexedGeometryStripArrayRetaine
 	} 
 
 	if (minDist < Double.MAX_VALUE) {
-	    dist[0] = minDist;
+            assert(minICount >=3);
+            int[] vertexIndices = iInfo.getVertexIndices();
+            if (vertexIndices == null) {
+                vertexIndices = new int[3];
+                iInfo.setVertexIndices(vertexIndices);
+            }
+            vertexIndices[0] = minICount - 3;
+            vertexIndices[1] = minICount - 2;
+            vertexIndices[2] = minICount - 1;
 	    iPnt.x = x;
 	    iPnt.y = y;
 	    iPnt.z = z;
-	    return true;
+            return true;
 	}
 	return false;
     }
- 
+         
     // intersect pnts[] with every triangle in this object
     boolean intersect(Point3d[] pnts) {
 	int j, end;
diff --git a/src/classes/share/javax/media/j3d/IndexedUnorderSet.java b/src/classes/share/javax/media/j3d/IndexedUnorderSet.java
index 89d3a4a..54e4b36 100644
--- a/src/classes/share/javax/media/j3d/IndexedUnorderSet.java
+++ b/src/classes/share/javax/media/j3d/IndexedUnorderSet.java
@@ -60,7 +60,7 @@ package javax.media.j3d;
 
 class IndexedUnorderSet implements Cloneable, java.io.Serializable  {
 
-    // TODO: set to false when release
+    // XXXX: set to false when release
     final static boolean debug = false;
 
     /**
diff --git a/src/classes/share/javax/media/j3d/J3DBuffer.java b/src/classes/share/javax/media/j3d/J3DBuffer.java
index 6422dc1..6cc97cf 100644
--- a/src/classes/share/javax/media/j3d/J3DBuffer.java
+++ b/src/classes/share/javax/media/j3d/J3DBuffer.java
@@ -25,21 +25,11 @@ import com.sun.j3d.internal.ByteOrderWrapper;
  * <code>rewind</code> on the read-only view, so that elements 0
  * through <code>buffer.limit()-1</code> will be available internally.
  *
- * <p>
- * NOTE: Use of this class requires version 1.4 of the
- * Java<sup><font size="-2">TM</font></sup>&nbsp;2 Platform.
- * Any attempt to access this class on an earlier version of the
- * Java&nbsp;2 Platform will fail with a NoClassDefFoundError.
- * Refer to the documentation for version 1.4 of the Java&nbsp;2 SDK
- * for a description of the
- * <code>
- * <a href="http://java.sun.com/j2se/1.4/docs/api/java/nio/package-summary.html">java.nio</a>
- * </code> package.
- *
  * @see GeometryArray#setCoordRefBuffer(J3DBuffer)
  * @see GeometryArray#setColorRefBuffer(J3DBuffer)
  * @see GeometryArray#setNormalRefBuffer(J3DBuffer)
  * @see GeometryArray#setTexCoordRefBuffer(int,J3DBuffer)
+ * @see GeometryArray#setVertexAttrRefBuffer(int,J3DBuffer)
  * @see GeometryArray#setInterleavedVertexBuffer(J3DBuffer)
  * @see CompressedGeometry#CompressedGeometry(CompressedGeometryHeader,J3DBuffer)
  *
diff --git a/src/classes/share/javax/media/j3d/J3DGraphics2DImpl.java b/src/classes/share/javax/media/j3d/J3DGraphics2DImpl.java
index 2ed6814..439b0f7 100644
--- a/src/classes/share/javax/media/j3d/J3DGraphics2DImpl.java
+++ b/src/classes/share/javax/media/j3d/J3DGraphics2DImpl.java
@@ -140,7 +140,7 @@ final class J3DGraphics2DImpl extends J3DGraphics2D {
 		    }
 		}
 		// Behavior Scheduler or other threads
-		// TODO: may not be legal for behaviorScheduler
+		// XXXX: may not be legal for behaviorScheduler
 		// May cause deadlock if it is in behaviorScheduler
 		// and we wait for Renderer to finish
 		boolean renderRun = (Thread.currentThread() != 
@@ -589,13 +589,13 @@ final class J3DGraphics2DImpl extends J3DGraphics2D {
 
     public final void drawArc(int x, int y, int width, int height,
 			      int startAngle, int arcAngle) {
-	// TODO: call validate with bounding box of primitive
+	// XXXX: call validate with bounding box of primitive
 	validate();
 	offScreenGraphics2D.drawArc(x, y, width, height, startAngle, arcAngle);
     }
 
     public final void drawGlyphVector(GlyphVector g, float x, float y) {
-	// TODO: call validate with bounding box of primitive
+	// XXXX: call validate with bounding box of primitive
 	validate();
 	offScreenGraphics2D.drawGlyphVector(g, x, y);
     }
@@ -619,29 +619,29 @@ final class J3DGraphics2DImpl extends J3DGraphics2D {
 	    }
 	    validate(minx, miny, maxx, maxy);
 	} else {
-	    // TODO: call validate with bounding box of primitive
-	    // TODO: Need to consider Stroke width
+	    // XXXX: call validate with bounding box of primitive
+	    // XXXX: Need to consider Stroke width
 	    validate();
 	}
 	offScreenGraphics2D.drawLine(x1, y1, x2, y2);
     }
 
     public final void drawOval(int x, int y, int width, int height) {
-	// TODO: call validate with bounding box of primitive
+	// XXXX: call validate with bounding box of primitive
 	validate();
 	offScreenGraphics2D.drawOval(x, y, width, height);
     }
 
     public final void drawPolygon(int xPoints[], int yPoints[],
 				  int nPoints) {
-	// TODO: call validate with bounding box of primitive
+	// XXXX: call validate with bounding box of primitive
 	validate();
 	offScreenGraphics2D.drawPolygon(xPoints,  yPoints, nPoints);
     }
 
     public final void drawPolyline(int xPoints[], int yPoints[],
 				   int nPoints) {
-	// TODO: call validate with bounding box of primitive
+	// XXXX: call validate with bounding box of primitive
 	validate();
 	offScreenGraphics2D.drawPolyline(xPoints,  yPoints, nPoints);
     }
@@ -661,7 +661,7 @@ final class J3DGraphics2DImpl extends J3DGraphics2D {
 
     public final void drawRoundRect(int x, int y, int width, int height,
 				    int arcWidth, int arcHeight) {
-	// TODO: call validate with bounding box of primitive
+	// XXXX: call validate with bounding box of primitive
 	validate();
 	offScreenGraphics2D.drawRoundRect(x, y, width, height, arcWidth,
 				      arcHeight);
@@ -669,14 +669,14 @@ final class J3DGraphics2DImpl extends J3DGraphics2D {
 
     public final void drawString(AttributedCharacterIterator iterator,
 				 int x, int y) {
-	// TODO: call validate with bounding box of primitive
+	// XXXX: call validate with bounding box of primitive
 	validate();
 	offScreenGraphics2D.drawString(iterator, x, y);
     }
 
     public final void drawString(AttributedCharacterIterator iterator,
 				 float x, float y) {
-	// TODO: call validate with bounding box of primitive
+	// XXXX: call validate with bounding box of primitive
 	validate();
 	offScreenGraphics2D.drawString(iterator, x, y);
     }
@@ -706,20 +706,20 @@ final class J3DGraphics2DImpl extends J3DGraphics2D {
 
     public final void fillArc(int x, int y, int width, int height,
 			      int startAngle, int arcAngle) {
-	// TODO: call validate with bounding box of primitive
+	// XXXX: call validate with bounding box of primitive
 	validate();
 	offScreenGraphics2D.fillArc(x, y, width, height, startAngle, arcAngle);
     }
 
     public final void fillOval(int x, int y, int width, int height) {
-	// TODO: call validate with bounding box of primitive
+	// XXXX: call validate with bounding box of primitive
 	validate();
 	offScreenGraphics2D.fillOval(x, y, width, height);
     }
 
     public final void fillRoundRect(int x, int y, int width, int height,
 				    int arcWidth, int arcHeight) {
-	// TODO: call validate with bounding box of primitive
+	// XXXX: call validate with bounding box of primitive
 	validate();
 	offScreenGraphics2D.fillRoundRect(x, y, width, height, arcWidth, 
 				      arcHeight);
@@ -810,59 +810,59 @@ final class J3DGraphics2DImpl extends J3DGraphics2D {
 
     public void draw3DRect(int x, int y, int width, int height,
                            boolean raised) {
-	// TODO: call validate with bounding box of primitive
+	// XXXX: call validate with bounding box of primitive
 	validate();
 	offScreenGraphics2D.draw3DRect(x, y, width, height, raised);
     }
 
     public void drawBytes(byte data[], int offset, int length, int x, int y) {
-	// TODO: call validate with bounding box of primitive
+	// XXXX: call validate with bounding box of primitive
 	validate();
 	offScreenGraphics2D.drawBytes(data,  offset, length, x, y);
     }
  
     public void drawChars(char data[], int offset, int length, int x, int y) {
-	// TODO: call validate with bounding box of primitive
+	// XXXX: call validate with bounding box of primitive
 	validate();
 	offScreenGraphics2D.drawChars(data,  offset, length, x, y);
     }
 
     public void drawPolygon(Polygon p) {
-	// TODO: call validate with bounding box of primitive
+	// XXXX: call validate with bounding box of primitive
 	validate();
 	offScreenGraphics2D.drawPolygon(p);
     }
 
     public void drawRect(int x, int y, int width, int height) {
-	// TODO: call validate with bounding box of primitive
-	// TODO: need to consider Stroke width
+	// XXXX: call validate with bounding box of primitive
+	// XXXX: need to consider Stroke width
 	validate();
 	offScreenGraphics2D.drawRect(x, y, width, height);
     }
 
     public void fill3DRect(int x, int y, int width, int height,
                            boolean raised) {
-	// TODO: call validate with bounding box of primitive
-	// TODO: need to consider Stroke width
+	// XXXX: call validate with bounding box of primitive
+	// XXXX: need to consider Stroke width
 	validate();
 	offScreenGraphics2D.fill3DRect(x, y, width, height, raised);
     }
 
     public void fillPolygon(Polygon p) {
-	// TODO: call validate with bounding box of primitive
+	// XXXX: call validate with bounding box of primitive
 	validate();
 	offScreenGraphics2D.fillPolygon(p);
     }
 
     public final void fillPolygon(int xPoints[], int yPoints[],
                                      int nPoints) {
-	// TODO: call validate with bounding box of primitive
+	// XXXX: call validate with bounding box of primitive
 	validate();
 	offScreenGraphics2D.fillPolygon(xPoints, yPoints, nPoints);
     }
 
     public final void fillRect(int x, int y, int width, int height) {
-	// TODO: call validate with bounding box of primitive
+	// XXXX: call validate with bounding box of primitive
 	validate();
 	offScreenGraphics2D.fillRect(x, y, width, height);
     }
@@ -888,7 +888,7 @@ final class J3DGraphics2DImpl extends J3DGraphics2D {
 	    doDrawAndFlushImage(img, x, y, observer);
 	} else { 
 	    // Behavior Scheduler or other threads
-	    // TODO: may not be legal for behaviorScheduler
+	    // XXXX: may not be legal for behaviorScheduler
 	    // May cause deadlock if it is in behaviorScheduler
 	    // and we wait for Renderer to finish
 	    boolean renderRun = (Thread.currentThread() != 
diff --git a/src/classes/share/javax/media/j3d/J3dMessage.java b/src/classes/share/javax/media/j3d/J3dMessage.java
index 625f2df..d2d073d 100644
--- a/src/classes/share/javax/media/j3d/J3dMessage.java
+++ b/src/classes/share/javax/media/j3d/J3dMessage.java
@@ -84,6 +84,9 @@ class J3dMessage extends Object {
     static final int ORDERED_GROUP_TABLE_CHANGED    = 59;
     static final int BEHAVIOR_REEVALUATE            = 60;
     static final int CREATE_OFFSCREENBUFFER	    = 61;
+    static final int SHADER_ATTRIBUTE_CHANGED       = 62;
+    static final int SHADER_ATTRIBUTE_SET_CHANGED   = 63;
+    static final int SHADER_APPEARANCE_CHANGED 	    = 64;    
 
     /**
      * This is the time snapshot at which this change occured
diff --git a/src/classes/share/javax/media/j3d/J3dNotification.java b/src/classes/share/javax/media/j3d/J3dNotification.java
new file mode 100644
index 0000000..8560295
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/J3dNotification.java
@@ -0,0 +1,42 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * J3dNotification is used to hold data for asynchronous error notification.
+ */
+
+class J3dNotification extends Object {
+    /**
+     * The various notification types.
+     */
+    static final int INVALID_TYPE       = -1;
+    static final int SHADER_ERROR       =  0;
+
+    /**
+     * This holds the type of this message
+     */
+    int type = INVALID_TYPE;
+
+    /**
+     * The universe that this message originated from
+     */
+    VirtualUniverse universe;
+
+    /**
+     * The arguements for a message, 6 for now
+     */
+    static final int MAX_ARGS = 6;
+
+    Object[] args = new Object[MAX_ARGS];
+}
diff --git a/src/classes/share/javax/media/j3d/Light.java b/src/classes/share/javax/media/j3d/Light.java
index 2588607..47c479a 100644
--- a/src/classes/share/javax/media/j3d/Light.java
+++ b/src/classes/share/javax/media/j3d/Light.java
@@ -220,6 +220,14 @@ public abstract class Light extends Leaf {
     public static final int
     ALLOW_SCOPE_WRITE = CapabilityBits.LIGHT_ALLOW_SCOPE_WRITE;
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_STATE_READ,
+        ALLOW_COLOR_READ,
+        ALLOW_INFLUENCING_BOUNDS_READ,
+        ALLOW_SCOPE_READ
+    };
+    
     /**
      * Constructs a Light node with default parameters.  The default
      * values are as follows:
@@ -232,6 +240,8 @@ public abstract class Light extends Leaf {
      * </ul>
      */
     public Light() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -239,7 +249,10 @@ public abstract class Light extends Leaf {
      * @param color the color of the light source
      */
     public Light(Color3f color) {
-	((LightRetained)this.retained).initColor(color);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((LightRetained)this.retained).initColor(color);
     }
 
     /**
@@ -249,7 +262,10 @@ public abstract class Light extends Leaf {
      * @param color the color of the light source
      */
     public Light(boolean lightOn, Color3f color) {
-	((LightRetained)this.retained).initEnable(lightOn);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((LightRetained)this.retained).initEnable(lightOn);
 	((LightRetained)this.retained).initColor(color);
     }
 
diff --git a/src/classes/share/javax/media/j3d/LineArray.java b/src/classes/share/javax/media/j3d/LineArray.java
index f2d0e12..da484d1 100644
--- a/src/classes/share/javax/media/j3d/LineArray.java
+++ b/src/classes/share/javax/media/j3d/LineArray.java
@@ -27,23 +27,22 @@ public class LineArray extends GeometryArray {
     }
 
     /**
-     * Constructs an empty LineArray object with the specified
-     * number of vertices, and vertex format.
-     * @param vertexCount the number of vertex elements in this array
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+     * Constructs an empty LineArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
      * @exception IllegalArgumentException if vertexCount is less than 2
      * or vertexCount is <i>not</i> a multiple of 2
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int)}
+     * for more exceptions that can be thrown
      */
     public LineArray(int vertexCount, int vertexFormat) {
 	super(vertexCount,vertexFormat);
@@ -53,60 +52,30 @@ public class LineArray extends GeometryArray {
     }
 
     /**
-     * Constructs an empty LineArray object with the specified
-     * number of vertices, and vertex format, number of texture coordinate
-     * sets, and texture coordinate mapping array.
-     *
-     * @param vertexCount the number of vertex elements in this array<p>
-     *
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
-     *
-     * @param texCoordSetCount the number of texture coordinate sets
-     * in this GeometryArray object.  If <code>vertexFormat</code>
-     * does not include one of <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetCount</code> parameter is not used.<p>
-     *
-     * @param texCoordSetMap an array that maps texture coordinate
-     * sets to texture units.  The array is indexed by texture unit
-     * number for each texture unit in the associated Appearance
-     * object.  The values in the array specify the texture coordinate
-     * set within this GeometryArray object that maps to the
-     * corresponding texture
-     * unit.  All elements within the array must be less than
-     * <code>texCoordSetCount</code>.  A negative value specifies that
-     * no texture coordinate set maps to the texture unit
-     * corresponding to the index.  If there are more texture units in
-     * any associated Appearance object than elements in the mapping
-     * array, the extra elements are assumed to be -1.  The same
-     * texture coordinate set may be used for more than one texture
-     * unit.  Each texture unit in every associated Appearance must
-     * have a valid source of texture coordinates: either a
-     * non-negative texture coordinate set must be specified in the
-     * mapping array or texture coordinate generation must be enabled.
-     * Texture coordinate generation will take precedence for those
-     * texture units for which a texture coordinate set is specified
-     * and texture coordinate generation is enabled.  If
-     * <code>vertexFormat</code> does not include one of
-     * <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetMap</code> array is not used.
+     * Constructs an empty LineArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
      *
      * @exception IllegalArgumentException if vertexCount is less than 2
      * or vertexCount is <i>not</i> a multiple of 2
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for more exceptions that can be thrown
      *
      * @since Java 3D 1.2
      */
@@ -122,6 +91,53 @@ public class LineArray extends GeometryArray {
 	    throw new IllegalArgumentException(J3dI18N.getString("LineArray0"));
     }
 
+    /**
+     * Constructs an empty LineArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrSizes
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @exception IllegalArgumentException if vertexCount is less than 2
+     * or vertexCount is <i>not</i> a multiple of 3
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for more exceptions that can be thrown
+     *
+     * @since Java 3D 1.4
+     */
+    public LineArray(int vertexCount,
+		     int vertexFormat,
+		     int texCoordSetCount,
+		     int[] texCoordSetMap,
+		     int vertexAttrCount,
+		     int[] vertexAttrSizes) {
+
+	super(vertexCount, vertexFormat,
+	      texCoordSetCount, texCoordSetMap,
+	      vertexAttrCount, vertexAttrSizes);
+
+        if (vertexCount < 2 || ((vertexCount%2) != 0))
+	    throw new IllegalArgumentException(J3dI18N.getString("LineArray0"));
+    }
+
     /**
      * Creates the retained mode LineArrayRetained object that this
      * LineArray object will point to.
@@ -137,23 +153,24 @@ public class LineArray extends GeometryArray {
      */
     public NodeComponent cloneNodeComponent() {
 	LineArrayRetained rt = (LineArrayRetained) retained;
-
-
-	int texSetCount = rt.getTexCoordSetCount();
-	LineArray l;
-	if (texSetCount == 0) {
-	    l = new LineArray(rt.getVertexCount(), 
-			      rt.getVertexFormat());
-	} else {
-	    int texMap[] = new int[rt.getTexCoordSetMapLength()];
-	    rt.getTexCoordSetMap(texMap);
-	    l = new LineArray(rt.getVertexCount(), 
-			      rt.getVertexFormat(),
-			      texSetCount,
-			      texMap);
-
-	}
-
+        int texSetCount = rt.getTexCoordSetCount();
+        int[] texMap = null;
+        int vertexAttrCount = rt.getVertexAttrCount();
+        int[] vertexAttrSizes = null;
+        if (texSetCount > 0) {
+            texMap = new int[rt.getTexCoordSetMapLength()];
+            rt.getTexCoordSetMap(texMap);
+        }
+        if (vertexAttrCount > 0) {
+            vertexAttrSizes = new int[vertexAttrCount];
+            rt.getVertexAttrSizes(vertexAttrSizes);
+        }
+        LineArray l = new LineArray(rt.getVertexCount(),
+                rt.getVertexFormat(),
+                texSetCount,
+                texMap, 
+                vertexAttrCount,
+                vertexAttrSizes);
 	l.duplicateNodeComponent(this);
         return l;
      }
diff --git a/src/classes/share/javax/media/j3d/LineArrayRetained.java b/src/classes/share/javax/media/j3d/LineArrayRetained.java
index b29597a..2b07b54 100644
--- a/src/classes/share/javax/media/j3d/LineArrayRetained.java
+++ b/src/classes/share/javax/media/j3d/LineArrayRetained.java
@@ -25,15 +25,16 @@ class LineArrayRetained extends GeometryArrayRetained implements Cloneable {
     LineArrayRetained() {
 	this.geoType = GEO_TYPE_LINE_SET;
     }
-  
-    boolean intersect(PickShape pickShape, double dist[],  Point3d iPnt) {
+ 
+    boolean intersect(PickShape pickShape, PickInfo.IntersectionInfo iInfo,  int flags, Point3d iPnt) {
 	Point3d pnts[] = new Point3d[2];
 	double sdist[] = new double[1];
 	double minDist = Double.MAX_VALUE;
 	double x = 0, y = 0, z = 0;
+        int count = 0;
+        int minICount = 0; 
 	int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
 		 initialVertexIndex : initialCoordIndex);
-
 	pnts[0] = new Point3d();
 	pnts[1] = new Point3d();
     
@@ -44,18 +45,20 @@ class LineArrayRetained extends GeometryArrayRetained implements Cloneable {
 	    while (i < validVertexCount) {
 		getVertexData(i++, pnts[0]);
 		getVertexData(i++, pnts[1]);
+                count += 2;
 		if (intersectLineAndRay(pnts[0], pnts[1], pickRay.origin,
 					pickRay.direction, sdist,
 					iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
-		    }
+                    }
 		}
 	    }
 	    break;
@@ -69,20 +72,21 @@ class LineArrayRetained extends GeometryArrayRetained implements Cloneable {
 	    while (i < validVertexCount) {
 		getVertexData(i++, pnts[0]);
 		getVertexData(i++, pnts[1]);
+                count += 2;
 		if (intersectLineAndRay(pnts[0], pnts[1],
 					pickSegment.start, 
 					dir, sdist, iPnt) &&
 		    (sdist[0] <= 1.0)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
 		    }
-
 		}
 	    }
 	    break;
@@ -92,12 +96,14 @@ class LineArrayRetained extends GeometryArrayRetained implements Cloneable {
 	    while (i < validVertexCount) {
 		getVertexData(i++, pnts[0]);
 		getVertexData(i++, pnts[1]);
+                count += 2;
 		if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -113,12 +119,14 @@ class LineArrayRetained extends GeometryArrayRetained implements Cloneable {
 	    while (i < validVertexCount) {
 		getVertexData(i++, pnts[0]);
 		getVertexData(i++, pnts[1]);
+                count += 2;
 		if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -133,12 +141,14 @@ class LineArrayRetained extends GeometryArrayRetained implements Cloneable {
 	    while (i < validVertexCount) {
 		getVertexData(i++, pnts[0]);
 		getVertexData(i++, pnts[1]);
+                count += 2;
 		if (intersectBoundingPolytope(pnts, bpolytope, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -152,12 +162,14 @@ class LineArrayRetained extends GeometryArrayRetained implements Cloneable {
 	    while (i < validVertexCount) {
 		getVertexData(i++, pnts[0]);
 		getVertexData(i++, pnts[1]);
+                count += 2;
 		if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -171,12 +183,14 @@ class LineArrayRetained extends GeometryArrayRetained implements Cloneable {
 	    while (i < validVertexCount) {
 		getVertexData(i++, pnts[0]);
 		getVertexData(i++, pnts[1]);
+                count += 2;
 		if (intersectCone(pnts, pickCone, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -192,7 +206,14 @@ class LineArrayRetained extends GeometryArrayRetained implements Cloneable {
 	} 
 
 	if (minDist < Double.MAX_VALUE) {
-	    dist[0] = minDist;
+            assert(minICount >=2);
+            int[] vertexIndices = iInfo.getVertexIndices();
+            if (vertexIndices == null) {
+                vertexIndices = new int[2];
+                iInfo.setVertexIndices(vertexIndices);
+            }
+            vertexIndices[0] = minICount - 2;
+            vertexIndices[1] = minICount - 1;
 	    iPnt.x = x;
 	    iPnt.y = y;
 	    iPnt.z = z;
@@ -200,8 +221,7 @@ class LineArrayRetained extends GeometryArrayRetained implements Cloneable {
 	}
 	return false;
    
-    }
-  
+    }    
 
     boolean intersect(Point3d[] pnts) {
 	Point3d[] points = new Point3d[2];
diff --git a/src/classes/share/javax/media/j3d/LineAttributes.java b/src/classes/share/javax/media/j3d/LineAttributes.java
index ca2d569..c60a68c 100644
--- a/src/classes/share/javax/media/j3d/LineAttributes.java
+++ b/src/classes/share/javax/media/j3d/LineAttributes.java
@@ -169,7 +169,13 @@ public class LineAttributes extends NodeComponent {
      */
     public static final int PATTERN_USER_DEFINED = 4;
 
-
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_ANTIALIASING_READ,
+        ALLOW_PATTERN_READ,
+        ALLOW_WIDTH_READ        
+    };
+    
     /**
      * Constructs a LineAttributes object with default parameters.
      * The default values are as follows:
@@ -182,6 +188,8 @@ public class LineAttributes extends NodeComponent {
      * </ul>
      */
      public LineAttributes(){
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
       }
 
     /**
@@ -197,6 +205,9 @@ public class LineAttributes extends NodeComponent {
        if (linePattern < PATTERN_SOLID || linePattern > PATTERN_DASH_DOT)
 	 throw new IllegalArgumentException(J3dI18N.getString("LineAttributes0"));
 
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
        ((LineAttributesRetained)this.retained).initLineWidth(lineWidth);
        ((LineAttributesRetained)this.retained).initLinePattern(linePattern);
        ((LineAttributesRetained)this.retained).initLineAntialiasingEnable(lineAntialiasing);
diff --git a/src/classes/share/javax/media/j3d/LineStripArray.java b/src/classes/share/javax/media/j3d/LineStripArray.java
index ef9ce5a..00581d2 100644
--- a/src/classes/share/javax/media/j3d/LineStripArray.java
+++ b/src/classes/share/javax/media/j3d/LineStripArray.java
@@ -30,28 +30,26 @@ public class LineStripArray extends GeometryStripArray {
     }
 
     /**
-     * Constructs an empty LineStripArray object with the specified
-     * number of vertices, vertex format, and
-     * array of per-strip vertex counts.
-     * @param vertexCount the number of vertex elements in this array
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
-     * @param stripVertexCounts array that specifies
-     * the count of the number of vertices for each separate strip.
-     * The length of this array is the number of separate strips.
+     * Constructs an empty LineStripArray object using the
+     * specified parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param stripVertexCounts
+     * see {@link GeometryStripArray#GeometryStripArray(int,int,int[])}
+     * for a description of this parameter.
      *
      * @exception IllegalArgumentException if vertexCount is less than 2
      * or any element in the stripVertexCounts array is less than 2
+     * ;<br>
+     * See {@link GeometryStripArray#GeometryStripArray(int,int,int[])}
+     * for more exceptions that can be thrown
      */
     public LineStripArray(int vertexCount,
 			  int vertexFormat,
@@ -64,65 +62,34 @@ public class LineStripArray extends GeometryStripArray {
     }
 
     /**
-     * Constructs an empty LineStripArray object with the specified
-     * number of vertices, vertex format, number of texture coordinate
-     * sets, texture coordinate mapping array, and
-     * array of per-strip vertex counts.
-     *
-     * @param vertexCount the number of vertex elements in this array<p>
-     *
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
-     *
-     * @param texCoordSetCount the number of texture coordinate sets
-     * in this GeometryArray object.  If <code>vertexFormat</code>
-     * does not include one of <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetCount</code> parameter is not used.<p>
-     *
-     * @param texCoordSetMap an array that maps texture coordinate
-     * sets to texture units.  The array is indexed by texture unit
-     * number for each texture unit in the associated Appearance
-     * object.  The values in the array specify the texture coordinate
-     * set within this GeometryArray object that maps to the
-     * corresponding texture
-     * unit.  All elements within the array must be less than
-     * <code>texCoordSetCount</code>.  A negative value specifies that
-     * no texture coordinate set maps to the texture unit
-     * corresponding to the index.  If there are more texture units in
-     * any associated Appearance object than elements in the mapping
-     * array, the extra elements are assumed to be -1.  The same
-     * texture coordinate set may be used for more than one texture
-     * unit.  Each texture unit in every associated Appearance must
-     * have a valid source of texture coordinates: either a
-     * non-negative texture coordinate set must be specified in the
-     * mapping array or texture coordinate generation must be enabled.
-     * Texture coordinate generation will take precedence for those
-     * texture units for which a texture coordinate set is specified
-     * and texture coordinate generation is enabled.  If
-     * <code>vertexFormat</code> does not include one of
-     * <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetMap</code> array is not used.<p>
-     *
-     * @param stripVertexCounts array that specifies
-     * the count of the number of vertices for each separate strip.
-     * The length of this array is the number of separate strips.
+     * Constructs an empty LineStripArray object using the
+     * specified parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param stripVertexCounts
+     * see {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int[])}
+     * for a description of this parameter.
      *
      * @exception IllegalArgumentException if vertexCount is less than 2
      * or any element in the stripVertexCounts array is less than 2
+     * ;<br>
+     * See {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int[])}
+     * for more exceptions that can be thrown
      *
      * @since Java 3D 1.2
      */
@@ -140,6 +107,59 @@ public class LineStripArray extends GeometryStripArray {
 	    throw new IllegalArgumentException(J3dI18N.getString("LineStripArray0"));
     }
 
+    /**
+     * Constructs an empty LineStripArray object using the
+     * specified parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrSizes
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param stripVertexCounts
+     * see {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int,int[],int[])}
+     * for a description of this parameter.
+     *
+     * @exception IllegalArgumentException if vertexCount is less than 2
+     * or any element in the stripVertexCounts array is less than 2
+     * ;<br>
+     * See {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int,int[],int[])}
+     * for more exceptions that can be thrown
+     *
+     * @since Java 3D 1.4
+     */
+    public LineStripArray(int vertexCount,
+			  int vertexFormat,
+			  int texCoordSetCount,
+			  int[] texCoordSetMap,
+			  int vertexAttrCount,
+			  int[] vertexAttrSizes,
+			  int[] stripVertexCounts) {
+
+	super(vertexCount, vertexFormat,
+	      texCoordSetCount, texCoordSetMap,
+	      vertexAttrCount, vertexAttrSizes,
+	      stripVertexCounts);
+
+        if (vertexCount < 2 )
+	    throw new IllegalArgumentException(J3dI18N.getString("LineStripArray0"));
+    }
+
     /**
      * Creates the retained mode LineStripArrayRetained object that this
      * LineStripArray object will point to.
@@ -153,24 +173,28 @@ public class LineStripArray extends GeometryStripArray {
      * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
      */
     public NodeComponent cloneNodeComponent() {
-	LineStripArrayRetained rt = (LineStripArrayRetained) retained;
+        LineStripArrayRetained rt = (LineStripArrayRetained) retained;
         int stripcounts[] = new int[rt.getNumStrips()];
-	rt.getStripVertexCounts(stripcounts);
-	int texSetCount = rt.getTexCoordSetCount();
-	LineStripArray l;
-	if (texSetCount == 0) {
-	    l = new LineStripArray(rt.getVertexCount(), 
-				   rt.getVertexFormat(),
-				   stripcounts);
-	} else {
-	    int texMap[] = new int[rt.getTexCoordSetMapLength()];
-	    rt.getTexCoordSetMap(texMap);
-	    l = new LineStripArray(rt.getVertexCount(), 
-				   rt.getVertexFormat(),
-				   texSetCount,
-				   texMap,
-				   stripcounts);
-	}
+        rt.getStripVertexCounts(stripcounts);
+        int texSetCount = rt.getTexCoordSetCount();
+        int[] texMap = null;
+        int vertexAttrCount = rt.getVertexAttrCount();
+        int[] vertexAttrSizes = null;
+        if (texSetCount > 0) {
+            texMap = new int[rt.getTexCoordSetMapLength()];
+            rt.getTexCoordSetMap(texMap);
+        }
+        if (vertexAttrCount > 0) {
+            vertexAttrSizes = new int[vertexAttrCount];
+            rt.getVertexAttrSizes(vertexAttrSizes);
+        }
+        LineStripArray l = new LineStripArray(rt.getVertexCount(),
+                rt.getVertexFormat(),
+                texSetCount,
+                texMap,
+                vertexAttrCount,
+                vertexAttrSizes,
+                stripcounts);
         l.duplicateNodeComponent(this);
         return l;
      }
diff --git a/src/classes/share/javax/media/j3d/LineStripArrayRetained.java b/src/classes/share/javax/media/j3d/LineStripArrayRetained.java
index 4270d74..69fa80b 100644
--- a/src/classes/share/javax/media/j3d/LineStripArrayRetained.java
+++ b/src/classes/share/javax/media/j3d/LineStripArrayRetained.java
@@ -30,14 +30,15 @@ class LineStripArrayRetained extends GeometryStripArrayRetained {
 	this.geoType = GEO_TYPE_LINE_STRIP_SET;
     }
 
-    boolean intersect(PickShape pickShape, double dist[],  Point3d iPnt) {
+    boolean intersect(PickShape pickShape, PickInfo.IntersectionInfo iInfo,  int flags, Point3d iPnt) {
 	Point3d pnts[] = new Point3d[2];
 	double sdist[] = new double[1];
 	double minDist = Double.MAX_VALUE;
 	double x = 0, y = 0, z = 0;
 	int j, end;
 	int i = 0;
-
+        int count = 0;
+        int minICount = 0; 
 	pnts[0] = new Point3d();
 	pnts[1] = new Point3d();
 
@@ -49,20 +50,22 @@ class LineStripArrayRetained extends GeometryStripArrayRetained {
 		j = stripStartVertexIndices[i];
 		end = j + stripVertexCounts[i++];
 		getVertexData(j++, pnts[0]);
+                count++;
 		while (j < end) {
 		    getVertexData(j++, pnts[1]);
-
+                    count++;
 		    if (intersectLineAndRay(pnts[0], pnts[1], pickRay.origin,
 					    pickRay.direction, sdist,
 					    iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);
@@ -80,21 +83,23 @@ class LineStripArrayRetained extends GeometryStripArrayRetained {
 		j = stripStartVertexIndices[i];
 		end = j + stripVertexCounts[i++];
 		getVertexData(j++, pnts[0]);
-		while (j < end) {
+                count++;
+                while (j < end) {
 		    getVertexData(j++, pnts[1]);
-
+                    count++;
 		    if (intersectLineAndRay(pnts[0], pnts[1],
 					    pickSegment.start, 
 					    dir, sdist, iPnt) &&
 			(sdist[0] <= 1.0)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);			
@@ -109,17 +114,20 @@ class LineStripArrayRetained extends GeometryStripArrayRetained {
 		j = stripStartVertexIndices[i];
 		end = j + stripVertexCounts[i++];
 		getVertexData(j++, pnts[0]);
+                count++;
 		while (j < end) {	 
 		    getVertexData(j++, pnts[1]);
+                    count++;
 		    if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);			   
@@ -135,17 +143,20 @@ class LineStripArrayRetained extends GeometryStripArrayRetained {
 		j = stripStartVertexIndices[i];
 		end = j + stripVertexCounts[i++];
 		getVertexData(j++, pnts[0]);
+                count++;
 		while (j < end) {
 		    getVertexData(j++, pnts[1]);
+                    count++;
 		    if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);
@@ -160,17 +171,20 @@ class LineStripArrayRetained extends GeometryStripArrayRetained {
 		j = stripStartVertexIndices[i];
 		end = j + stripVertexCounts[i++];
 		getVertexData(j++, pnts[0]);
+                count++;
 		while (j < end) {
 		    getVertexData(j++, pnts[1]);
+                    count++;
 		    if (intersectBoundingPolytope(pnts, bpolytope, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);
@@ -184,18 +198,20 @@ class LineStripArrayRetained extends GeometryStripArrayRetained {
 		j = stripStartVertexIndices[i];
 		end = j + stripVertexCounts[i++];
 		getVertexData(j++, pnts[0]);
+                count++;
 		while (j < end) {
 		    getVertexData(j++, pnts[1]);
-
+                    count++;
 		    if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);
@@ -209,17 +225,20 @@ class LineStripArrayRetained extends GeometryStripArrayRetained {
 		j = stripStartVertexIndices[i];
 		end = j + stripVertexCounts[i++];
 		getVertexData(j++, pnts[0]);
+                count++;
 		while (j < end) {
 		    getVertexData(j++, pnts[1]);
+                    count++;
 		    if (intersectCone(pnts, pickCone, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[0].set(pnts[1]);
@@ -234,14 +253,20 @@ class LineStripArrayRetained extends GeometryStripArrayRetained {
 	} 
 
 	if (minDist < Double.MAX_VALUE) {
-	    dist[0] = minDist;
+            assert(minICount >= 2);
+            int[] vertexIndices = iInfo.getVertexIndices();
+            if (vertexIndices == null) {
+                vertexIndices = new int[2];
+                iInfo.setVertexIndices(vertexIndices);
+            }
+            vertexIndices[0] = minICount - 2;
+            vertexIndices[1] = minICount - 1;
 	    iPnt.x = x;
 	    iPnt.y = y;
 	    iPnt.z = z;
 	    return true;
 	}
-	return false;
-   
+	return false;  
     }
   
     boolean intersect(Point3d[] pnts) {
diff --git a/src/classes/share/javax/media/j3d/LinearFog.java b/src/classes/share/javax/media/j3d/LinearFog.java
index 5232d32..4aa543f 100644
--- a/src/classes/share/javax/media/j3d/LinearFog.java
+++ b/src/classes/share/javax/media/j3d/LinearFog.java
@@ -25,14 +25,16 @@ import javax.vecmath.Color3f;
  * the node, but the actual fog equation will ideally take place in eye
  * coordinates.
  * <P>
- * The linear fog blending factor, f, is computed as follows:
- * <P><UL>
- * f = backDistance - z / backDistance - frontDistance<P>
- * where
- * <ul>z is the distance from the viewpoint.<br>
- * frontDistance is the distance at which fog starts obscuring objects.<br>
- * backDistance is the distance at which fog totally obscurs objects.
- * </ul><P></UL>
+ * The linear fog blending factor, <code>f</code>, is computed as follows:
+ * <ul>
+ * <code>f = (backDistance - z) / (backDistance - frontDistance)</code>
+ * </ul>
+ * where:
+ * <ul>
+ * <code>z</code> is the distance from the viewpoint.<br>
+ * <code>frontDistance</code> is the distance at which fog starts obscuring objects.<br>
+ * <code>backDistance</code> is the distance at which fog totally obscurs objects.
+ * </ul>
  */
 public class LinearFog extends Fog {
     /**
@@ -49,6 +51,11 @@ public class LinearFog extends Fog {
     public static final int
     ALLOW_DISTANCE_WRITE = CapabilityBits.LINEAR_FOG_ALLOW_DISTANCE_WRITE;
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+	ALLOW_DISTANCE_READ
+    };
+
     /**
      * Constructs a LinearFog node with default parameters.
      * The default values are as follows:
@@ -59,6 +66,8 @@ public class LinearFog extends Fog {
      */
     public LinearFog() {
 	// Just use the defaults
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -67,6 +76,9 @@ public class LinearFog extends Fog {
      */
     public LinearFog(Color3f color) {
 	super(color);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -77,6 +89,10 @@ public class LinearFog extends Fog {
      */
     public LinearFog(Color3f color, double frontDistance, double backDistance) {
 	super(color);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
 	((LinearFogRetained)this.retained).initFrontDistance(frontDistance);
 	((LinearFogRetained)this.retained).initBackDistance(backDistance);
     }
@@ -89,6 +105,9 @@ public class LinearFog extends Fog {
      */
     public LinearFog(float r, float g, float b) {
 	super(r, g, b);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -102,6 +121,10 @@ public class LinearFog extends Fog {
     public LinearFog(float r, float g, float b,
 		     double frontDistance, double backDistance) {
 	super(r, g, b);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
 	((LinearFogRetained)this.retained).initFrontDistance(frontDistance);
 	((LinearFogRetained)this.retained).initBackDistance(backDistance);
     }
diff --git a/src/classes/share/javax/media/j3d/LinearFogRetained.java b/src/classes/share/javax/media/j3d/LinearFogRetained.java
index 4f7b723..4eff1ec 100644
--- a/src/classes/share/javax/media/j3d/LinearFogRetained.java
+++ b/src/classes/share/javax/media/j3d/LinearFogRetained.java
@@ -24,12 +24,10 @@ class LinearFogRetained extends FogRetained {
     /**
      * Fog front and back   distance
      */
-    double frontDistance = 0.1;
-    double backDistance = 1.0;
-    double localToVworldScale = 1.0;
-    double frontDistanceInEc;
-    double backDistanceInEc;
-    double vworldToCoexistenceScale;
+    private double frontDistance = 0.1;
+    private double backDistance = 1.0;
+    private double frontDistanceInEc;
+    private double backDistanceInEc;
 
     // dirty bits for LinearFog
     static final int FRONT_DISTANCE_CHANGED	= FogRetained.LAST_DEFINED_BIT << 1;
@@ -169,29 +167,21 @@ class LinearFogRetained extends FogRetained {
 	    ((LinearFogRetained)mirrorFog).backDistance = ((Double)((Object[])objs[4])[5]).doubleValue();
 	    
 	}
-	((LinearFogRetained)mirrorFog).localToVworldScale = 
-	    getLastLocalToVworld().getDistanceScale();	
-
+	((LinearFogRetained)mirrorFog).setLocalToVworldScale(getLastLocalToVworld().getDistanceScale());	
 
 	super.updateMirrorObject(objs);
     }
 
     /**
      * Scale distances from local to eye coordinate
-     */  
-
-    void validateDistancesInEc(double vworldToCoexistenceScale) {
+     */
+    protected void validateDistancesInEc(double vworldToCoexistenceScale) {
         // vworldToCoexistenceScale can be used here since
         // CoexistenceToEc has a unit scale
-        double localToEcScale = localToVworldScale * vworldToCoexistenceScale;
+        double localToEcScale = getLocalToVworldScale() * vworldToCoexistenceScale;
 
         frontDistanceInEc = frontDistance * localToEcScale;
         backDistanceInEc = backDistance * localToEcScale;
     }
 
-    // Called on mirror object
-    void updateTransformChange() {
-	super.updateTransformChange();
-        localToVworldScale = sgFog.getLastLocalToVworld().getDistanceScale();
-    }
 }
diff --git a/src/classes/share/javax/media/j3d/Link.java b/src/classes/share/javax/media/j3d/Link.java
index 151952c..549c3aa 100644
--- a/src/classes/share/javax/media/j3d/Link.java
+++ b/src/classes/share/javax/media/j3d/Link.java
@@ -34,11 +34,18 @@ public class Link extends Leaf {
     public static final int
     ALLOW_SHARED_GROUP_WRITE = CapabilityBits.LINK_ALLOW_SHARED_GROUP_WRITE;
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_SHARED_GROUP_READ
+    };
+    
     /**
      * Constructs a Link node object that does not yet point to a
      * SharedGroup node.
      */
     public Link() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -47,7 +54,10 @@ public class Link extends Leaf {
      * @param sharedGroup the SharedGroup node
      */
     public Link(SharedGroup sharedGroup) {
-	((LinkRetained)this.retained).setSharedGroup(sharedGroup);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((LinkRetained)this.retained).setSharedGroup(sharedGroup);
     }
 
     /**
diff --git a/src/classes/share/javax/media/j3d/LinkRetained.java b/src/classes/share/javax/media/j3d/LinkRetained.java
index f9dafb5..9ba765c 100644
--- a/src/classes/share/javax/media/j3d/LinkRetained.java
+++ b/src/classes/share/javax/media/j3d/LinkRetained.java
@@ -293,7 +293,7 @@ class LinkRetained extends LeafRetained {
 
 	super.compile(compState);
 
-        // TODO: for now keep the static transform in the parent tg
+        // XXXX: for now keep the static transform in the parent tg
         compState.keepTG = true;
 
         // don't remove this group node
diff --git a/src/classes/share/javax/media/j3d/Locale.java b/src/classes/share/javax/media/j3d/Locale.java
index ecb4c86..157cbde 100644
--- a/src/classes/share/javax/media/j3d/Locale.java
+++ b/src/classes/share/javax/media/j3d/Locale.java
@@ -158,6 +158,7 @@ public class Locale extends Object {
 	    throw new MultipleParentException(J3dI18N.getString("Locale0"));
         }
 
+        universe.notifyStructureChangeListeners(true, this, branchGroup);
 	universe.resetWaitMCFlag();
 	synchronized (universe.sceneGraphLock) {
 	    doAddBranchGraph(branchGroup);
@@ -377,6 +378,7 @@ public class Locale extends Object {
 					   universe);
 	}
 	universe.setLiveState.reset(null); // cleanup memory
+        universe.notifyStructureChangeListeners(false, this, branchGroup);
     }
 
     /**
@@ -408,10 +410,12 @@ public class Locale extends Object {
 	    throw new MultipleParentException(J3dI18N.getString("Locale3"));
         }
 	universe.resetWaitMCFlag();
+        universe.notifyStructureChangeListeners(true, this, newGroup);
 	synchronized (universe.sceneGraphLock) {
 	    doReplaceBranchGraph(oldGroup, newGroup);
 	    universe.setLiveState.reset(this);
 	}
+        universe.notifyStructureChangeListeners(false, this, oldGroup);
 	universe.waitForMC();
     }
 
@@ -494,7 +498,7 @@ public class Locale extends Object {
 	createMessage.args[4] = universe.setLiveState.ogCIOTableList.toArray();
 	VirtualUniverse.mc.processMessage(createMessage);
 
-	// TODO: make these two into one message
+	// XXXX: make these two into one message
 	createMessage = VirtualUniverse.mc.getMessage();
         createMessage.threads = universe.setLiveState.notifyThreads;
         createMessage.type = J3dMessage.INSERT_NODES;
@@ -525,11 +529,8 @@ public class Locale extends Object {
         createMessage.universe = universe;
         VirtualUniverse.mc.processMessage(createMessage);
 
-
 	// Free up memory.
 	universe.setLiveState.reset(null);
-
-
     }
 
     /**
@@ -553,7 +554,47 @@ public class Locale extends Object {
 
 	return branchGroups.elements();
     }
+
     
+    void validateModeFlagAndPickShape(int mode, int flags, PickShape pickShape) {
+
+        if (universe == null) {
+	    throw new IllegalStateException(J3dI18N.getString("Locale4"));
+	}
+        
+        if((mode != PickInfo.PICK_BOUNDS) && (mode != PickInfo.PICK_GEOMETRY)) {
+          
+          throw new IllegalArgumentException(J3dI18N.getString("Locale5"));
+        }
+        
+        if((pickShape instanceof PickPoint) && (mode == PickInfo.PICK_GEOMETRY)) {
+          throw new IllegalArgumentException(J3dI18N.getString("Locale6"));
+        }
+        
+        if(((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) &&
+                ((flags & PickInfo.ALL_GEOM_INFO) != 0)) {
+            throw new IllegalArgumentException(J3dI18N.getString("Locale7"));
+        }
+        
+        if((mode == PickInfo.PICK_BOUNDS) && 
+                (((flags & (PickInfo.CLOSEST_GEOM_INFO | 
+                            PickInfo.ALL_GEOM_INFO |
+                            PickInfo.CLOSEST_DISTANCE |
+                            PickInfo.CLOSEST_INTERSECTION_POINT)) != 0))) {
+          
+          throw new IllegalArgumentException(J3dI18N.getString("Locale8"));
+        }
+  
+        if((pickShape instanceof PickBounds) && 
+                (((flags & (PickInfo.CLOSEST_GEOM_INFO | 
+                            PickInfo.ALL_GEOM_INFO |
+                            PickInfo.CLOSEST_DISTANCE |
+                            PickInfo.CLOSEST_INTERSECTION_POINT)) != 0))) {
+          
+          throw new IllegalArgumentException(J3dI18N.getString("Locale9"));
+        }        
+    }
+
     /**
      * Returns an array referencing all the items that are pickable below this
      * <code>Locale</code> that intersect with PickShape.
@@ -571,12 +612,103 @@ public class Locale extends Object {
 	    throw new IllegalStateException(J3dI18N.getString("Locale4"));
 	}
 
-	return Picking.pickAll( this, pickShape );
+        PickInfo[] pickInfoArr = pickAll( PickInfo.PICK_BOUNDS,
+                PickInfo.SCENEGRAPHPATH, pickShape);
+        
+       if(pickInfoArr == null) {
+            return null;
+       }
+        SceneGraphPath[] sgpArr = new SceneGraphPath[pickInfoArr.length];
+        for( int i=0; i<sgpArr.length; i++) {
+            sgpArr[i] = pickInfoArr[i].getSceneGraphPath();
+        }
+
+        return sgpArr;
+    
     }
 
 
     /**
-     * Returns a sorted array of references to all the Pickable items
+     * Returns an array unsorted references to all the PickInfo objects that are pickable 
+     * below this <code>Locale</code> that intersect with PickShape. 
+     * The accuracy of the pick is set by the pick mode. The mode include : 
+     * PickInfo.PICK_BOUNDS and PickInfo.PICK_GEOMETRY. The amount of information returned 
+     * is specified via a masked variable, flags, indicating which components are 
+     * present in each returned PickInfo object. 
+     *
+     * @param mode  picking mode, one of <code>PickInfo.PICK_BOUNDS</code> or <code>PickInfo.PICK_GEOMETRY</code>.
+     *
+     * @param flags a mask indicating which components are present in each PickInfo object.  
+     * This is specified as one or more individual bits that are bitwise "OR"ed together to 
+     * describe the PickInfo data. The flags include :
+     * <ul>
+     * <code>PickInfo.SCENEGRAPHPATH</code> - request for computed SceneGraphPath.<br>    
+     * <code>PickInfo.NODE</code> - request for computed intersected Node.<br>
+     * <code>PickInfo.LOCAL_TO_VWORLD</code> - request for computed local to virtual world transform.<br>
+     * <code>PickInfo.CLOSEST_INTERSECTION_POINT</code> - request for closest intersection point.<br>
+     * <code>PickInfo.CLOSEST_DISTANCE</code> - request for the distance of closest intersection.<br>
+     * <code>PickInfo.CLOSEST_GEOM_INFO</code> - request for only the closest intersection geometry information.<br>
+     * <code>PickInfo.ALL_GEOM_INFO</code> - request for all intersection geometry information.<br>
+     * </ul>
+     *
+     * @param pickShape the description of this picking volume or area.
+     *
+     * @exception IllegalArgumentException if flags contains both CLOSEST_GEOM_INFO and 
+     * ALL_GEOM_INFO.
+     *
+     * @exception IllegalArgumentException if pickShape is a PickPoint and pick mode
+     * is set to PICK_GEOMETRY.
+     *
+     * @exception IllegalArgumentException if pick mode is neither PICK_BOUNDS 
+     * nor PICK_GEOMETRY.
+     *
+     * @exception IllegalArgumentException if pick mode is PICK_BOUNDS 
+     * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE,
+     * CLOSEST_GEOM_INFO or ALL_GEOM_INFO.
+     *
+     * @exception IllegalArgumentException if pickShape is PickBounds 
+     * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE,
+     * CLOSEST_GEOM_INFO or ALL_GEOM_INFO.
+     *
+     * @exception IllegalStateException if this Locale has been
+     * removed from its VirtualUniverse.
+     *
+     * @exception CapabilityNotSetException if the mode is
+     * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit
+     * is not set in any Geometry objects referred to by any shape
+     * node whose bounds intersects the PickShape.
+     *   
+     * @exception CapabilityNotSetException if flags contains any of
+     * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO
+     * or ALL_GEOM_INFO, and the capability bits that control reading of
+     * coordinate data are not set in any GeometryArray object referred
+     * to by any shape node that intersects the PickShape.
+     * The capability bits that must be set to avoid this exception are as follows :
+     * <ul> 
+     * <li>By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ</li>
+     * <li>By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ</li>
+     * <li>Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ
+     * (in addition to one of the above)</li>
+     * </ul>
+     *
+     * @see BranchGroup#pickAll(int,int,javax.media.j3d.PickShape)
+     * @see PickInfo
+     * 
+     * @since Java 3D 1.4
+     *
+     */
+    public PickInfo[] pickAll( int mode, int flags, PickShape pickShape ) {
+        
+        validateModeFlagAndPickShape(mode, flags, pickShape);   
+
+	GeometryAtom geomAtoms[] = universe.geometryStructure.pickAll(this, pickShape);
+        
+        return PickInfo.pick(this, geomAtoms, mode, flags, pickShape, PickInfo.PICK_ALL);
+
+    }
+
+    /**
+     * Returns a sorted array of references to all the pickable items
      * that intersect with the pickShape. Element [0] references the
      * item closest to <i>origin</i> of PickShape successive array
      * elements are further from the <i>origin</i>
@@ -596,9 +728,118 @@ public class Locale extends Object {
 	    throw new IllegalStateException(J3dI18N.getString("Locale4"));
 	}
 
-	return Picking.pickAllSorted( this, pickShape );
+        PickInfo[] pickInfoArr = pickAllSorted( PickInfo.PICK_BOUNDS,
+                PickInfo.SCENEGRAPHPATH, pickShape);
+
+       if(pickInfoArr == null) {
+            return null;
+       }
+        SceneGraphPath[] sgpArr = new SceneGraphPath[pickInfoArr.length];
+        for( int i=0; i<sgpArr.length; i++) {
+            sgpArr[i] = pickInfoArr[i].getSceneGraphPath();
+        }
+
+        return sgpArr;                
+        
     }
 
+    /**
+     * Returns a sorted array of PickInfo references to all the pickable
+     * items that intersect with the pickShape. Element [0] references 
+     * the item closest to <i>origin</i> of PickShape successive array
+     * elements are further from the <i>origin</i>
+     * The accuracy of the pick is set by the pick mode. The mode include : 
+     * PickInfo.PICK_BOUNDS and PickInfo.PICK_GEOMETRY. The amount of information returned 
+     * is specified via a masked variable, flags, indicating which components are 
+     * present in each returned PickInfo object. 
+     *
+     * @param mode  picking mode, one of <code>PickInfo.PICK_BOUNDS</code> or <code>PickInfo.PICK_GEOMETRY</code>.
+     *
+     * @param flags a mask indicating which components are present in each PickInfo object.  
+     * This is specified as one or more individual bits that are bitwise "OR"ed together to 
+     * describe the PickInfo data. The flags include :
+     * <ul>
+     * <code>PickInfo.SCENEGRAPHPATH</code> - request for computed SceneGraphPath.<br>    
+     * <code>PickInfo.NODE</code> - request for computed intersected Node.<br>
+     * <code>PickInfo.LOCAL_TO_VWORLD</code> - request for computed local to virtual world transform.<br>
+     * <code>PickInfo.CLOSEST_INTERSECTION_POINT</code> - request for closest intersection point.<br>
+     * <code>PickInfo.CLOSEST_DISTANCE</code> - request for the distance of closest intersection.<br>
+     * <code>PickInfo.CLOSEST_GEOM_INFO</code> - request for only the closest intersection geometry information.<br>
+     * <code>PickInfo.ALL_GEOM_INFO</code> - request for all intersection geometry information.<br>
+     * </ul>
+     *
+     * @param pickShape the description of this picking volume or area.
+     *
+     * @exception IllegalArgumentException if flags contains both CLOSEST_GEOM_INFO and 
+     * ALL_GEOM_INFO.
+     *
+     * @exception IllegalArgumentException if pickShape is a PickPoint and pick mode
+     * is set to PICK_GEOMETRY.
+     *
+     * @exception IllegalArgumentException if pick mode is neither PICK_BOUNDS 
+     * nor PICK_GEOMETRY.
+     *
+     * @exception IllegalArgumentException if pick mode is PICK_BOUNDS 
+     * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE,
+     * CLOSEST_GEOM_INFO or ALL_GEOM_INFO.
+     *
+     * @exception IllegalArgumentException if pickShape is PickBounds 
+     * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE,
+     * CLOSEST_GEOM_INFO or ALL_GEOM_INFO.
+     *
+     * @exception IllegalStateException if this Locale has been
+     * removed from its VirtualUniverse.
+     *
+     * @exception CapabilityNotSetException if the mode is
+     * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit
+     * is not set in any Geometry objects referred to by any shape
+     * node whose bounds intersects the PickShape.
+     *   
+     * @exception CapabilityNotSetException if flags contains any of
+     * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO
+     * or ALL_GEOM_INFO, and the capability bits that control reading of
+     * coordinate data are not set in any GeometryArray object referred
+     * to by any shape node that intersects the PickShape.
+     * The capability bits that must be set to avoid this exception are as follows :
+     * <ul> 
+     * <li>By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ</li>
+     * <li>By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ</li>
+     * <li>Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ
+     * (in addition to one of the above)</li>
+     * </ul>
+     *
+     * @see BranchGroup#pickAllSorted(int,int,javax.media.j3d.PickShape)
+     * @see PickInfo
+     * 
+     * @since Java 3D 1.4
+     *
+     */
+    public PickInfo[] pickAllSorted( int mode, int flags, PickShape pickShape ) {
+
+        validateModeFlagAndPickShape(mode, flags, pickShape);   
+        GeometryAtom geomAtoms[] = universe.geometryStructure.pickAll(this, pickShape);
+
+        if ((geomAtoms == null) || (geomAtoms.length == 0)) {
+            return null;
+        }
+        
+        PickInfo[] pickInfoArr  = null;
+        
+	if (mode == PickInfo.PICK_GEOMETRY) {
+            // Need to have closestDistance set
+            flags |= PickInfo.CLOSEST_DISTANCE;
+            pickInfoArr= PickInfo.pick(this, geomAtoms, mode, flags, pickShape, PickInfo.PICK_ALL);
+	    if (pickInfoArr != null) {
+		PickInfo.sortPickInfoArray(pickInfoArr);
+	    }
+        }
+        else {
+            PickInfo.sortGeomAtoms(geomAtoms, pickShape);
+            pickInfoArr= PickInfo.pick(this, geomAtoms, mode, flags, pickShape, PickInfo.PICK_ALL);          
+        }
+        
+        return pickInfoArr;
+    }
 
     /**
      * Returns a SceneGraphPath which references the pickable item
@@ -615,13 +856,101 @@ public class Locale extends Object {
      * @see BranchGroup#pickClosest
      */
     public SceneGraphPath pickClosest( PickShape pickShape ) {
-	if (universe == null) {
-	    throw new IllegalStateException(J3dI18N.getString("Locale4"));
-	}
+        if (universe == null) {
+            throw new IllegalStateException(J3dI18N.getString("Locale4"));
+        }
 
-	return Picking.pickClosest( this, pickShape );
+        PickInfo pickInfo = pickClosest( PickInfo.PICK_BOUNDS,
+                PickInfo.SCENEGRAPHPATH, pickShape);
+        
+        if(pickInfo == null) {
+            return null;
+        }
+        return pickInfo.getSceneGraphPath();
     }
 
+    /**
+     * Returns a PickInfo which references the pickable item
+     * which is closest to the origin of <code>pickShape</code>.
+     * The accuracy of the pick is set by the pick mode. The mode include : 
+     * PickInfo.PICK_BOUNDS and PickInfo.PICK_GEOMETRY. The amount of information returned 
+     * is specified via a masked variable, flags, indicating which components are 
+     * present in each returned PickInfo object. 
+     *
+     * @param mode  picking mode, one of <code>PickInfo.PICK_BOUNDS</code> or <code>PickInfo.PICK_GEOMETRY</code>.
+     *
+     * @param flags a mask indicating which components are present in each PickInfo object.  
+     * This is specified as one or more individual bits that are bitwise "OR"ed together to 
+     * describe the PickInfo data. The flags include :
+     * <ul>
+     * <code>PickInfo.SCENEGRAPHPATH</code> - request for computed SceneGraphPath.<br>    
+     * <code>PickInfo.NODE</code> - request for computed intersected Node.<br>
+     * <code>PickInfo.LOCAL_TO_VWORLD</code> - request for computed local to virtual world transform.<br>
+     * <code>PickInfo.CLOSEST_INTERSECTION_POINT</code> - request for closest intersection point.<br>
+     * <code>PickInfo.CLOSEST_DISTANCE</code> - request for the distance of closest intersection.<br>
+     * <code>PickInfo.CLOSEST_GEOM_INFO</code> - request for only the closest intersection geometry information.<br>
+     * <code>PickInfo.ALL_GEOM_INFO</code> - request for all intersection geometry information.<br>
+     * </ul>
+     *
+     * @param pickShape the description of this picking volume or area.
+     *
+     * @exception IllegalArgumentException if flags contains both CLOSEST_GEOM_INFO and 
+     * ALL_GEOM_INFO.
+     *
+     * @exception IllegalArgumentException if pickShape is a PickPoint and pick mode
+     * is set to PICK_GEOMETRY.
+     *
+     * @exception IllegalArgumentException if pick mode is neither PICK_BOUNDS 
+     * nor PICK_GEOMETRY.
+     *
+     * @exception IllegalArgumentException if pick mode is PICK_BOUNDS 
+     * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE,
+     * CLOSEST_GEOM_INFO or ALL_GEOM_INFO.
+     *
+     * @exception IllegalArgumentException if pickShape is PickBounds 
+     * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE,
+     * CLOSEST_GEOM_INFO or ALL_GEOM_INFO.
+     *
+     * @exception IllegalStateException if this Locale has been
+     * removed from its VirtualUniverse.
+     *
+     * @exception CapabilityNotSetException if the mode is
+     * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit
+     * is not set in any Geometry objects referred to by any shape
+     * node whose bounds intersects the PickShape.
+     *   
+     * @exception CapabilityNotSetException if flags contains any of
+     * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO
+     * or ALL_GEOM_INFO, and the capability bits that control reading of
+     * coordinate data are not set in any GeometryArray object referred
+     * to by any shape node that intersects the PickShape.
+     * The capability bits that must be set to avoid this exception are as follows :
+     * <ul> 
+     * <li>By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ</li>
+     * <li>By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ</li>
+     * <li>Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ
+     * (in addition to one of the above)</li>
+     * </ul>
+     *
+     * @see BranchGroup#pickClosest(int,int,javax.media.j3d.PickShape)
+     * @see PickInfo
+     * 
+     * @since Java 3D 1.4
+     *
+     */
+    public PickInfo pickClosest( int mode, int flags, PickShape pickShape ) {
+
+        PickInfo[] pickInfoArr = null;
+
+        pickInfoArr = pickAllSorted( mode, flags, pickShape );
+        
+        if(pickInfoArr == null) {
+            return null;
+        }
+        
+        return pickInfoArr[0];
+        
+    }
 
     /**
      * Returns a reference to any item that is Pickable below this
@@ -638,10 +967,100 @@ public class Locale extends Object {
 	if (universe == null) {
 	    throw new IllegalStateException(J3dI18N.getString("Locale4"));
 	}
-
-	return Picking.pickAny( this, pickShape );
+       
+        PickInfo pickInfo = pickAny( PickInfo.PICK_BOUNDS,
+                PickInfo.SCENEGRAPHPATH, pickShape);
+        
+        if(pickInfo == null) {
+            return null;
+        }
+        return pickInfo.getSceneGraphPath();
+        
     }
 
+    /**
+     * Returns a PickInfo which references the pickable item  below this
+     * Locale which intersects with <code>pickShape</code>.
+     * The accuracy of the pick is set by the pick mode. The mode include : 
+     * PickInfo.PICK_BOUNDS and PickInfo.PICK_GEOMETRY. The amount of information returned 
+     * is specified via a masked variable, flags, indicating which components are 
+     * present in each returned PickInfo object. 
+     *
+     * @param mode  picking mode, one of <code>PickInfo.PICK_BOUNDS</code> or <code>PickInfo.PICK_GEOMETRY</code>.
+     *
+     * @param flags a mask indicating which components are present in each PickInfo object.  
+     * This is specified as one or more individual bits that are bitwise "OR"ed together to 
+     * describe the PickInfo data. The flags include :
+     * <ul>
+     * <code>PickInfo.SCENEGRAPHPATH</code> - request for computed SceneGraphPath.<br>    
+     * <code>PickInfo.NODE</code> - request for computed intersected Node.<br>
+     * <code>PickInfo.LOCAL_TO_VWORLD</code> - request for computed local to virtual world transform.<br>
+     * <code>PickInfo.CLOSEST_INTERSECTION_POINT</code> - request for closest intersection point.<br>
+     * <code>PickInfo.CLOSEST_DISTANCE</code> - request for the distance of closest intersection.<br>
+     * <code>PickInfo.CLOSEST_GEOM_INFO</code> - request for only the closest intersection geometry information.<br>
+     * <code>PickInfo.ALL_GEOM_INFO</code> - request for all intersection geometry information.<br>
+     * </ul>
+     *
+     * @param pickShape the description of this picking volume or area.
+     *
+     * @exception IllegalArgumentException if flags contains both CLOSEST_GEOM_INFO and 
+     * ALL_GEOM_INFO.
+     *
+     * @exception IllegalArgumentException if pickShape is a PickPoint and pick mode
+     * is set to PICK_GEOMETRY.
+     *
+     * @exception IllegalArgumentException if pick mode is neither PICK_BOUNDS 
+     * nor PICK_GEOMETRY.
+     *
+     * @exception IllegalArgumentException if pick mode is PICK_BOUNDS 
+     * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE,
+     * CLOSEST_GEOM_INFO or ALL_GEOM_INFO.
+     *
+     * @exception IllegalArgumentException if pickShape is PickBounds 
+     * and flags includes any of CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE,
+     * CLOSEST_GEOM_INFO or ALL_GEOM_INFO.
+     *
+     * @exception IllegalStateException if this Locale has been
+     * removed from its VirtualUniverse.
+     *
+     * @exception CapabilityNotSetException if the mode is
+     * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit
+     * is not set in any Geometry objects referred to by any shape
+     * node whose bounds intersects the PickShape.
+     *   
+     * @exception CapabilityNotSetException if flags contains any of
+     * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO
+     * or ALL_GEOM_INFO, and the capability bits that control reading of
+     * coordinate data are not set in any GeometryArray object referred
+     * to by any shape node that intersects the PickShape.
+     * The capability bits that must be set to avoid this exception are as follows :
+     * <ul> 
+     * <li>By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ</li>
+     * <li>By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ</li>
+     * <li>Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ
+     * (in addition to one of the above)</li>
+     * </ul>
+     *
+     * @see BranchGroup#pickAny(int,int,javax.media.j3d.PickShape)
+     * @see PickInfo
+     * 
+     * @since Java 3D 1.4
+     *
+     */
+    public PickInfo pickAny( int mode, int flags, PickShape pickShape ) {
+
+        validateModeFlagAndPickShape(mode, flags, pickShape);
+	GeometryAtom geomAtoms[] = universe.geometryStructure.pickAll(this, pickShape);
+        
+        PickInfo[] pickInfoArr = PickInfo.pick(this, geomAtoms, mode, flags, pickShape, PickInfo.PICK_ANY);
+        
+        if(pickInfoArr == null) {
+            return null;
+        }
+        
+        return pickInfoArr[0];
+        
+    }
 
     /**
      * Cleans up resources associated with this Locale
diff --git a/src/classes/share/javax/media/j3d/MasterControl.java b/src/classes/share/javax/media/j3d/MasterControl.java
index c70b268..8e15f3f 100644
--- a/src/classes/share/javax/media/j3d/MasterControl.java
+++ b/src/classes/share/javax/media/j3d/MasterControl.java
@@ -20,6 +20,8 @@ package javax.media.j3d;
 
 import java.util.*;
 import java.awt.*;
+import java.io.File;
+
 
 class MasterControl {
 
@@ -77,6 +79,8 @@ class MasterControl {
     static final Integer SET_QUERYPROPERTIES = new Integer(20);    
     static final Integer SET_VIEW = new Integer(21);
 
+    private static boolean librariesLoaded = false;
+
     /**
      * reference to MasterControl thread
      */
@@ -197,6 +201,9 @@ class MasterControl {
     // Only one Timer thread in the system.
     TimerThread timerThread;
 
+    // Only one Notification thread in the system.
+    private NotificationThread notificationThread;
+
     /**
      * This flag indicates that MC is running
      */
@@ -305,9 +312,11 @@ class MasterControl {
 
     // This is a time stamp used when context is created
     private long contextTimeStamp = 0;
-
-    // This is a counter for canvas bit
-    private int canvasBitCount = 0;
+    
+    // This is an array of  canvasIds in used
+    private boolean[] canvasIds = null;
+    private int canvasFreeIndex = 0;
+    private Object canvasIdLock = new Object();
 
     // This is a counter for rendererBit
     private int rendererCount = 0;
@@ -335,15 +344,28 @@ class MasterControl {
     // Flag that indicates whether separate specular color is disabled or not
     boolean disableSeparateSpecularColor = false;
 
-    // Maximum number of texture units
-    int textureUnitMax = 100;
-
     // Flag that indicates whether DisplayList is used or not
     boolean isDisplayList = true;
 
     // If this flag is set, then by-ref geometry will not be
     // put in display list
     boolean buildDisplayListIfPossible = false;
+    
+    // If this flag is set, then geometry arrays with vertex attributes can
+    // be in display list.
+    boolean vertexAttrsInDisplayList = false;
+
+    // The global shading language being used. Using a ShaderProgram
+    // with a shading language other than the one specified by
+    // globalShadingLanguage will cause a ShaderError to be generated,
+    static int globalShadingLanguage = Shader.SHADING_LANGUAGE_GLSL;
+
+    // Flags indicating whether the Cg or GLSL libraries are available; we still need
+    // to check for the actual extension support when the Canvas3D with its associated context
+    // is created. Note that these are qualifed by the above globalShadingLanguage, so at
+    // most one of these two flags will be true;
+    static boolean cgLibraryAvailable = false;
+    static boolean glslLibraryAvailable = false;
 
     
     // REQUESTCLEANUP messages argument
@@ -390,6 +412,9 @@ class MasterControl {
     // False to disable rescale normal if OGL support
     boolean isForceNormalized = false;
 
+    // True to allow simulated (multi-pass) multi-texture
+    boolean allowSimulatedMultiTexture = false;
+
     // Hashtable that maps a GraphicsDevice to its associated
     // Screen3D--this is only used for on-screen Canvas3Ds
     Hashtable deviceScreenMap = new Hashtable();
@@ -417,6 +442,13 @@ class MasterControl {
     private ArrayList timestampUpdateList = new ArrayList(3);
 
     private UnorderList freeMessageList = new UnorderList(8);
+    
+    // System properties containing the native library search PATH
+    // The order listed is the order in which they will be searched
+    private static final String[] systemPathProps = {
+        "sun.boot.library.path",
+        "java.library.path"
+    };
 
     long awt;
     native long getAWT();
@@ -424,6 +456,9 @@ class MasterControl {
     // Method to initialize the native J3D library
     private native boolean initializeJ3D(boolean disableXinerama);
 
+    // Method to verify whether the native Cg library is available
+    private static native boolean loadNativeCgLibrary(String[] libpath);
+
     // Method to get number of procesor
     private native int getNumberOfProcessor();
 
@@ -464,7 +499,8 @@ class MasterControl {
      * VirtualUniverse.
      */
     MasterControl() {
-
+        assert librariesLoaded;
+        
 	// Get AWT handle
 	awt = getAWT();
 
@@ -522,23 +558,6 @@ class MasterControl {
 	    System.err.println("Java 3D: separate specular color disabled if possible");
 	}
 
-	// Get the maximum number of texture units
-	final int defaultTextureUnitMax = textureUnitMax;
-	Integer textureUnitLimit =
-	    (Integer) java.security.AccessController.doPrivileged(
-	    new java.security.PrivilegedAction() {
-		public Object run() {
-		    return Integer.getInteger("j3d.textureUnitMax",
-					      defaultTextureUnitMax);
-		}
-	    });
-
-	textureUnitMax = textureUnitLimit.intValue();
-	if (textureUnitMax != defaultTextureUnitMax) {
-	    System.err.println("Java 3D: maximum number of texture units = " +
-			       textureUnitMax);
-	}
-
 	isDisplayList = getBooleanProperty("j3d.displaylist", isDisplayList,
 					   "display list");
 
@@ -553,24 +572,41 @@ class MasterControl {
 			       "compiled vertex array");
 
 	isForceNormalized =
-	    getBooleanProperty("j3d.forceNormalized", isForceNormalized,
+	    getBooleanProperty("j3d.forceNormalized",
+			       isForceNormalized,
 			       "force normalized");
 
+        allowSimulatedMultiTexture =
+	    getBooleanProperty("j3d.simulatedMultiTexture",
+			       allowSimulatedMultiTexture,
+			       "simulated multi-texture");
+
+	if (allowSimulatedMultiTexture) {
+	    System.err.println("************************************************************************");
+	    System.err.println(J3dI18N.getString("MasterControl2"));
+	    System.err.println(J3dI18N.getString("MasterControl3"));
+	    System.err.println("************************************************************************");
+	}
 
-	boolean j3dOptimizeSpace =
+        boolean j3dOptimizeSpace =
 	    getBooleanProperty("j3d.optimizeForSpace", true,
 			       "optimize for space");
 
-	// Build Display list for by-ref geometry and infrequently changing geometry
-	// ONLY IF (isDisplayList is true and optimizeForSpace if False)
-	if (isDisplayList && !j3dOptimizeSpace) {
-	    buildDisplayListIfPossible = true;
-	}
-	else {
-	    buildDisplayListIfPossible = false;
-	}
+        if (isDisplayList) {
+            // Build Display list for by-ref geometry
+            // ONLY IF optimizeForSpace is false
+            if (!j3dOptimizeSpace) {
+                buildDisplayListIfPossible = true;
+            }
+
+            // Build display lists for geometry with vertex attributes
+            // ONLY if we are in GLSL mode and GLSL shaders are available
+            if (glslLibraryAvailable) {
+                vertexAttrsInDisplayList = true;
+            }
+        }
 
-	// Check to see whether Renderer can run without DSI lock
+        // Check to see whether Renderer can run without DSI lock
 	doDsiRenderLock = getBooleanProperty("j3d.renderLock",
 					     doDsiRenderLock,
 					     "render lock");
@@ -682,6 +718,16 @@ class MasterControl {
 
 	// create the freelists
 	FreeListManager.createFreeLists();
+
+	// create an array canvas use registers
+	// The 32 limit can be lifted once the
+	// resourceXXXMasks in other classes 
+	// are change not to use integer.
+	canvasIds = new boolean[32];
+	for(int i=0; i<canvasIds.length; i++) {
+	    canvasIds[i] = false;
+	}
+	canvasFreeIndex = 0;
     }
 
     private static String getProperty(final String prop) {
@@ -728,7 +774,9 @@ class MasterControl {
      * the MasterControl object is created.
      */
     static void loadLibraries() {
-       	// This works around a native load library bug
+        assert !librariesLoaded;
+
+        // This works around a native load library bug
        	try {
             java.awt.Toolkit toolkit = java.awt.Toolkit.getDefaultToolkit();
             toolkit = null;   // just making sure GC collects this
@@ -745,37 +793,113 @@ class MasterControl {
 	});
 
 	// Load the native J3D library
-       	java.security.AccessController.doPrivileged(new 
+        final String oglLibraryName = "j3dcore-ogl";
+        final String d3dLibraryName = "j3dcore-d3d";
+        final String libraryName = (String)
+        java.security.AccessController.doPrivileged(new
 	    java.security.PrivilegedAction() {
 		public Object run() {
-		    
+		    String libName = oglLibraryName;
+
+		    // If it is a Windows OS, we want to support dynamic native library selection (ogl, d3d)
 		    String osName = System.getProperty("os.name");
-		    // System.err.println(" os.name is " + osName );
-		    // If it is a Windows OS, we want to support
-		    // dynamic native library selection (ogl, d3d)
-		    if((osName.length() > 8) && 
-		       ((osName.substring(0,7)).equals("Windows"))){
-			
-			// TODO : Will support a more flexible dynamic 
-			// selection scheme via the use of Preferences API.
+		    if (osName != null && osName.startsWith("Windows")) {
+			// XXXX : Should eventually support a more flexible dynamic 
+			// selection scheme via an API call.
 			String str = System.getProperty("j3d.rend");
-			if ((str == null) || (!str.equals("d3d"))) {
-			    // System.err.println("(1) ogl case : j3d.rend is " + str );
-			    System.loadLibrary("j3dcore-ogl");
-
-			}
-			else {
-			    // System.err.println("(2) d3d case : j3d.rend is " + str);
-			    System.loadLibrary("j3dcore-d3d");
+			if (str != null && str.equals("d3d")) {
+			    libName = d3dLibraryName;
 			}
 		    }
-		    else {
-			// System.err.println("(3) ogl case");
-			System.loadLibrary("j3dcore-ogl");
-		    }
-		    return null;
+
+                    System.loadLibrary(libName);
+		    return libName;
 		}
 	    });
+
+        // Get the global j3d.shadingLanguage property
+	final String slStr = getProperty("j3d.shadingLanguage");
+	if (slStr != null) {
+	    boolean found = false;
+	    if (slStr.equals("GLSL")) {
+		globalShadingLanguage = Shader.SHADING_LANGUAGE_GLSL;
+		found = true;
+	    }
+	    else if (slStr.equals("Cg")) {
+		globalShadingLanguage = Shader.SHADING_LANGUAGE_CG;
+		found = true;
+	    }
+
+	    if (found) {
+		System.err.println("Java 3D: Setting global shading language to " + slStr);
+	    }
+	    else {
+		System.err.println("Java 3D: Unrecognized shading language: " + slStr);
+	    }
+	}
+
+        // Check whether the Cg library is available
+        if (globalShadingLanguage == Shader.SHADING_LANGUAGE_CG) {
+            String cgLibraryName = libraryName + "-cg";
+            String[] libpath = setupLibPath(systemPathProps, cgLibraryName);
+            if (loadNativeCgLibrary(libpath)) {
+                cgLibraryAvailable = true;
+            }
+        }
+        
+        // Check whether the GLSL library is available
+        if (globalShadingLanguage == Shader.SHADING_LANGUAGE_GLSL) {
+            if (libraryName == oglLibraryName) {
+                // No need to verify that GLSL is available, since GLSL is part
+                // of OpenGL as an extension (or part of core in 2.0)
+                glslLibraryAvailable = true;
+            }
+        }
+
+        assert !(glslLibraryAvailable && cgLibraryAvailable) :
+            "ERROR: cannot support both GLSL and CG at the same time";
+
+        librariesLoaded = true;
+    }
+
+
+    /**
+     * Parse the specified System properties containing a PATH and return an
+     * array of Strings, where each element is an absolute filename consisting of
+     * the individual component of the path concatenated with the (relative)
+     * library file name. Only those absolute filenames that exist are included.
+     * If no absolute filename is found, we will try the relative library name.
+     */
+    private static String[] setupLibPath(String[] props, String libName) {
+        ArrayList pathList = new ArrayList();
+
+        String filename = System.mapLibraryName(libName);
+        for (int n = 0; n < props.length; n++) {
+            String pathString = getProperty(props[n]);
+            boolean done = false;
+            int posStart = 0;
+            while (!done) {
+                int posEnd = pathString.indexOf(File.pathSeparator, posStart);
+                if (posEnd == -1) {
+                    posEnd = pathString.length();
+                    done = true;
+                }
+                String pathDir = pathString.substring(posStart, posEnd);
+                File pathFile = new File(pathDir, filename);
+                if (pathFile.exists()) {
+                    pathList.add(pathFile.getAbsolutePath());
+                }
+
+                posStart = posEnd + 1;
+            }
+        }
+
+        // If no absolute path names exist, add in the relative library name
+        if (pathList.size() == 0) {
+            pathList.add(filename);
+        }
+
+        return (String[])pathList.toArray(new String[0]);
     }
 
 
@@ -837,6 +961,14 @@ class MasterControl {
         return (1 << rendererCount++);
     }
 
+
+    /**
+     * This returns the a unused renderer bit
+     */
+    int getRendererId() {
+        return rendererCount++;
+    }
+
     /**
      * This returns a context creation time stamp
      * Note: this has to be called under the contextCreationLock
@@ -900,28 +1032,39 @@ class MasterControl {
 	FreeListManager.freeObject(FreeListManager.TEXTURE3D, new Integer(id));
     }
 
-    int getCanvasBit() {
-	// Master control need to keep count itself
-	MemoryFreeList cbId =
-	    FreeListManager.getFreeList(FreeListManager.CANVASBIT);
-	if (cbId.size() > 0) {
-	    return ((Integer)FreeListManager.
-		    getObject(FreeListManager.CANVASBIT)).intValue();
-	}
-	else {
-	    if (canvasBitCount > 31) {
+    int getCanvasId() {
+        int i;
+
+	synchronized(canvasIdLock) {
+	    // Master control need to keep count itself        
+	    for(i=canvasFreeIndex; i<canvasIds.length; i++) {
+		if(canvasIds[i] == false)
+		    break;
+	    }
+
+	    if ( canvasFreeIndex >= canvasIds.length) {
 		throw new RuntimeException("Cannot render to more than 32 Canvas3Ds");
 	    }
-	    return (1 << canvasBitCount++);
+
+	    canvasIds[i] = true;
+	    canvasFreeIndex = i + 1;
 	}
+
+        return i;
+        
     }
 
+    void freeCanvasId(int canvasId) {
+        // Valid range is [0, 31]
+	synchronized(canvasIdLock) {
 
-    void freeCanvasBit(int canvasBit) {
-	FreeListManager.freeObject(FreeListManager.CANVASBIT,
-				   new Integer(canvasBit));
+	    canvasIds[canvasId] = false;
+	    if(canvasFreeIndex > canvasId) {
+		canvasFreeIndex = canvasId;
+	    }
+	}
     }
-
+    
     Transform3D getTransform3D(Transform3D val) {
 	Transform3D t;
 	t = (Transform3D)
@@ -1096,6 +1239,10 @@ class MasterControl {
 			timerThread.finish();
 			timerThread = null;		
 		    }    
+		    if (notificationThread != null) {
+			notificationThread.finish();
+			notificationThread = null;
+		    }
 		    requestObjList.clear();
 		    requestTypeList.clear();
 		    return true;
@@ -1210,7 +1357,14 @@ class MasterControl {
 	}
 	setWork();
     }
-		
+
+    /**
+     * This takes the specified notification message and sends it to the
+     * notification thread for processing.
+     */
+    void sendNotification(J3dNotification notification) {
+        notificationThread.addNotification(notification);
+    }
 
     /**
      * Create and start the MasterControl Thread.
@@ -2050,6 +2204,19 @@ class MasterControl {
 	      }
 	});
 	timerThread.start();
+
+        // Create notification thread
+	java.security.AccessController.doPrivileged(
+			    new java.security.PrivilegedAction() {
+              public Object run() {
+		  synchronized (rootThreadGroup) {
+		      notificationThread = new NotificationThread(rootThreadGroup);
+		      notificationThread.setPriority(threadPriority);
+		  }
+		  return null;
+	      }
+	});
+	notificationThread.start();
     }
 
     /**
@@ -2099,6 +2266,10 @@ class MasterControl {
 		timerThread.finish();
 		timerThread = null;
 	    }
+            if (notificationThread != null) {
+                notificationThread.finish();
+                notificationThread = null;
+            }
 
 	    // shouldn't all of these be synchronized ???
 	    synchronized (VirtualUniverse.mc.deviceScreenMap) {
@@ -2112,8 +2283,11 @@ class MasterControl {
 	    // list here because other structure may release them
 	    // later 
 
-	    FreeListManager.clearList(FreeListManager.CANVASBIT);
-	    canvasBitCount = 0;
+	    for(int i=0; i<canvasIds.length; i++) {
+		canvasIds[i] = false;
+	    }
+	    canvasFreeIndex = 0;
+	    
 	    renderOnceList.clear();
 	    timestampUpdateList.clear();
 
@@ -3441,7 +3615,7 @@ class MasterControl {
     static {
 	/*
         // Determine whether the JVM is version JDK1.5 or later.
-        // TODO: replace this with code that checks for the existence
+        // XXXX: replace this with code that checks for the existence
 	// of a class or method that is defined in 1.5, but not in 1.4
         String versionString =
             (String) java.security.AccessController.doPrivileged(
diff --git a/src/classes/share/javax/media/j3d/Material.java b/src/classes/share/javax/media/j3d/Material.java
index d35c1d5..deacac9 100644
--- a/src/classes/share/javax/media/j3d/Material.java
+++ b/src/classes/share/javax/media/j3d/Material.java
@@ -103,7 +103,11 @@ public class Material extends NodeComponent {
      */
     public static final int AMBIENT_AND_DIFFUSE = 4;
 
-
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_COMPONENT_READ
+    };
+    
     /**
      * Constructs and initializes a Material object using default parameters.
      * The default values are as follows:
@@ -119,6 +123,8 @@ public class Material extends NodeComponent {
      */
     public Material() {
 	// Just use the defaults
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -139,7 +145,10 @@ public class Material extends NodeComponent {
 		    Color3f diffuseColor,
 		    Color3f specularColor,
 		    float shininess) {
-      ((MaterialRetained)this.retained).createMaterial(ambientColor,
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((MaterialRetained)this.retained).createMaterial(ambientColor,
 		    emissiveColor, diffuseColor, specularColor, 
 		    shininess);
     }
@@ -602,7 +611,8 @@ public class Material extends NodeComponent {
 	 * Capability read bit set will be displayed.
 	 */
 	public String toString() {
-		StringBuffer str=new StringBuffer("Material Object:");
+		StringBuffer str = new StringBuffer(getNamePrefix());
+		str.append("javax.media.j3d.Material: ");
 		Color3f color=new Color3f();
 		try {
 			getAmbientColor(color);
diff --git a/src/classes/share/javax/media/j3d/MediaContainer.java b/src/classes/share/javax/media/j3d/MediaContainer.java
index 10e1502..e2c6e39 100644
--- a/src/classes/share/javax/media/j3d/MediaContainer.java
+++ b/src/classes/share/javax/media/j3d/MediaContainer.java
@@ -60,6 +60,12 @@ public class MediaContainer extends NodeComponent {
      public static final int
     ALLOW_URL_WRITE = CapabilityBits.MEDIA_CONTAINER_ALLOW_URL_WRITE;
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_CACHE_READ,
+        ALLOW_URL_READ
+    };
+    
     /**
      * Constructs a MediaContainer object with default parameters.
      * The default values are as follows:
@@ -72,6 +78,8 @@ public class MediaContainer extends NodeComponent {
      */  
     public MediaContainer() {
          // Just use default values
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -81,6 +89,9 @@ public class MediaContainer extends NodeComponent {
      * @exception SoundException if the URL is not valid or cannot be opened
      */  
     public MediaContainer(String path) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((MediaContainerRetained)this.retained).setURLString(path);
     }
 
@@ -91,6 +102,9 @@ public class MediaContainer extends NodeComponent {
      * @exception SoundException if the URL is not valid or cannot be opened
      */  
     public MediaContainer(URL url) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((MediaContainerRetained)this.retained).setURLObject(url);
     }
 
@@ -102,6 +116,9 @@ public class MediaContainer extends NodeComponent {
      * @since Java 3D 1.2
      */  
     public MediaContainer(InputStream stream) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((MediaContainerRetained)this.retained).setInputStream(stream);
     }
 
diff --git a/src/classes/share/javax/media/j3d/MediaContainerRetained.java b/src/classes/share/javax/media/j3d/MediaContainerRetained.java
index 19f2547..f120902 100644
--- a/src/classes/share/javax/media/j3d/MediaContainerRetained.java
+++ b/src/classes/share/javax/media/j3d/MediaContainerRetained.java
@@ -141,7 +141,7 @@ class MediaContainerRetained extends NodeComponentRetained {
      * @param stream InputStream that references the sound data
      */
     void setInputStream(InputStream stream, boolean forceLoad) {
-        // %%% TODO AudioDevice not intellegent enough to process InputStreams yet
+        // XXXX: AudioDevice not intellegent enough to process InputStreams yet
         // can NOT set stream field unless the other related fields are null
         if (stream != null) {
             if (url != null || urlString != null)
diff --git a/src/classes/share/javax/media/j3d/ModelClip.java b/src/classes/share/javax/media/j3d/ModelClip.java
index 308af79..6895c2d 100644
--- a/src/classes/share/javax/media/j3d/ModelClip.java
+++ b/src/classes/share/javax/media/j3d/ModelClip.java
@@ -109,7 +109,14 @@ public class ModelClip extends Leaf {
     public static final int ALLOW_SCOPE_WRITE =
     CapabilityBits.MODEL_CLIP_ALLOW_SCOPE_WRITE;
 
-
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_SCOPE_READ,
+        ALLOW_ENABLE_READ,
+        ALLOW_INFLUENCING_BOUNDS_READ,
+        ALLOW_PLANE_READ
+    };
+                
     /**
      * Constructs a ModelClip node with default parameters.  The default
      * values are as follows:
@@ -128,6 +135,9 @@ public class ModelClip extends Leaf {
      */
     public ModelClip() {
 	// Just use the defaults
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+        
     }
 
 
@@ -137,7 +147,10 @@ public class ModelClip extends Leaf {
      * @param planes an array of 6 model clipping planes
      */
     public ModelClip(Vector4d[] planes) {
-	((ModelClipRetained)this.retained).initPlanes(planes);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((ModelClipRetained)this.retained).initPlanes(planes);
     }
 
 
@@ -149,6 +162,9 @@ public class ModelClip extends Leaf {
      * @param enables an array of 6 enable flags
      */
     public ModelClip(Vector4d[] planes, boolean[] enables) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+        
 	((ModelClipRetained)this.retained).initPlanes(planes);
 	((ModelClipRetained)this.retained).initEnables(enables);
     }
diff --git a/src/classes/share/javax/media/j3d/Morph.java b/src/classes/share/javax/media/j3d/Morph.java
index 9cabaa8..a03edec 100644
--- a/src/classes/share/javax/media/j3d/Morph.java
+++ b/src/classes/share/javax/media/j3d/Morph.java
@@ -77,6 +77,8 @@ import javax.vecmath.*;
  * <i>before</i> the indexes are applied.  Only the indexes in the
  * first geometry array (geometry[0]) are used when rendering the
  * geometry.
+ *
+ * @deprecated As of Java 3D version 1.4.
  */
 
 public class Morph extends Leaf {
@@ -149,9 +151,19 @@ public class Morph extends Leaf {
     public static final int ALLOW_APPEARANCE_OVERRIDE_WRITE =
 	CapabilityBits.MORPH_ALLOW_APPEARANCE_OVERRIDE_WRITE;
 
-
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_GEOMETRY_ARRAY_READ,
+        ALLOW_APPEARANCE_READ,
+        ALLOW_WEIGHTS_READ,        
+        ALLOW_COLLISION_BOUNDS_READ,
+        ALLOW_APPEARANCE_OVERRIDE_READ
+    };
+                
     // non public default constructor
     Morph() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);        
     }
 
     /**
@@ -171,6 +183,7 @@ public class Morph extends Leaf {
      * a null or zero-length array of GeometryArray objects is
      * permitted, and specifies that no geometry is drawn.  In this case,
      * the array of weights is initialized to a zero-length array.
+     *
      * @exception IllegalArgumentException if any of the specified
      * geometry array objects differ from each other in any of the
      * following ways:
@@ -187,19 +200,28 @@ public class Morph extends Leaf {
      * (coord, color, normal, texcoord),
      * for indexed geometry by-reference</li>
      * </ul>
+     *
+     * @exception UnsupportedOperationException if the specified
+     * geometry arrays contain vertex attributes (that is, if their
+     * vertexFormat includes the <code>VERTEX_ATTRIBUTES</code> flag).
      */
     public Morph(GeometryArray geometryArrays[]) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+        
 	((MorphRetained)retained).setGeometryArrays(geometryArrays);
     }
 
     /**
      * Constructs and initializes a Morph node with the specified array
      * of GeometryArray objects and the specified appearance object.
+     *
      * @param geometryArrays the geometry components of the Morph node
      * a null or zero-length array of GeometryArray objects is
      * permitted, and specifies that no geometry is drawn.  In this case,
      * the array of weights is initialized to a zero-length array.
      * @param appearance the appearance component of the Morph node
+     *
      * @exception IllegalArgumentException if any of the specified
      * geometry array objects differ from each other in any of the
      * following ways:
@@ -216,9 +238,16 @@ public class Morph extends Leaf {
      * (coord, color, normal, texcoord),
      * for indexed geometry by-reference</li>
      * </ul>
+     *
+     * @exception UnsupportedOperationException if the specified
+     * geometry arrays contain vertex attributes (that is, if their
+     * vertexFormat includes the <code>VERTEX_ATTRIBUTES</code> flag).
      */
     public Morph(GeometryArray geometryArrays[], Appearance appearance) {
-	((MorphRetained)retained).setGeometryArrays(geometryArrays);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((MorphRetained)retained).setGeometryArrays(geometryArrays);
 	((MorphRetained)this.retained).setAppearance(appearance);
     }
 
@@ -303,6 +332,10 @@ public class Morph extends Leaf {
      * (coord, color, normal, texcoord),
      * for indexed geometry by-reference</li>
      * </ul>
+     *
+     * @exception UnsupportedOperationException if the specified
+     * geometry arrays contain vertex attributes (that is, if their
+     * vertexFormat includes the <code>VERTEX_ATTRIBUTES</code> flag).
      */
     public void setGeometryArrays(GeometryArray geometryArrays[]) {
 
@@ -642,7 +675,8 @@ public class Morph extends Leaf {
     // have the bit set.
     private void checkForAllowIntersect() {
 	MorphRetained morphR = ((MorphRetained)this.retained);
-	for (int i = 0; i < morphR.numGeometryArrays; i++) {
+	int numGeometryArrays = morphR.getNumGeometryArrays();
+	for (int i = 0; i < numGeometryArrays; i++) {
 	    if (!morphR.geometryArrays[i].source.
 		getCapability(Geometry.ALLOW_INTERSECT)) {
 
diff --git a/src/classes/share/javax/media/j3d/MorphRetained.java b/src/classes/share/javax/media/j3d/MorphRetained.java
index 34633f7..351122f 100644
--- a/src/classes/share/javax/media/j3d/MorphRetained.java
+++ b/src/classes/share/javax/media/j3d/MorphRetained.java
@@ -56,7 +56,7 @@ class MorphRetained extends LeafRetained implements GeometryUpdater {
      */
     GeometryArrayRetained geometryArrays[];
 
-    int numGeometryArrays = 0;
+    private int numGeometryArrays = 0;
 
     /**
      * The weight vector the morph node.
@@ -200,6 +200,13 @@ class MorphRetained extends LeafRetained implements GeometryUpdater {
 	    }
 	    doErrorCheck(prevGeo, geo);
 	}
+
+	// Check the first one for vertex attributes
+	geo = (GeometryArrayRetained)geometryArrays[0].retained;
+	if ((geo.vertexFormat & GeometryArray.VERTEX_ATTRIBUTES) != 0) {
+	    throw new UnsupportedOperationException(J3dI18N.getString("MorphRetained9"));
+	}
+
 	// Check if the first one is in Immediate context
 	if (geometryArrays[0] != null) {
 	    geo = (GeometryArrayRetained)geometryArrays[0].retained;
@@ -452,8 +459,58 @@ class MorphRetained extends LeafRetained implements GeometryUpdater {
     boolean getAppearanceOverrideEnable() {
 	return appearanceOverrideEnable;
     }
+   
+    boolean intersect(PickInfo pickInfo, PickShape pickShape, int flags ) {
+
+        Transform3D localToVworld = pickInfo.getLocalToVWorldRef();
+
+	Transform3D vworldToLocal = new Transform3D();
+	vworldToLocal.invert(localToVworld);
+	PickShape newPS = pickShape.transform(vworldToLocal);
 
+	GeometryRetained geo = (GeometryRetained) (morphedGeometryArray.retained);
 
+        if (geo.mirrorGeometry != null) {
+            geo = geo.mirrorGeometry;
+        }
+        
+        if (((flags & PickInfo.CLOSEST_INTERSECTION_POINT) == 0) &&
+                ((flags & PickInfo.CLOSEST_DISTANCE) == 0) &&
+                ((flags & PickInfo.CLOSEST_GEOM_INFO) == 0) &&
+                ((flags & PickInfo.ALL_GEOM_INFO) == 0)) {
+            return geo.intersect(newPS, null, 0, null);
+        } else {
+            Point3d closestIPnt = new Point3d();
+            Point3d iPnt = new Point3d();
+            Point3d iPntVW = new Point3d();
+            PickInfo.IntersectionInfo intersectionInfo
+                    = pickInfo.createIntersectionInfo();
+
+            if (geo.intersect(newPS, intersectionInfo, flags, iPnt)) {
+                
+                iPntVW.set(iPnt);
+                localToVworld.transform(iPntVW);
+                double distance = pickShape.distance(iPntVW);
+                if ((flags & PickInfo.CLOSEST_DISTANCE) != 0) {
+                    pickInfo.setClosestDistance(distance);
+                }
+                if((flags & PickInfo.CLOSEST_INTERSECTION_POINT) != 0) {
+                    pickInfo.setClosestIntersectionPoint(iPnt);
+                } else if ((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) {
+                    intersectionInfo.setGeometry((Geometry) geo.source);
+                    intersectionInfo.setGeometryIndex(0);
+                    intersectionInfo.setIntersectionPoint(iPnt);
+                    intersectionInfo.setDistance(distance);
+                    // VertexIndices has been computed in intersect method.
+                    pickInfo.insertIntersectionInfo(intersectionInfo);
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    
     /**
      * Check if the geometry component of this shape node under path
      *  intersects with the pickShape.
@@ -461,48 +518,38 @@ class MorphRetained extends LeafRetained implements GeometryUpdater {
      *  contains the closest distance of intersection if it is not
      *  equal to null.
      */
-    boolean intersect(SceneGraphPath path, PickShape pickShape,
-		      double[] dist) {
-
+    boolean intersect(SceneGraphPath path,
+            PickShape pickShape, double[] dist) {
+        
 	// This method will not do bound intersect check, as it assume caller
 	// has already done that. ( For performance and code simplification
 	// reasons. )
 
-	Transform3D localToVworld = path.getTransform();
-
+        int flags;
+        PickInfo pickInfo = new PickInfo();
+        
+        Transform3D localToVworld = path.getTransform();
 	if (localToVworld == null) {
 	    throw new RuntimeException(J3dI18N.getString("MorphRetained5"));   
 	}
 
-	Transform3D vworldToLocal = VirtualUniverse.mc.getTransform3D(null);
-	vworldToLocal.invert(localToVworld);
-	PickShape newPS = pickShape.transform(vworldToLocal);
-	FreeListManager.freeObject(FreeListManager.TRANSFORM3D, vworldToLocal);
-
-	Point3d iPnt = Shape3DRetained.getPoint3d();
-
-	GeometryRetained geo = (GeometryRetained) (morphedGeometryArray.retained);
-
-	if (geo.mirrorGeometry != null) {
-	    geo = geo.mirrorGeometry;
-	}
-
-	boolean isIntersect;
-	if (dist != null) {
-	    isIntersect = geo.intersect(newPS, dist, iPnt);
-	    if (isIntersect) {
-		// compute the real distance since the dist return
-		// above distance may scaled (non-uniform) by transform3d
-		localToVworld.transform(iPnt);
-		dist[0] = pickShape.distance(iPnt);
-	    } 
-	} else {
-	    isIntersect = geo.intersect(newPS, null, iPnt);
-	}
-	Shape3DRetained.freePoint3d(iPnt);
-	return isIntersect;
-    }
-
+        pickInfo.setLocalToVWorldRef( localToVworld);
+        //System.out.println("MorphRetained.intersect() : ");
+        if (dist == null) {
+            //System.out.println("      no dist request ....");
+            return intersect(pickInfo, pickShape, 0);
+        }
+        
+        flags = PickInfo.CLOSEST_DISTANCE;
+        if (intersect(pickInfo, pickShape, flags)) {
+            dist[0] = pickInfo.getClosestDistance();
+            return true;
+        }
+        
+        return false;
+          
+      }    
+   
     /**
      * Sets the Morph node's weight vector
      * @param wieghts the new vector of weights for the morph node
@@ -571,8 +618,8 @@ class MorphRetained extends LeafRetained implements GeometryUpdater {
         } else {
             return super.getBounds();
         }
-    } 
-  
+    }
+
     Bounds getEffectiveBounds() {
         if(boundsAutoCompute) {
 	    return getBounds();
@@ -604,6 +651,11 @@ class MorphRetained extends LeafRetained implements GeometryUpdater {
 	}
     } 
 
+    // Return the number of geometry arrays in this MorphRetained object.
+    int getNumGeometryArrays() {
+	return numGeometryArrays;
+    }
+
     // If the geometry of a morph changes, make sure that the
     // validVertexCount has not changed
     void updateMorphedGeometryArray(GeometryArrayRetained geo, boolean coordinatesChanged) {
@@ -1767,8 +1819,8 @@ class MorphRetained extends LeafRetained implements GeometryUpdater {
 	        }		
 	        if ((vFormat & GeometryArray.TEXTURE_COORDINATE) != 0) {
 	            for (k = 0; k < texCoordSetCount; k++) {
-	                 morphedGeo.setTextureCoordinateIndices(k, 0, 
-	            			(int[]) igeo.indexTexCoord[k]);
+	                 morphedGeo.setTextureCoordinateIndices(k, 0,
+                                 igeo.indexTexCoord[k]);
 	            }
 	        }
             }
@@ -1797,7 +1849,7 @@ class MorphRetained extends LeafRetained implements GeometryUpdater {
 
         super.compile(compState);
 
-        // TODO: for now keep the static transform in the parent tg
+        // XXXX: for now keep the static transform in the parent tg
         compState.keepTG = true;
 
         if (J3dDebug.devPhase && J3dDebug.debug) {
diff --git a/src/classes/share/javax/media/j3d/Node.java b/src/classes/share/javax/media/j3d/Node.java
index 9be9ab3..3853a63 100644
--- a/src/classes/share/javax/media/j3d/Node.java
+++ b/src/classes/share/javax/media/j3d/Node.java
@@ -109,7 +109,34 @@ public abstract class Node extends SceneGraphObject {
      */
     public static final int
     ALLOW_LOCAL_TO_VWORLD_READ = CapabilityBits.NODE_ALLOW_LOCAL_TO_VWORLD_READ;
-    
+
+    /**
+     * Specifies that this Node allows read access to its parent Group node.
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int
+	ALLOW_PARENT_READ = CapabilityBits.NODE_ALLOW_PARENT_READ;
+
+    /**
+     * Specifies that this Node allows read access to its Locale.
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int
+        ALLOW_LOCALE_READ = CapabilityBits.NODE_ALLOW_LOCALE_READ;
+
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_BOUNDS_READ,
+        ALLOW_PICKABLE_READ,
+        ALLOW_COLLIDABLE_READ,
+        ALLOW_AUTO_COMPUTE_BOUNDS_READ,
+        ALLOW_LOCAL_TO_VWORLD_READ,
+        ALLOW_PARENT_READ,
+        ALLOW_LOCALE_READ
+    };
+
     // for checking for cycles
     private boolean visited = false;
 
@@ -125,18 +152,22 @@ public abstract class Node extends SceneGraphObject {
      * </ul>
      */
     public Node() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
-     * Retrieves the parent of this Node.  This method is only valid
-     * during the construction of the scene graph.
+
      * @return the parent of this node, or null if this node has no parent
-     * @exception RestrictedAccessException if this object is part of live
-     * or compiled scene graph
+     * @exception CapabilityNotSetException if appropriate capability is
+     * not set and this object is part of live or compiled scene graph
      */
     public Node getParent() {
-	if (isLiveOrCompiled())
-            throw new RestrictedAccessException(J3dI18N.getString("Node0"));
+	if (isLiveOrCompiled()) {
+	    if(!this.getCapability(ALLOW_PARENT_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("Node0"));
+	    }
+	}
 
 	NodeRetained nr = ((NodeRetained)this.retained).getParent();
 	return (nr == null ? null :  (Node) nr.getSource());
@@ -245,24 +276,36 @@ public abstract class Node extends SceneGraphObject {
      * of all transforms in the scene graph from the root down to
      * <code>this</code> node.  It is only valid
      * for nodes that are part of a live scene graph.
+     * If the node is not part of a live scene graph then the coordinates are
+     * calculated as if the graph was attached at the origin of a locale.
      * @param t the object that will receive the local coordinates to
      * Vworld coordinates transform.
-     * @exception RestrictedAccessException if the node is <em>not</em>
+     * @exception RestrictedAccessException if the node is compiled but not 
      * part of a live scene graph
      * @exception CapabilityNotSetException if appropriate capability is
-     * not set and this node is part of live scene graph
+     * not set and this node is part of live or compiled scene graph
      * @exception IllegalSharingException if the node is a descendant
      * of a SharedGroup node.
      */
     public void getLocalToVworld(Transform3D t) {
-	if (!isLive())
-	    throw new RestrictedAccessException(J3dI18N.getString("Node7"));
-
-	if(!this.getCapability(ALLOW_LOCAL_TO_VWORLD_READ))
-	    throw new CapabilityNotSetException(J3dI18N.getString("Node8"));
-
-	((NodeRetained)this.retained).getLocalToVworld(t);
+        if (isLiveOrCompiled()) {
+            if(!this.getCapability(ALLOW_LOCAL_TO_VWORLD_READ))
+                    throw new CapabilityNotSetException(J3dI18N.getString("Node8"));
+        }
+        
+	if (!isLive()) {
+            // TODO Support compiled graphs
+            if (isCompiled())
+                throw new RestrictedAccessException(J3dI18N.getString("Node7"));
+            
+            // In 1.4 we support getLocalToVworld for non live nodes
+            ((NodeRetained)this.retained).computeNonLiveLocalToVworld(t, this);
+	    //throw new RestrictedAccessException(J3dI18N.getString("Node7"));
+        } else {
+            ((NodeRetained)this.retained).getLocalToVworld(t);
+        }
     }
+    
 
     /**
      * Retrieves the local coordinates to virtual world coordinates
@@ -281,15 +324,44 @@ public abstract class Node extends SceneGraphObject {
      * @exception IllegalArgumentException if the specified path does
      * not contain a valid Locale, or if the last node in the path is
      * different from this node
+     * @exception IllegalSharingException if the node is not a descendant
+     * of a SharedGroup node.
      */
     public void getLocalToVworld(SceneGraphPath path, Transform3D t) {
-	if (!isLive())
+	if (!isLive()) {
 	    throw new RestrictedAccessException(J3dI18N.getString("Node7"));
-
-	if(!this.getCapability(ALLOW_LOCAL_TO_VWORLD_READ))
-	    throw new CapabilityNotSetException(J3dI18N.getString("Node8"));
+        } 
+        
+        if(!this.getCapability(ALLOW_LOCAL_TO_VWORLD_READ))
+            throw new CapabilityNotSetException(J3dI18N.getString("Node8"));
 
         ((NodeRetained)this.retained).getLocalToVworld(path,t);
+        
+    }
+
+    /**
+     * Retrieves the locale to which this node is attached. If the
+     * node is not part of a live scene graph, null is returned.
+     *
+     * @return the locale to which this node is attached.
+     *
+     * @exception CapabilityNotSetException if appropriate capability is
+     * not set and this node is part of live scene graph
+     * @exception IllegalSharingException if the node is a descendant
+     * of a SharedGroup node.
+     *
+     * @since Java 3D 1.4
+     */
+    public Locale getLocale() {
+	if (!isLive()) {
+	    return null;
+	}
+
+	if(!this.getCapability(ALLOW_LOCALE_READ)) {
+	    throw new CapabilityNotSetException(J3dI18N.getString("Node17"));
+	}
+
+	return ((NodeRetained)this.retained).getLocale();
     }
 
     /**
diff --git a/src/classes/share/javax/media/j3d/NodeData.java b/src/classes/share/javax/media/j3d/NodeData.java
index 90505e2..5c927c6 100644
--- a/src/classes/share/javax/media/j3d/NodeData.java
+++ b/src/classes/share/javax/media/j3d/NodeData.java
@@ -15,7 +15,7 @@ package javax.media.j3d;
 
 class NodeData {
     // per path node data
-    // TODO: replace per path mirror objects with node data
-    // TODO: move other basic node's data here
+    // XXXX: replace per path mirror objects with node data
+    // XXXX: move other basic node's data here
     SwitchState switchState = null;
 }
diff --git a/src/classes/share/javax/media/j3d/NodeRetained.java b/src/classes/share/javax/media/j3d/NodeRetained.java
index de91ce4..a8d9467 100644
--- a/src/classes/share/javax/media/j3d/NodeRetained.java
+++ b/src/classes/share/javax/media/j3d/NodeRetained.java
@@ -374,7 +374,26 @@ abstract class NodeRetained extends SceneGraphObjectRetained implements NnuId {
 	return;
     }    
     
-    
+    /**
+     * Compute the LocalToVworld of this node even though it is not live. We
+     * assume the graph is attached at the origin of a locale
+     */
+    void computeNonLiveLocalToVworld(Transform3D t, Node caller) {
+        NodeRetained n = getParent();
+        
+        if (n==null)
+            t.setIdentity();
+        else
+            n.computeNonLiveLocalToVworld(t, caller);
+        
+        if (this instanceof TransformGroupRetained && this.source!=caller) {
+            Transform3D trans = new Transform3D();
+            ((TransformGroupRetained)this).getTransform(trans);
+            t.mul(trans);
+        }
+        
+    }
+        
     /**
      * Get the localToVworld transform for a node.
      */
@@ -413,7 +432,19 @@ abstract class NodeRetained extends SceneGraphObjectRetained implements NnuId {
 	HashKey newKey = new HashKey(key);	
 	computeLocalToVworld(this, this, newKey, t);	
     }
-    
+
+
+    /**
+     * Get the Locale to which the node is attached
+     */
+    Locale getLocale() {
+	if (inSharedGroup) {
+	    throw new IllegalSharingException(J3dI18N.getString("NodeRetained0"));
+	}
+
+	return locale;
+    }
+
 
     /**
      * Get the current localToVworld transform for a node
@@ -868,6 +899,7 @@ abstract class NodeRetained extends SceneGraphObjectRetained implements NnuId {
 
     boolean isStatic() {
 	if (source.getCapability(Node.ALLOW_LOCAL_TO_VWORLD_READ) ||
+	    source.getCapability(Node.ALLOW_PARENT_READ) ||
 	    source.getCapability(Node.ENABLE_PICK_REPORTING) ||
 	    source.getCapability(Node.ENABLE_COLLISION_REPORTING) ||
 	    source.getCapability(Node.ALLOW_BOUNDS_READ) ||
diff --git a/src/classes/share/javax/media/j3d/NotificationThread.java b/src/classes/share/javax/media/j3d/NotificationThread.java
new file mode 100644
index 0000000..1d7a44f
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/NotificationThread.java
@@ -0,0 +1,120 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.LinkedList;
+
+/**
+ * The NotificationThread class is used for asynchronous error notification,
+ * such as notifying ShaderError listeners.
+ */
+class NotificationThread extends Thread {
+    // action flag for runMonitor
+    private static final int WAIT   = 0;
+    private static final int NOTIFY = 1;
+    private static final int STOP   = 2;
+
+    private volatile boolean running = true;
+    private boolean waiting = false;
+    private boolean ready = false;
+    
+    private LinkedList notificationQueue = new LinkedList();
+
+    /**
+     * Creates a new instance of NotificationThread 
+     */
+    NotificationThread(ThreadGroup t) {
+        // Only one notification thread for the entire system
+	super(t, "J3D-NotificationThread");
+    }
+    
+    /**
+     * Adds a notification message to the queue
+     */
+    synchronized void addNotification(J3dNotification n) {
+        notificationQueue.add(n);
+        runMonitor(NOTIFY);
+    }
+
+    /**
+     * Gets the list of queued notification messages
+     */
+    private synchronized J3dNotification[] getNotifications() {
+        J3dNotification[] notifications = (J3dNotification[])notificationQueue.toArray(new J3dNotification[0]);
+        notificationQueue.clear();
+        return notifications;
+    }
+    
+    /**
+     * Processes all pending notification messages
+     */
+    private void processNotifications() {
+        J3dNotification[] notifications = getNotifications();
+        
+        for (int i = 0; i < notifications.length; i++) {
+            J3dNotification n = notifications[i];
+            switch (n.type) {
+            case J3dNotification.SHADER_ERROR:
+                n.universe.notifyShaderErrorListeners((ShaderError)n.args[0]);
+                break;
+            default:
+                System.err.println("J3dNotification.processNotifications: unrecognized type = " + n.type);
+            }
+        }
+    }
+
+    // Called from MasterControlThread
+    void finish() {
+	runMonitor(STOP);
+    }
+
+    public void run() {
+	while (running) {
+	    runMonitor(WAIT);
+            
+            processNotifications();
+	}
+//        System.err.println("Notification thread finished");
+    }
+
+
+    private synchronized void runMonitor(int action) {
+        switch (action) {
+        case WAIT:
+            while (running && !ready) {
+                waiting = true;
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                }
+                waiting = false;
+            }
+            ready = false;
+            break;
+        case NOTIFY:
+            ready = true;
+            if (waiting) {
+                notify();
+            }
+            break;
+        case STOP:
+            running = false;
+            notify();
+            break;
+        default:
+            // Should never get here...
+            assert(false);
+        }
+    }
+
+}
diff --git a/src/classes/share/javax/media/j3d/OrderedGroup.java b/src/classes/share/javax/media/j3d/OrderedGroup.java
index e3d4e87..1ffdd93 100644
--- a/src/classes/share/javax/media/j3d/OrderedGroup.java
+++ b/src/classes/share/javax/media/j3d/OrderedGroup.java
@@ -72,6 +72,10 @@ public class OrderedGroup extends Group {
     public static final int ALLOW_CHILD_INDEX_ORDER_WRITE =
 	CapabilityBits.ORDERED_GROUP_ALLOW_CHILD_INDEX_ORDER_WRITE;
 
+    // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_CHILD_INDEX_ORDER_READ
+    };
 
     /**
      * Constructs and initializes a new OrderedGroup node object.
@@ -79,6 +83,8 @@ public class OrderedGroup extends Group {
      * that its children are rendered in increasing index order.
      */
     public OrderedGroup() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);        
     }
 
 
diff --git a/src/classes/share/javax/media/j3d/OrientedShape3D.java b/src/classes/share/javax/media/j3d/OrientedShape3D.java
index bea85dd..478eb71 100644
--- a/src/classes/share/javax/media/j3d/OrientedShape3D.java
+++ b/src/classes/share/javax/media/j3d/OrientedShape3D.java
@@ -144,6 +144,14 @@ public class OrientedShape3D extends Shape3D {
 	CapabilityBits.ORIENTED_SHAPE3D_ALLOW_SCALE_WRITE;
 
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+	ALLOW_MODE_READ,
+	ALLOW_AXIS_READ,
+	ALLOW_POINT_READ,
+	ALLOW_SCALE_READ
+    };
+
     /**
      * Constructs an OrientedShape3D node with default parameters.
      * The default values are as follows:
@@ -157,6 +165,8 @@ public class OrientedShape3D extends Shape3D {
      */
     public OrientedShape3D() {
 	super();
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
 
@@ -182,6 +192,10 @@ public class OrientedShape3D extends Shape3D {
 			   Vector3f axis) {
 
 	super(geometry, appearance);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((OrientedShape3DRetained)retained).initAlignmentMode(mode);
         ((OrientedShape3DRetained)retained).initAlignmentAxis(axis);
     }
@@ -203,6 +217,10 @@ public class OrientedShape3D extends Shape3D {
 			   Point3f point) {
 
 	super(geometry, appearance);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((OrientedShape3DRetained)retained).initAlignmentMode(mode);
         ((OrientedShape3DRetained)retained).initRotationPoint(point);
 
@@ -240,6 +258,9 @@ public class OrientedShape3D extends Shape3D {
 
 	super(geometry, appearance);
 
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((OrientedShape3DRetained)retained).initAlignmentMode(mode);
         ((OrientedShape3DRetained)retained).initAlignmentAxis(axis);
         ((OrientedShape3DRetained)retained).
@@ -272,6 +293,9 @@ public class OrientedShape3D extends Shape3D {
 
 	super(geometry, appearance);
 
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((OrientedShape3DRetained)retained).initAlignmentMode(mode);
         ((OrientedShape3DRetained)retained).initRotationPoint(point);
         ((OrientedShape3DRetained)retained).
diff --git a/src/classes/share/javax/media/j3d/PhysicalBody.java b/src/classes/share/javax/media/j3d/PhysicalBody.java
index 442e44f..40de532 100644
--- a/src/classes/share/javax/media/j3d/PhysicalBody.java
+++ b/src/classes/share/javax/media/j3d/PhysicalBody.java
@@ -128,7 +128,7 @@ public class PhysicalBody extends Object {
     synchronized void notifyUsers() {
 	for (int i=users.size()-1; i>=0; i--) {
 	    View view = (View)users.get(i);
-            // TODO: notifyUsers should have a parameter denoting field changed
+            // XXXX: notifyUsers should have a parameter denoting field changed
             if (view.soundScheduler != null) {
                 view.soundScheduler.setListenerFlag(
                         SoundScheduler.EAR_POSITIONS_CHANGED |
diff --git a/src/classes/share/javax/media/j3d/PickConeRay.java b/src/classes/share/javax/media/j3d/PickConeRay.java
index ebc2d6a..7821e1e 100644
--- a/src/classes/share/javax/media/j3d/PickConeRay.java
+++ b/src/classes/share/javax/media/j3d/PickConeRay.java
@@ -204,7 +204,7 @@ public final class PickConeRay extends PickCone {
 	    double distToEdge;
 	    for (i=0;i<ptope.nVerts;i++) {
 		for (j=i;i<ptope.nVerts;i++) {
-		    // TODO: make BoundingPolytope.pointInPolytope available to package
+		    // XXXX: make BoundingPolytope.pointInPolytope available to package
 		    // scope
 		    midpt.x = (ptope.verts[i].x + ptope.verts[j].x) * 0.5;
 		    midpt.y = (ptope.verts[i].y + ptope.verts[j].y) * 0.5;
diff --git a/src/classes/share/javax/media/j3d/PickConeSegment.java b/src/classes/share/javax/media/j3d/PickConeSegment.java
index fe2c45b..11adb80 100644
--- a/src/classes/share/javax/media/j3d/PickConeSegment.java
+++ b/src/classes/share/javax/media/j3d/PickConeSegment.java
@@ -233,7 +233,7 @@ public final class PickConeSegment extends PickCone {
 	    double distToEdge;
 	    for (i=0;i<ptope.nVerts;i++) {
 		for (j=i;i<ptope.nVerts;i++) {
-		    // TODO: make BoundingPolytope.pointInPolytope available to package
+		    // XXXX: make BoundingPolytope.pointInPolytope available to package
 		    // scope
 		    midpt.x = (ptope.verts[i].x + ptope.verts[j].x) * 0.5;
 		    midpt.y = (ptope.verts[i].y + ptope.verts[j].y) * 0.5;
diff --git a/src/classes/share/javax/media/j3d/PickCylinder.java b/src/classes/share/javax/media/j3d/PickCylinder.java
index 9d805eb..1a624f9 100644
--- a/src/classes/share/javax/media/j3d/PickCylinder.java
+++ b/src/classes/share/javax/media/j3d/PickCylinder.java
@@ -71,7 +71,7 @@ public abstract class PickCylinder extends PickShape {
 
     // This is a duplicate of the same method, declared private inside of 
     // BoundingPolytope
-    // TODO: remove this once the original method is available (public) in
+    // XXXX: remove this once the original method is available (public) in
     // BoundingPolytope
     static boolean pointInPolytope(BoundingPolytope ptope, 
 				   double x, double y, double z ){
diff --git a/src/classes/share/javax/media/j3d/PickCylinderRay.java b/src/classes/share/javax/media/j3d/PickCylinderRay.java
index 2fb33b1..6b5266f 100644
--- a/src/classes/share/javax/media/j3d/PickCylinderRay.java
+++ b/src/classes/share/javax/media/j3d/PickCylinderRay.java
@@ -185,7 +185,7 @@ public final class PickCylinderRay extends PickCylinder {
 	    double distToEdge;
 	    for (i=0;i<ptope.nVerts;i++) {
 		for (j=i;i<ptope.nVerts;i++) {
-		    // TODO: make BoundingPolytope.pointInPolytope available to package
+		    // XXXX: make BoundingPolytope.pointInPolytope available to package
 		    // scope
 		    midpt.x = (ptope.verts[i].x + ptope.verts[j].x) * 0.5;
 		    midpt.y = (ptope.verts[i].y + ptope.verts[j].y) * 0.5;
diff --git a/src/classes/share/javax/media/j3d/PickCylinderSegment.java b/src/classes/share/javax/media/j3d/PickCylinderSegment.java
index 37bb1a7..6f03cd0 100644
--- a/src/classes/share/javax/media/j3d/PickCylinderSegment.java
+++ b/src/classes/share/javax/media/j3d/PickCylinderSegment.java
@@ -205,7 +205,7 @@ public final class PickCylinderSegment extends PickCylinder {
 	    double distToEdge;
 	    for (i=0;i<ptope.nVerts;i++) {
 		for (j=i;i<ptope.nVerts;i++) {
-		    // TODO: make BoundingPolytope.pointInPolytope available to package
+		    // XXXX: make BoundingPolytope.pointInPolytope available to package
 		    // scope
 		    midpt.x = (ptope.verts[i].x + ptope.verts[j].x) * 0.5;
 		    midpt.y = (ptope.verts[i].y + ptope.verts[j].y) * 0.5;
diff --git a/src/classes/share/javax/media/j3d/PickInfo.java b/src/classes/share/javax/media/j3d/PickInfo.java
new file mode 100644
index 0000000..64a6392
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/PickInfo.java
@@ -0,0 +1,1056 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.*;
+
+/**
+ * The PickInfo object contains the computed information about a pick hit.  
+ * The detailed information about each intersection of the PickShape 
+ * with the picked Node can be inquired.  The PickInfo object is constructed with
+ * basic information and more detailed information can be generated by setting the
+ * appropriate mask to the flag argument in the pick methods of BranchGroup and 
+ * Locale.
+ * <p>
+ *
+ * @see Locale
+ * @see BranchGroup 
+ *
+ * @since Java 3D 1.4
+ */
+
+
+public class PickInfo extends Object {
+    
+    static final int PICK_ALL = 1;
+
+    static final int PICK_ANY = 2;
+
+    /* The SceneGraphPath of the intersected pickable item */
+    private SceneGraphPath sgp;
+
+    /* The intersected pickable node object */
+    private  Node node;
+    
+    /* A copy of LocalToVworld transform of the pickable node */
+    private Transform3D l2vw;
+
+    /* The closest intersection point */
+    private Point3d closestIntersectionPoint;
+ 
+    /* Distance between start point of pickShape and closest intersection point */
+    private double  closestDistance;
+
+    /* An array to store intersection results */
+    private IntersectionInfo[] intersectionInfoArr;
+    
+    /* The following references are for internal geometry computation use only */
+    private ArrayList intersectionInfoList = new ArrayList();
+    private boolean intersectionInfoListSorted = false;
+    private Transform3D l2vwRef;
+    private Node nodeRef;
+    
+    /**
+     * Specifies a Pick using the bounds of the pickable nodes.
+     */
+    public static final int PICK_BOUNDS = 1;
+    
+    /**
+     * Specifies a Pick using the geometry of the pickable nodes.
+     */
+    public static final int PICK_GEOMETRY = 2;
+    
+    /**
+   * Specifies that this PickInfo returns the computed SceneGraphPath object.
+   */
+    public static final int SCENEGRAPHPATH  = 0x01;
+    
+    /**
+     * Specifies that this PickInfo returns the computed intersected Node object.
+     */
+    public static final int NODE = 0x02;
+    
+    /**
+     * Specifies that this PickInfo returns the computed local to vworld transform.
+     */
+    public static final int LOCAL_TO_VWORLD = 0x04;
+    
+    /**
+     * Specifies that this PickInfo returns the closest intersection point.
+     */
+    public static final int CLOSEST_INTERSECTION_POINT = 0x08;
+
+    /**
+     * Specifies that this PickInfo returns the closest intersection distance.
+     */
+    public static final int CLOSEST_DISTANCE = 0x10;
+
+    /**
+     * Specifies that this PickInfo returns only the closest intersection 
+     * geometry information.
+     */
+    public static final int CLOSEST_GEOM_INFO = 0x20;
+
+    /**
+     * Specifies that this PickInfo returns all the closest intersection 
+     * geometry informations.
+     */
+    public static final int ALL_GEOM_INFO = 0x40;
+
+
+    /** PickInfo Constructor */
+    PickInfo() {
+
+    }
+    
+    void setSceneGraphPath(SceneGraphPath sgp) {
+        this.sgp = sgp;
+    }
+    
+    void setNode(Node node) {
+        this.node = node;
+    }
+    
+    void setLocalToVWorld(Transform3D l2vw) {
+        this.l2vw = l2vw;
+    }
+    
+    void setClosestIntersectionPoint(Point3d cIPt) {
+        this.closestIntersectionPoint = cIPt;
+    }
+    
+    void setClosestDistance(double cDist) {
+        this.closestDistance = cDist;
+    }
+    
+    void setLocalToVWorldRef(Transform3D l2vwRef) {
+        this.l2vwRef = l2vwRef;
+    }
+    
+    void setNodeRef(Node nodeRef) {
+        this.nodeRef = nodeRef;
+    }
+    
+    IntersectionInfo createIntersectionInfo() {
+        return new IntersectionInfo();
+    }
+    
+    void insertIntersectionInfo(IntersectionInfo iInfo) {
+        intersectionInfoList.add(iInfo);
+        intersectionInfoListSorted = false;
+    }
+    
+    void sortIntersectionInfoArray(IntersectionInfo[] iInfoArr) {
+
+        class Sort {
+	    
+	    IntersectionInfo iInfoArr[];
+
+	    Sort(IntersectionInfo[] iInfoArr) {
+                // System.out.println("Sort IntersectionInfo ...");
+		this.iInfoArr = iInfoArr;
+	    }
+
+	    void sorting() {
+		if (iInfoArr.length < 7) {
+                    // System.out.println(" -- insertSort.");
+		    insertSort();
+	    	} else {
+                    // System.out.println(" -- quicksort.");                    
+		    quicksort(0, iInfoArr.length-1);
+    		}
+	    }
+
+	    // Insertion sort on smallest arrays
+	    final void insertSort() {
+		for (int i=0; i<iInfoArr.length; i++) {
+		    for (int j=i; j>0 && 
+                             (iInfoArr[j-1].distance > iInfoArr[j].distance); j--) {
+			IntersectionInfo iInfo = iInfoArr[j];
+			iInfoArr[j] = iInfoArr[j-1];
+			iInfoArr[j-1] = iInfo;
+		    }
+		}
+	    }
+
+            final void quicksort( int l, int r ) {
+		int i = l;
+		int j = r;
+		double k = iInfoArr[(l+r) / 2].distance;
+
+		do {
+		    while (iInfoArr[i].distance<k) i++;
+		    while (k<iInfoArr[j].distance) j--;
+		    if (i<=j) {			
+			IntersectionInfo iInfo = iInfoArr[i];
+			iInfoArr[i] = iInfoArr[j];
+			iInfoArr[j] = iInfo;
+			i++;
+			j--;
+		    }
+		} while (i<=j);
+		
+		if (l<j) quicksort(l,j);
+		if (l<r) quicksort(i,r);
+	    }
+	}
+
+	(new Sort(iInfoArr)).sorting();            
+        intersectionInfoListSorted = true;
+    }
+
+    static void sortPickInfoArray(PickInfo[] pickInfoArr) {
+    
+        class Sort {
+	    
+	    PickInfo pIArr[];
+
+	    Sort(PickInfo[] pIArr) {
+                // System.out.println("Sort PickInfo ...");
+		this.pIArr = pIArr;
+	    }
+
+	    void sorting() {
+		if (pIArr.length < 7) {
+                    // System.out.println(" -- insertSort.");
+		    insertSort();
+	    	} else {
+                    // System.out.println(" -- quicksort.");                    
+		    quicksort(0, pIArr.length-1);
+    		}
+	    }
+
+	    // Insertion sort on smallest arrays
+	    final void insertSort() {
+		for (int i=0; i<pIArr.length; i++) {
+		    for (int j=i; j>0 && 
+                             (pIArr[j-1].closestDistance > pIArr[j].closestDistance); j--) {
+			PickInfo pI = pIArr[j];
+			pIArr[j] = pIArr[j-1];
+			pIArr[j-1] = pI;
+		    }
+		}
+	    }
+
+            final void quicksort( int l, int r ) {
+		int i = l;
+		int j = r;
+		double k = pIArr[(l+r) / 2].closestDistance;
+
+		do {
+		    while (pIArr[i].closestDistance<k) i++;
+		    while (k<pIArr[j].closestDistance) j--;
+		    if (i<=j) {			
+			PickInfo pI = pIArr[i];
+			pIArr[i] = pIArr[j];
+			pIArr[j] = pI;
+			i++;
+			j--;
+		    }
+		} while (i<=j);
+		
+		if (l<j) quicksort(l,j);
+		if (l<r) quicksort(i,r);
+	    }
+	}
+
+	(new Sort(pickInfoArr)).sorting();        
+        
+    }
+
+    
+    /**
+     * Retrieves the reference to the SceneGraphPath in this PickInfo object.
+     * @return the SceneGraphPath object, or null if  flag is not set with SCENEGRAPHPATH.
+     * @see Locale
+     * @see BranchGroup
+     */
+    public SceneGraphPath getSceneGraphPath() {
+	return sgp;
+    }
+
+    /**
+     * Retrieves the reference to the picked node, either a Shape3D or a Morph, in this PickInfo object.
+     * @return the picked leaf node object, or null if  flag is not set with NODE.
+     * @see Locale
+     * @see BranchGroup
+     */
+    public Node getNode() {
+	return node;
+    }
+
+    /**
+     * Retrieves the reference to the LocalToVworld transform of the picked node in this PickInfo object.
+     * @return the local to vworld transform, or null if  flag is not set with LOCAL_TO_VWORLD.
+     * @see Locale
+     * @see BranchGroup
+     */
+    public Transform3D getLocalToVWorld() {
+	return l2vw;
+    }
+	
+    /**
+     * Retrieves the reference to the closest intersection point in this PickInfo object.
+     * @return the closest intersection point, or null if  flag is not set with CLOSEST_INTERSECTION_POINT.
+     * @see Locale
+     * @see BranchGroup
+     */
+    public Point3d getClosestIntersectionPoint() {
+	return closestIntersectionPoint;
+    }
+
+    /**
+     * Retrieves the distance between the start point of the pickShape and the closest intersection point.
+     * @return the closest distance in double, or NaN if  flag is not set with CLOSEST_INTERSECTION_POINT.
+     * Note : If this PickInfo object is returned by either pickClosest or pickAllSorted method, the return
+     * value is the closest distance in double even if flag is not set with CLOSET_INTERSECTION_POINT.
+     * @see Locale
+     * @see BranchGroup
+     */
+    public double getClosestDistance() {
+	return closestDistance;
+    }
+
+    Transform3D getLocalToVWorldRef() {
+        return l2vwRef;
+    }
+    
+    Node getNodeRef() {
+        return nodeRef;
+    }
+    
+    /**
+     * Retrieves the reference to the array of intersection results in this PickInfo object.
+     * @return an array of 1 IntersectionInfo object if flag is to set  CLOSEST_GEOM_INFO,
+     * or an array of <i>N</i> IntersectionInfo objects containing all intersections of 
+     * the picked node in sorted order if flag is to set ALL_GEOM_INFO, or null if neither 
+     * bit is set.
+     * @see Locale
+     * @see BranchGroup
+     */
+    public IntersectionInfo[] getIntersectionInfos() {
+        if (intersectionInfoListSorted == false) {
+            intersectionInfoArr = new IntersectionInfo[intersectionInfoList.size()];
+            intersectionInfoArr =
+                    (IntersectionInfo []) intersectionInfoList.toArray(intersectionInfoArr);
+            
+            sortIntersectionInfoArray(intersectionInfoArr);                    
+         }
+        
+        return intersectionInfoArr;
+    }
+     
+    /**
+     * Search the path from nodeR up to Locale.
+     * Return the search path as ArrayList if found.
+     * Note that the locale will not insert into path.
+     */
+    static ArrayList initSceneGraphPath(NodeRetained nodeR) { 
+	ArrayList path = new ArrayList(5);
+
+	do {
+	    if (nodeR.source.getCapability(Node.ENABLE_PICK_REPORTING)){
+		path.add(nodeR);  
+	    }
+	    nodeR = nodeR.parent;
+	} while (nodeR != null);  // reach Locale
+
+	return path;
+    }    
+
+    static private Node[] createPath(NodeRetained srcNode,
+				     BranchGroupRetained bgRetained,
+				     GeometryAtom geomAtom,
+				     ArrayList initpath) {
+        
+        ArrayList path = retrievePath(srcNode, bgRetained,
+				      geomAtom.source.key);
+        assert(path != null);
+        
+        return mergePath(path, initpath);
+
+    }
+    
+    
+    /**
+     * Return true if bg is inside cachedBG or bg is null
+     */
+    static private boolean inside(BranchGroupRetained bgArr[],
+				  BranchGroupRetained bg) {
+
+	if ((bg == null) || (bgArr == null)) {
+	    return true;
+	}
+
+	for (int i=0; i < bgArr.length; i++) {
+	    if (bgArr[i] == bg) {
+		return true;
+	    }
+	}
+	return false;
+    }
+
+    /**
+     * search the full path from the bottom of the scene graph -
+     * startNode, up to the Locale if endNode is null.
+     * If endNode is not null, the path is found up to, but not
+     * including, endNode or return null if endNode not hit 
+     * during the search.
+     */
+    static private ArrayList retrievePath(NodeRetained startNode, 
+					  NodeRetained endNode,
+					  HashKey key) {
+
+	ArrayList path = new ArrayList(5);
+	NodeRetained nodeR = startNode;
+	
+	if (nodeR.inSharedGroup) {
+	    // getlastNodeId() will destroy this key
+	    key = new HashKey(key);
+	}
+
+	do {
+	    if (nodeR == endNode) { // we found it !
+		return path;
+	    }
+
+	    if (nodeR.source.getCapability(Node.ENABLE_PICK_REPORTING)) {
+		path.add(nodeR);  
+	    }
+
+	    if (nodeR instanceof SharedGroupRetained) {
+		// retrieve the last node ID
+		String nodeId = key.getLastNodeId();
+		Vector parents = ((SharedGroupRetained) nodeR).parents;
+		int sz = parents.size();
+		NodeRetained prevNodeR = nodeR;
+		for(int i=0; i< sz; i++) {
+		    NodeRetained linkR = (NodeRetained) parents.elementAt(i);
+		    if (linkR.nodeId.equals(nodeId)) {
+			nodeR = linkR;
+			// Need to add Link to the path report
+			path.add(nodeR);
+			// since !(endNode instanceof Link), we 
+			// can skip the check (nodeR == endNode) and
+			// proceed to parent of link below
+			break;
+		    }
+		}
+		if (nodeR == prevNodeR) { 
+		    // branch is already detach
+		    return null;
+		}
+	    }
+	    nodeR = nodeR.parent;
+	} while (nodeR != null); // reach Locale
+	
+	if (endNode == null) {
+	    // user call pickxxx(Locale locale, PickShape shape)
+	    return path;
+	}
+
+	// user call pickxxx(BranchGroup endNode, PickShape shape)
+	// if locale is reached and endNode not hit, this is not
+	// the path user want to select 
+	return null;
+    }
+    
+    /**
+     * copy p1, (follow by) p2 into a new array, p2 can be null
+     * The path is then reverse before return.
+     */
+    static private Node[] mergePath(ArrayList p1, ArrayList p2) {
+	int s = p1.size();
+	int len;
+	int i;
+	int l;
+	if (p2 == null) {
+	    len = s;
+	} else {
+	    len = s + p2.size();
+	}
+
+	Node nodes[] = new Node[len];
+	l = len-1;
+	for (i=0; i < s; i++) {
+	    nodes[l-i] = (Node) ((NodeRetained) p1.get(i)).source;
+	}
+	for (int j=0; i< len; i++, j++) {
+	    nodes[l-i] = (Node) ((NodeRetained) p2.get(j)).source;
+	}
+	return nodes;
+    }
+
+    /**
+     * Sort the GeometryAtoms distance from shape in ascending order
+     * geomAtoms.length must be >= 1
+     */
+    static void sortGeomAtoms(GeometryAtom geomAtoms[], 
+				      PickShape shape) {
+
+	final double distance[] = new double[geomAtoms.length];
+	Point4d pickPos = new Point4d();
+
+	for (int i=0; i < geomAtoms.length; i++) {
+	    shape.intersect(geomAtoms[i].source.vwcBounds, pickPos);	    
+	    distance[i] = pickPos.w;
+	}
+
+	class Sort {
+	    
+	    GeometryAtom atoms[];
+
+	    Sort(GeometryAtom[] atoms) {
+		this.atoms = atoms;
+	    }
+
+	    void sorting() {
+		if (atoms.length < 7) {
+		    insertSort();
+	    	} else {
+		    quicksort(0, atoms.length-1);
+    		}
+	    }
+
+	    // Insertion sort on smallest arrays
+	    final void insertSort() {
+		for (int i=0; i<atoms.length; i++) {
+		    for (int j=i; j>0 && 
+			     (distance[j-1] > distance[j]); j--) {
+			double t = distance[j];
+			distance[j] = distance[j-1];
+			distance[j-1] = t;
+			GeometryAtom p = atoms[j];
+			atoms[j] = atoms[j-1];
+			atoms[j-1] = p;
+		    }
+		}
+	    }
+
+            final void quicksort( int l, int r ) {
+		int i = l;
+		int j = r;
+		double k = distance[(l+r) / 2];
+
+		do {
+		    while (distance[i]<k) i++;
+		    while (k<distance[j]) j--;
+		    if (i<=j) {
+			double tmp = distance[i];
+			distance[i] =distance[j];
+			distance[j] = tmp;
+			
+			GeometryAtom p=atoms[i];
+			atoms[i]=atoms[j];
+			atoms[j]=p;
+			i++;
+			j--;
+		    }
+		} while (i<=j);
+		
+		if (l<j) quicksort(l,j);
+		if (l<r) quicksort(i,r);
+	    }
+	}
+
+	(new Sort(geomAtoms)).sorting();
+    }
+
+
+    /**
+     * return all PickInfo[] of the geomAtoms.
+     * If initpath is null, the path is search from 
+     * geomAtom Shape3D/Morph Node up to Locale
+     * (assume the same locale).
+     * Otherwise, the path is search up to node or 
+     * null is return if it is not hit.
+     */   
+    static ArrayList getPickInfos(ArrayList initpath, 
+                                  BranchGroupRetained bgRetained, 
+				  GeometryAtom geomAtoms[],
+			          Locale locale, int flags, int pickType) {
+
+        ArrayList pickInfoList = new ArrayList(5);
+        NodeRetained srcNode;
+        ArrayList text3dList = null;
+
+        if ((geomAtoms == null) || (geomAtoms.length == 0)) {
+            return null;
+        }
+        
+	for (int i=0; i < geomAtoms.length; i++) {
+            assert((geomAtoms[i] != null) &&
+                    (geomAtoms[i].source != null));
+            
+	    PickInfo pickInfo = null;
+            Shape3DRetained shape = geomAtoms[i].source;
+            srcNode = shape.sourceNode;
+
+            if (srcNode == null) {
+                // The node is just detach from branch so sourceNode = null
+                continue;
+            }
+            
+            // Special case, for Text3DRetained, it is possible
+            // for different geomAtoms pointing to the same
+            // source Text3DRetained. So we need to combine
+            // those cases and report only once.
+            if (srcNode instanceof Shape3DRetained) {
+                Shape3DRetained s3dR = (Shape3DRetained) srcNode;
+                GeometryRetained geomR = null;
+                for(int cnt=0; cnt<s3dR.geometryList.size(); cnt++) {
+                    geomR = (GeometryRetained) s3dR.geometryList.get(cnt);
+                    if(geomR != null)
+                        break;
+                }
+                
+                if (geomR == null)
+                    continue;
+                
+                if (geomR instanceof Text3DRetained) {
+                    // assume this case is not frequent, we allocate
+                    // ArrayList only when necessary and we use ArrayList
+                    // instead of HashMap since the case of when large
+                    // number of distingish Text3DRetained node hit is
+                    // rare.
+                    if (text3dList == null) {
+                        text3dList = new ArrayList(3);
+                    } else {
+                        int size = text3dList.size();
+                        boolean found = false;
+                        for (int j=0; j < size; j++) {
+                            if (text3dList.get(j) == srcNode) {
+                                found = true;
+                                break;
+                            }
+                        }
+                        if (found) {
+                            continue;  // try next geomAtom
+                        }
+                    }
+                    text3dList.add(srcNode);
+                }
+            }
+                        
+            // If srcNode is instance of compile retained, then loop thru
+            // the entire source list and add it to the scene graph path
+            if (srcNode instanceof Shape3DCompileRetained) {
+
+                Shape3DCompileRetained s3dCR = (Shape3DCompileRetained)srcNode;
+                
+                Node[] mpath = null;
+                boolean first = true;
+                
+                for (int n = 0; n < s3dCR.srcList.length; n++) {
+                    
+                    pickInfo = null;
+                    
+                    // PickInfo.SCENEGRAPHPATH - request for computed SceneGraphPath.
+                    if (((flags & SCENEGRAPHPATH) != 0) && 
+                         (inside(shape.branchGroupPath,bgRetained))){
+                        
+                        if(first) {
+                            mpath = createPath(srcNode, bgRetained, geomAtoms[i], initpath);
+                            first = false;
+                        }
+                        
+                        if(mpath != null) {
+                            SceneGraphPath sgpath = new SceneGraphPath(locale,
+                                    mpath, (Node) s3dCR.srcList[n]);
+                            sgpath.setTransform(shape.getCurrentLocalToVworld(0));
+			    if(pickInfo == null)
+				pickInfo = new PickInfo();
+                            pickInfo.setSceneGraphPath(sgpath);
+                        }
+                    }
+
+                    // PickInfo.NODE - request for computed intersected Node.
+                    if ((flags & NODE) != 0) {
+			if(pickInfo == null)
+			    pickInfo = new PickInfo();
+                        pickInfo.setNode((Node) s3dCR.srcList[n]);
+                    }
+                    
+                    // PickInfo.LOCAL_TO_VWORLD
+                    //    - request for computed local to virtual world transform.
+                    if ((flags & LOCAL_TO_VWORLD) != 0) {
+                        Transform3D l2vw = geomAtoms[i].source.getCurrentLocalToVworld();
+			if(pickInfo == null)
+			    pickInfo = new PickInfo();
+                        pickInfo.setLocalToVWorld( new Transform3D(l2vw));
+                    }
+		    
+                    // NOTE : Piggy bag for geometry computation by caller.
+                    if (((flags & CLOSEST_DISTANCE) != 0) ||
+                        ((flags & CLOSEST_GEOM_INFO) != 0) ||
+                        ((flags & CLOSEST_INTERSECTION_POINT) != 0) ||
+                        ((flags & ALL_GEOM_INFO) != 0)) {
+
+                        pickInfo.setNodeRef((Node) s3dCR.srcList[n]);
+                        Transform3D l2vw = geomAtoms[i].source.getCurrentLocalToVworld();
+			if(pickInfo == null)
+			    pickInfo = new PickInfo();
+			pickInfo.setLocalToVWorldRef(l2vw);
+                    }
+
+		    if(pickInfo != null)
+			pickInfoList.add(pickInfo);
+                    if(pickType == PICK_ANY) {
+                        return pickInfoList;                      
+                    }
+                }    
+            }
+            else {
+                Node[] mpath = null;
+
+                // PickInfo.SCENEGRAPHPATH - request for computed SceneGraphPath.
+                if (((flags & SCENEGRAPHPATH) != 0)  && 
+                    (inside(shape.branchGroupPath,bgRetained))) {
+
+                    mpath = createPath(srcNode, bgRetained, geomAtoms[i], initpath);
+                    
+                    if(mpath != null) {
+                        SceneGraphPath sgpath = new SceneGraphPath(locale, mpath,
+                                (Node) srcNode.source);
+                        sgpath.setTransform(shape.getCurrentLocalToVworld(0));
+			if(pickInfo == null) 
+			    pickInfo = new PickInfo();
+                        pickInfo.setSceneGraphPath(sgpath);
+                    }
+                }
+                
+                // PickInfo.NODE - request for computed intersected Node.
+                if ((flags & NODE) != 0) {
+		    if(pickInfo == null) 
+			pickInfo = new PickInfo();
+                    pickInfo.setNode((Node) srcNode.source);
+                }
+                
+                // PickInfo.LOCAL_TO_VWORLD
+                //    - request for computed local to virtual world transform.
+                if ((flags & LOCAL_TO_VWORLD) != 0) {
+                    Transform3D l2vw = geomAtoms[i].source.getCurrentLocalToVworld();
+		    if(pickInfo == null) 
+			pickInfo = new PickInfo();
+                    pickInfo.setLocalToVWorld( new Transform3D(l2vw));
+                }
+                
+                // NOTE : Piggy bag for geometry computation by caller.
+                if (((flags & CLOSEST_DISTANCE) != 0) ||
+                    ((flags & CLOSEST_GEOM_INFO) != 0) ||
+                    ((flags & CLOSEST_INTERSECTION_POINT) != 0) ||
+                    ((flags & ALL_GEOM_INFO) != 0)) {
+
+                    pickInfo.setNodeRef((Node) srcNode.source);                    
+                    Transform3D l2vw = geomAtoms[i].source.getCurrentLocalToVworld();
+		    if(pickInfo == null) 
+			pickInfo = new PickInfo();
+                    pickInfo.setLocalToVWorldRef(l2vw);
+                }
+
+		if(pickInfo != null)
+		    pickInfoList.add(pickInfo);
+                if(pickType == PICK_ANY) {
+                    return pickInfoList;                      
+                }
+            }
+        }
+
+	return pickInfoList;
+    }
+
+    static PickInfo[] pick(Object node, GeometryAtom[] geomAtoms,
+            int mode, int flags, PickShape pickShape, int pickType) {
+
+        int pickInfoListSize;
+        PickInfo[] pickInfoArr = null;
+        Locale locale = null;
+        BranchGroupRetained bgRetained = null;
+        ArrayList initPath = null;
+        ArrayList pickInfoList = null;
+	
+        if (node instanceof Locale) {
+            locale = (Locale) node;
+        }
+        else if ( node instanceof BranchGroupRetained) {
+            bgRetained = (BranchGroupRetained) node;
+            locale = bgRetained.locale;
+        }
+        synchronized (locale.universe.sceneGraphLock) {
+            if ( bgRetained != null) {
+                initPath = initSceneGraphPath(bgRetained);                
+            }
+            pickInfoList = getPickInfos(initPath, bgRetained, geomAtoms,
+                locale, flags, pickType);
+        }
+        
+        // We're done with PICK_BOUNDS case, but there is still more work for PICK_GEOMETRY case.
+        if((mode == PICK_GEOMETRY) && ((pickInfoListSize = pickInfoList.size()) > 0)) {
+            
+            //System.out.println("PickInfo.pick() - In geometry case : pickInfoList.size() is " + pickInfoListSize);
+            PickInfo pickInfo = null;
+            Node pickNode = null;
+            
+            // Order is impt. Need to do in reverse order.    
+            for(int i = pickInfoListSize - 1; i >= 0; i--) {
+                pickInfo = (PickInfo) pickInfoList.get(i);
+                pickNode = pickInfo.getNode();
+                
+                if (pickNode instanceof Shape3D) {
+
+		    /*
+		     * @exception CapabilityNotSetException if the mode is
+		     * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit
+		     * is not set in any Geometry objects referred to by any shape
+		     * node whose bounds intersects the PickShape.
+		     *
+                     * @exception CapabilityNotSetException if flags contains any of
+		     * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO
+		     * or ALL_GEOM_INFO, and the capability bits that control reading of
+		     * coordinate data are not set in any GeometryArray object referred
+		     * to by any shape node that intersects the PickShape.
+		     * The capability bits that must be set to avoid this exception are 
+                     * as follows :
+		     * 
+		     * By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ
+		     * By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ
+		     * Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ
+		     * (in addition to one of the above)
+		     *     
+		     */
+		    
+                    if (!pickNode.getCapability(Shape3D.ALLOW_GEOMETRY_READ)) {
+			throw new CapabilityNotSetException(J3dI18N.getString("PickInfo0"));
+		    }
+		    
+		    for (int j = 0; j < ((Shape3D)pickNode).numGeometries(); j++) {
+			Geometry geo = ((Shape3D)pickNode).getGeometry(j);
+			
+			if(!geo.getCapability(Geometry.ALLOW_INTERSECT)) {
+			    throw new CapabilityNotSetException(J3dI18N.getString("PickInfo1"));
+			}
+
+			if (geo instanceof GeometryArray) {
+			    if(!geo.getCapability(GeometryArray.ALLOW_COORDINATE_READ))
+				throw new CapabilityNotSetException(J3dI18N.getString("PickInfo2"));
+			    if(!geo.getCapability(GeometryArray.ALLOW_COUNT_READ))
+				throw new CapabilityNotSetException(J3dI18N.getString("PickInfo3"));
+			    if(!geo.getCapability(GeometryArray.ALLOW_FORMAT_READ))
+				throw new CapabilityNotSetException(J3dI18N.getString("PickInfo4")); 
+			    if (geo instanceof IndexedGeometryArray) {
+				if(!geo.getCapability(IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ))
+				    throw new CapabilityNotSetException(J3dI18N.getString("PickInfo5"));
+			    }				
+			} else if (geo instanceof CompressedGeometry) {
+			    if(!geo.getCapability(CompressedGeometry.ALLOW_GEOMETRY_READ))
+				throw new CapabilityNotSetException(J3dI18N.getString("PickInfo0"));
+			}
+		    }
+		    
+		    if (((Shape3DRetained)(pickNode.retained)).intersect(pickInfo, pickShape, flags) == false) {
+			// System.out.println("  ---- geom " + i + " not intersected");
+
+                        pickInfoList.remove(i);			
+
+                    }
+                    else if(pickType == PICK_ANY) {
+                        pickInfoArr = new PickInfo[1];
+                        pickInfoArr[0] = pickInfo;
+                        return pickInfoArr;
+                    }
+                } else if (pickNode instanceof Morph) {
+		    
+		    /*
+		     * @exception CapabilityNotSetException if the mode is
+		     * PICK_GEOMETRY and the Geometry.ALLOW_INTERSECT capability bit
+		     * is not set in any Geometry objects referred to by any shape
+		     * node whose bounds intersects the PickShape.
+		     *
+                     * @exception CapabilityNotSetException if flags contains any of
+		     * CLOSEST_INTERSECTION_POINT, CLOSEST_DISTANCE, CLOSEST_GEOM_INFO
+		     * or ALL_GEOM_INFO, and the capability bits that control reading of
+		     * coordinate data are not set in any GeometryArray object referred
+		     * to by any shape node that intersects the PickShape.
+		     * The capability bits that must be set to avoid this exception are 
+                     * as follows :
+		     * 
+		     * By-copy geometry : GeometryArray.ALLOW_COORDINATE_READ
+		     * By-reference geometry : GeometryArray.ALLOW_REF_DATA_READ
+		     * Indexed geometry : IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ
+		     * (in addition to one of the above)
+		     *     
+		     */
+
+                    if (!pickNode.getCapability(Morph.ALLOW_GEOMETRY_ARRAY_READ)) {
+			throw new CapabilityNotSetException(J3dI18N.getString("PickInfo6"));
+		    }
+		    
+		    int numGeo = ((MorphRetained)(pickNode.retained)).getNumGeometryArrays();
+		    for (int j = 0; j < numGeo; j++) {
+			GeometryArray geo = ((Morph)pickNode).getGeometryArray(j);
+
+			if(!geo.getCapability(Geometry.ALLOW_INTERSECT)) {
+			    throw new CapabilityNotSetException(J3dI18N.getString("PickInfo1"));
+			}
+
+			if(!geo.getCapability(GeometryArray.ALLOW_COORDINATE_READ))
+			    throw new CapabilityNotSetException(J3dI18N.getString("PickInfo2"));
+			if(!geo.getCapability(GeometryArray.ALLOW_COUNT_READ))
+			    throw new CapabilityNotSetException(J3dI18N.getString("PickInfo3"));
+			if(!geo.getCapability(GeometryArray.ALLOW_FORMAT_READ))
+			    throw new CapabilityNotSetException(J3dI18N.getString("PickInfo4"));
+			
+			if (geo instanceof IndexedGeometryArray) {
+			    if(!geo.getCapability(IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ))
+				throw new CapabilityNotSetException(J3dI18N.getString("PickInfo5"));   
+			}
+		    }
+
+                    if (((MorphRetained)(pickNode.retained)).intersect(pickInfo, pickShape, flags) == false) {
+                        pickInfoList.remove(i);                        
+                    }
+                    else if(pickType == PICK_ANY) {
+                        pickInfoArr = new PickInfo[1];
+                        pickInfoArr[0] = pickInfo;
+                        return pickInfoArr;                        
+                    }                    
+                }
+            }
+        }
+
+	// System.out.println("PickInfo : pickInfoList " + pickInfoList);
+
+        if ((pickInfoList != null) && (pickInfoList.size() > 0)) {
+	    // System.out.println("   ---  : pickInfoList.size() " + pickInfoList.size());
+	    // System.out.println("   ---  : pickInfoList's sgp " + 
+	    // ((PickInfo)(pickInfoList.get(0))).getSceneGraphPath());
+	    pickInfoArr = new PickInfo[pickInfoList.size()];
+	    return (PickInfo []) pickInfoList.toArray(pickInfoArr); 
+	}
+	
+	return null;
+	    
+    }
+    
+    /**
+     * The IntersectionInfo object holds extra information about an intersection 
+     * of a PickShape with a Node as part of a PickInfo. Information such as 
+     * the intersected geometry, the intersected point, and the vertex indices 
+     * can be inquired.  
+     * The local coordinates, normal, color and texture coordiantes of at the
+     * intersection can be computed, if they are present and readable, using the 
+     * interpolation weights and vertex indices.
+     * <p>
+     * If the Shape3D being picked has multiple geometry arrays, the possible arrays
+     * of IntersectionInfo are stored in the PickInfo and referred to by a geometry 
+     * index. If the picked geometry is of type, Text3D or CompressGeometry, 
+     * getVertexIndices is invalid. If the picked Node is an Morph 
+     * object, the geometry used in pick computation is alway at index 0.
+     * <p>
+     *
+     * @since Java 3D 1.4
+     */				    
+    
+    public class IntersectionInfo extends Object {
+	
+	/* The index to the intersected geometry in the pickable node */
+	private int geomIndex;
+
+        /* The reference to the intersected geometry in the pickable object */
+	private Geometry geom;
+
+	/* The intersection point */
+	private Point3d intersectionPoint;
+
+	/* Distance between start point of pickShape and intersection point */
+	private double  distance;
+
+	/* The vertex indices of the intersected primitive in the geometry */   
+	private int[] vertexIndices;
+
+	/* The interpolation weights for each of the verticies of the primitive */
+	// private float[] weights;  Not supported. Should be done in util. package  
+
+	/** IntersectionInfo Constructor */
+	IntersectionInfo() {
+
+	}
+
+        void setGeometryIndex(int geomIndex) {
+            this.geomIndex = geomIndex;
+        }
+        
+        void setGeometry(Geometry geom) {
+            this.geom = geom;
+        }
+        
+        void setIntersectionPoint(Point3d intersectionPoint) {
+            this.intersectionPoint = intersectionPoint;
+        }
+        
+        void setDistance(double distance) {
+            this.distance = distance;
+        }       
+        
+        void setVertexIndices(int[] vertexIndices) {
+	    this.vertexIndices = vertexIndices;
+	}
+
+        
+	/**
+	 * Retrieves the index to the intersected geometry in the picked node, either a Shape3D or Morph.
+	 * @return the index of the intersected geometry in the pickable node.
+	 */
+	public int getGeometryIndex() {
+	    return geomIndex;
+	}
+
+	/**
+	 * Retrieves the reference to the intersected geometry in the picked object, either a Shape3D or Morph.
+	 * @return the intersected geometry in the pickable node.
+	 */
+	public Geometry getGeometry() {
+	    return geom;
+	}
+
+	/**
+	 * Retrieves the reference to the intersection point in the pickable node.
+	 * @return the intersected point in the pickable node.
+	 */
+	public Point3d getIntersectionPoint() {
+	    return intersectionPoint;
+	}
+
+	/**
+	 * Retrieves the distance between the start point of the pickShape and the 
+	 * intersection point.
+	 * @return distance between the start point of the pickShape and the 
+	 * intersection point.
+	 */
+	public double getDistance() {	    
+	    return distance;
+	}
+        
+	/**
+	 * Retrieves the vertex indices of the intersected primitive in the geometry.
+	 * @return the vertex indices of the intersected primitive.
+	 */
+	public int[] getVertexIndices() {
+	    return vertexIndices;
+	}
+
+    }
+}
+
+
diff --git a/src/classes/share/javax/media/j3d/PickPoint.java b/src/classes/share/javax/media/j3d/PickPoint.java
index 3001af1..22ec12b 100644
--- a/src/classes/share/javax/media/j3d/PickPoint.java
+++ b/src/classes/share/javax/media/j3d/PickPoint.java
@@ -20,6 +20,10 @@ import javax.vecmath.*;
  *
  * @see BranchGroup#pickAll
  * @see Locale#pickAll
+ * @see PickBounds
+ *
+ * @deprecated As of Java 3D version 1.4, use PickBounds with a
+ * BoundingSphere that has a small radius.
  */
 public final class PickPoint extends PickShape {
 
diff --git a/src/classes/share/javax/media/j3d/Picking.java b/src/classes/share/javax/media/j3d/Picking.java
deleted file mode 100644
index 952248d..0000000
--- a/src/classes/share/javax/media/j3d/Picking.java
+++ /dev/null
@@ -1,660 +0,0 @@
-/*
- * $RCSfile$
- *
- * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
- *
- * Use is subject to license terms.
- *
- * $Revision$
- * $Date$
- * $State$
- */
-
-package javax.media.j3d;
-
-import javax.vecmath.*;
-import java.util.*;
-
-
-/**
- * Internal class that implements picking functionality.
- */
-
-class Picking {
-
-    static SceneGraphPath[] pickAll(Locale locale, PickShape shape) {
-	if(locale == null) {
-	    return null;
-	}
-
-	GeometryAtom geomAtoms[] =
-	    locale.universe.geometryStructure.pickAll(locale, shape);
-	if ((geomAtoms == null) || (geomAtoms.length == 0)) { 
-	    // although getSceneGraphPath() also return null, we
-	    // save time for synchronization
-	    return null;  
-	}
-	synchronized (locale.universe.sceneGraphLock) {
-	    return getSceneGraphPath(null, null, geomAtoms, locale); 
-	}
-    }
-  
-
-
-    static SceneGraphPath[] pickAll(BranchGroup node, 
-				    PickShape shape) {
-	if (node == null) {
-	    return null;
-	}
-
-	BranchGroupRetained nodeR = (BranchGroupRetained) node.retained;
-
-	if (nodeR.inSharedGroup) {
-	    throw new RestrictedAccessException(J3dI18N.getString("Picking0"));
-	}
-
-	Locale locale = nodeR.locale;
-							 
-	GeometryAtom geomAtoms[] =
-	    locale.universe.geometryStructure.pickAll(locale, shape);
-
-	if ((geomAtoms == null) || (geomAtoms.length == 0)) { 
-	    return null;
-	}
-
-	synchronized (nodeR.universe.sceneGraphLock) {
-	    return getSceneGraphPath(initSceneGraphPath(nodeR),
-				     nodeR, geomAtoms, locale);
-	}
-    }
-
-  
-    static SceneGraphPath[] pickAllSorted(Locale locale, 
-					  PickShape shape) {
-	if(locale == null) {
-	    return null;
-	}
-
-	GeometryAtom geomAtoms[] =
-	    locale.universe.geometryStructure.pickAll(locale, shape);
-
-	if ((geomAtoms == null) || (geomAtoms.length == 0)) { 
-	    return null;
-	}
-
-	sortGeomAtoms(geomAtoms, shape);
-
-	synchronized (locale.universe.sceneGraphLock) {
-	    return getSceneGraphPath(null, null, geomAtoms, locale); 
-	}
-    }
-
-
-    static SceneGraphPath[] pickAllSorted(BranchGroup node, 
-					  PickShape shape) {
-	if (node == null) {
-	    return null;
-	}
-
-	BranchGroupRetained nodeR = (BranchGroupRetained) node.retained;
-
-	if (nodeR.inSharedGroup) {
-	    throw new RestrictedAccessException(J3dI18N.getString("Picking0"));
-	}
-
-	Locale locale = nodeR.locale;
-
-	GeometryAtom geomAtoms[] =
-	    locale.universe.geometryStructure.pickAll(locale, shape);
-
-	if ((geomAtoms == null) || (geomAtoms.length == 0)) { 
-	    return null;
-	}
-
-	// we have to sort first before eliminate duplicate Text3D
-	// since we want the closest geometry atoms of Text3D
-	sortGeomAtoms(geomAtoms, shape);
-
-	synchronized (nodeR.universe.sceneGraphLock) {
-	    return getSceneGraphPath(initSceneGraphPath(nodeR),
-				     nodeR, geomAtoms, locale);
-	}
-    }
-
-  
-    static SceneGraphPath pickClosest(Locale locale, 
-				      PickShape shape) {	
-
-	if(locale == null) {
-	    return null;
-	}
-
-	GeometryAtom geomAtoms[] =
-	    locale.universe.geometryStructure.pickAll(locale, shape);
-
-
-	if ((geomAtoms == null) || (geomAtoms.length == 0)) { 
-	    return null;
-	}
-
-
-	GeometryAtom geomAtom = selectClosest(geomAtoms, shape);
-
-
-	synchronized (locale.universe.sceneGraphLock) {
-	    return getSceneGraphPath(null, null, geomAtom, locale); 
-	}
-    }
-
-  
-    static SceneGraphPath pickClosest(BranchGroup node, 
-				      PickShape shape) {
-	if (node == null) {
-	    return null;
-	}
-
-	BranchGroupRetained nodeR = (BranchGroupRetained) node.retained;
-
-	if (nodeR.inSharedGroup) {
-	    throw new RestrictedAccessException(J3dI18N.getString("Picking0"));
-	}
-
-	Locale locale = nodeR.locale;
-
-	GeometryAtom geomAtoms[] =
-	    locale.universe.geometryStructure.pickAll(locale, shape);
-
-
-
-	if ((geomAtoms == null) || (geomAtoms.length == 0)) { 	
-	    return null;
-	}
-
-
-	// We must sort all since the closest one in geomAtoms may not
-	// under the BranchGroup node
-	sortGeomAtoms(geomAtoms, shape);
-
-	synchronized (nodeR.universe.sceneGraphLock) {
-	    return getFirstSceneGraphPath(initSceneGraphPath(nodeR),
-					  nodeR, geomAtoms, locale);
-
-	}
-    }
-  
-
-    static SceneGraphPath pickAny(Locale locale, PickShape shape) {
-
-	if(locale == null) {
-	    return null;
-	}
-
-	GeometryAtom geomAtom =
-	    locale.universe.geometryStructure.pickAny(locale, shape);
-
-	if (geomAtom == null) {
-	    return null;
-	}
-
-	
-	synchronized (locale.universe.sceneGraphLock) {
-	    return getSceneGraphPath(null, null, geomAtom, locale); 
-	}
-    }
-  
-    static SceneGraphPath pickAny(BranchGroup node, PickShape shape) {	
-
-	if (node == null) {
-	    return null;
-	}
-
-	BranchGroupRetained nodeR = (BranchGroupRetained) node.retained;
-	
-	if (nodeR.inSharedGroup) {
-	    throw new RestrictedAccessException(J3dI18N.getString("Picking0"));
-	}
-	
-	Locale locale = nodeR.locale;
-
-	// since PickAny return from geometry may not lie under
-	// BranchGroup node, we have to use pickAll 
-
-	GeometryAtom geomAtoms[] =
-	    locale.universe.geometryStructure.pickAll(locale, shape);
-
-	if ((geomAtoms == null) || (geomAtoms.length == 0)) { 	
-	    return null;
-	}
-
-	synchronized (nodeR.universe.sceneGraphLock) {
-	    return getFirstSceneGraphPath(initSceneGraphPath(nodeR),
-				     nodeR, geomAtoms, locale);
-	}
-    }
-
-
-    /**
-     * Search the path from nodeR up to Locale.
-     * Return the search path as ArrayList if found.
-     * Note that the locale will not insert into path.
-     */
-    static private ArrayList initSceneGraphPath(NodeRetained nodeR) { 
-	ArrayList path = new ArrayList(5);
-
-	do {
-	    if (nodeR.source.getCapability(Node.ENABLE_PICK_REPORTING)){
-		path.add(nodeR);  
-	    }
-	    nodeR = nodeR.parent;
-	} while (nodeR != null);  // reach Locale
-
-	return path;
-    }
-
-    /**
-     * return all SceneGraphPath[] of the geomAtoms.
-     * If initpath is null, the path is search from 
-     * geomAtom Shape3D/Morph Node up to Locale
-     * (assume the same locale).
-     * Otherwise, the path is search up to node or 
-     * null is return if it is not hit.
-     */
-    static private SceneGraphPath[] getSceneGraphPath(ArrayList initpath, 
-						      BranchGroupRetained node, 
-						      GeometryAtom geomAtoms[],
-						      Locale locale) {
-
-	ArrayList paths = new ArrayList(5);
-	GeometryAtom geomAtom;
-	NodeRetained target;
-	ArrayList texts = null;
-
-	if (geomAtoms == null) {
-	    return null;
-	}
-
-
-	for (int i=0; i < geomAtoms.length; i++) {
-	    geomAtom = (GeometryAtom) geomAtoms[i];
-	    Shape3DRetained shape = geomAtom.source;
-		
-	    // isPickable and currentSwitchOn has been check in BHTree
-
-	    if (!inside(shape.branchGroupPath, node)) {
-		continue;
-	    }
-		
-	    target = shape.sourceNode;	
-
-	    if (target == null) {
-		// The node is just detach from branch so sourceNode = null		
-		continue;
-	    }
-	    
-	    // Special case, for Text3DRetained, it is possible
-	    // for different geomAtoms pointing to the same
-	    // source Text3DRetained. So we need to combine
-	    // those cases and report only once.
-	    if (target instanceof Shape3DRetained) {
-		Shape3DRetained s3dR = (Shape3DRetained) target;  
-		GeometryRetained geomR = null;
-		for(int cnt=0; cnt<s3dR.geometryList.size(); cnt++) {
-		    geomR = (GeometryRetained) s3dR.geometryList.get(cnt);
-		    if(geomR != null)
-			break;
-		}
-
-		if (geomR == null)
-		    continue;
-	    
-		if (geomR instanceof Text3DRetained) {
-		    // assume this case is not frequent, we allocate
-		    // ArrayList only when necessary and we use ArrayList
-		    // instead of HashMap since the case of when large
-		    // number of distingish Text3DRetained node hit is
-		    // rare.
-		    if (texts == null) {
-			texts = new ArrayList(3);
-		    } else {
-			int size = texts.size();
-			boolean found = false;
-			for (int j=0; j < size; j++) {
-			    if (texts.get(j) == target) {
-				found = true;
-				break;
-			    }
-			}
-			if (found) {
-			    continue;  // try next geomAtom
-			}
-		    }
-		    texts.add(target);
-		}
-	    }
-	    
-	    ArrayList path = retrievePath(target, node,
-					  geomAtom.source.key);
-
-	    if (path == null) {
-		continue;
-	    }
-
-	    // If target is instance of compile retained, then loop thru
-	    // the entire source list and add it to the scene graph path
-	    if (target instanceof Shape3DCompileRetained) {
-		Shape3DCompileRetained s3dCR = (Shape3DCompileRetained)target;
-		Node[] mpath = mergePath(path, initpath);
-		for (int n = 0; n < s3dCR.srcList.length; n++) {
-		    SceneGraphPath sgpath = new SceneGraphPath(locale,
-							       mpath,
-							       (Node) s3dCR.srcList[n]);
-		    sgpath.setTransform(shape.getCurrentLocalToVworld(0));
-		    paths.add(sgpath);
-		}
-		    
-	    }
-	    else {
-		SceneGraphPath sgpath = new SceneGraphPath(locale,
-							   mergePath(path, initpath),
-							   (Node) target.source);
-		sgpath.setTransform(shape.getCurrentLocalToVworld(0));
-		paths.add(sgpath);
-	    }
-
-
-	}
-	SceneGraphPath pathArray[] = new SceneGraphPath[paths.size()];
-	return (SceneGraphPath []) paths.toArray(pathArray);
-    }
-
-    /**
-     * return the SceneGraphPath of the geomAtom. 
-     * If initpath is null, the path is search from 
-     * geomAtom Shape3D/Morph Node up to Locale
-     * (assume the same locale).
-     * Otherwise, the path is search up to node or 
-     * null is return if it is not hit.
-     */
-    static private SceneGraphPath getSceneGraphPath(ArrayList initpath, 
-						    BranchGroupRetained node, 
-						    GeometryAtom geomAtom,
-						    Locale locale) {
-	if (geomAtom == null) {
-	    return null;
-	}
-	
-	Shape3DRetained shape = geomAtom.source;
-	NodeRetained target = shape.sourceNode;
-	
-	if (target == null) {
-	    // The node is just detach from branch so sourceNode = null		
-	    return null;
-	}
-
-	if (!inside(shape.branchGroupPath, node)) {
-	    return null;
-	}
-
-	ArrayList path = retrievePath(target, node, shape.key);
-
-	if (path == null) {
-	    return null;
-	}
-
-	SceneGraphPath sgpath = new SceneGraphPath(locale, 
-						   mergePath(path, initpath),
-						   (Node)
-						   target.source);
-	sgpath.setTransform(shape.getCurrentLocalToVworld(0));
-	return sgpath;
-    }
-
-    /**
-     * Return true if bg is inside cachedBG or bg is null
-     */
-    static private boolean inside(BranchGroupRetained bgArr[],
-				  BranchGroupRetained bg) {
-
-	if ((bg == null) || (bgArr == null)) {
-	    return true;
-	}
-
-	for (int i=0; i < bgArr.length; i++) {
-	    if (bgArr[i] == bg) {
-		return true;
-	    }
-	}
-	return false;
-    }
-
-
-    /**
-     * return the first SceneGraphPath of the geomAtom. 
-     * If initpath is null, the path is search from 
-     * geomAtom Shape3D/Morph Node up to Locale
-     * (assume the same locale).
-     * Otherwise, the path is search up to node or 
-     * null is return if it is not hit.
-     */
-    static private SceneGraphPath getFirstSceneGraphPath(ArrayList initpath, 
-							 BranchGroupRetained node, 
-							 GeometryAtom geomAtoms[],
-							 Locale locale) {
-	if (geomAtoms == null) {
-	    return null;
-	}
-
-	for (int i=0; i < geomAtoms.length; i++) {
-	    Shape3DRetained shape = geomAtoms[i].source;
-	    NodeRetained target = shape.sourceNode;
-
-	    if (target == null) {
-		// The node is just detach from branch so sourceNode = null		
-		continue;
-	    }
-	    if (!inside(shape.branchGroupPath, node)) {
-		continue;
-	    }
-	    ArrayList path = retrievePath(target, node, geomAtoms[i].source.key);
-
-	    if (path == null) {
-		continue;
-	    }
-	    SceneGraphPath sgpath = new SceneGraphPath(locale, 
-						       mergePath(path, initpath),
-						       (Node) target.source);
-	    sgpath.setTransform(shape.getCurrentLocalToVworld(0));
-	    return sgpath;
-	}
-	return null;
-    }
-
-
-    /**
-     * search the full path from the botton of the scene graph -
-     * startNode, up to the Locale if endNode is null.
-     * If endNode is not null, the path is found up to, but not
-     * including, endNode or return null if endNode not hit 
-     * during the search.
-     */
-    static private ArrayList retrievePath(NodeRetained startNode, 
-					  NodeRetained endNode,
-					  HashKey key) {
-
-	ArrayList path = new ArrayList(5);
-	NodeRetained nodeR = startNode;
-	
-	if (nodeR.inSharedGroup) {
-	    // getlastNodeId() will destroy this key
-	    key = new HashKey(key);
-	}
-
-	do {
-	    if (nodeR == endNode) { // we found it !
-		return path;
-	    }
-
-	    if (nodeR.source.getCapability(Node.ENABLE_PICK_REPORTING)) {
-		path.add(nodeR);  
-	    }
-
-	    if (nodeR instanceof SharedGroupRetained) {
-		// retrieve the last node ID
-		String nodeId = key.getLastNodeId();
-		Vector parents = ((SharedGroupRetained) nodeR).parents;
-		int sz = parents.size();
-		NodeRetained prevNodeR = nodeR;
-		for(int i=0; i< sz; i++) {
-		    NodeRetained linkR = (NodeRetained) parents.elementAt(i);
-		    if (linkR.nodeId.equals(nodeId)) {
-			nodeR = linkR;
-			// Need to add Link to the path report
-			path.add(nodeR);
-			// since !(endNode instanceof Link), we 
-			// can skip the check (nodeR == endNode) and
-			// proceed to parent of link below
-			break;
-		    }
-		}
-		if (nodeR == prevNodeR) { 
-		    // branch is already detach
-		    return null;
-		}
-	    }
-	    nodeR = nodeR.parent;
-	} while (nodeR != null); // reach Locale
-	
-	if (endNode == null) {
-	    // user call pickxxx(Locale locale, PickShape shape)
-	    return path;
-	}
-
-	// user call pickxxx(BranchGroup endNode, PickShape shape)
-	// if locale is reached and endNode not hit, this is not
-	// the path user want to select 
-	return null;
-    }
-    
-    /**
-     * copy p1, (follow by) p2 into a new array, p2 can be null
-     * The path is then reverse before return.
-     */
-    static private Node[] mergePath(ArrayList p1, ArrayList p2) {
-	int s = p1.size();
-	int len;
-	int i;
-	int l;
-	if (p2 == null) {
-	    len = s;
-	} else {
-	    len = s + p2.size();
-	}
-
-	Node nodes[] = new Node[len];
-	l = len-1;
-	for (i=0; i < s; i++) {
-	    nodes[l-i] = (Node) ((NodeRetained) p1.get(i)).source;
-	}
-	for (int j=0; i< len; i++, j++) {
-	    nodes[l-i] = (Node) ((NodeRetained) p2.get(j)).source;
-	}
-	return nodes;
-    }
-
-    /**
-     * Select the closest geomAtoms from shape
-     * geomAtoms.length must be >= 1
-     */
-    static private GeometryAtom selectClosest(GeometryAtom geomAtoms[], 
-					      PickShape shape) {
-	Point4d pickPos = new Point4d();
-	GeometryAtom closestAtom = geomAtoms[0];
-	shape.intersect(closestAtom.source.vwcBounds, pickPos);
-	double distance = pickPos.w;
-
-	for (int i=1; i < geomAtoms.length; i++) {
-	    shape.intersect(geomAtoms[i].source.vwcBounds, pickPos);	    
-	    if (pickPos.w < distance) {
-		distance = pickPos.w;
-		closestAtom = geomAtoms[i];
-	    }
-	}
-	return closestAtom;
-    }
-
-    /**
-     * Sort the GeometryAtoms distance from shape in ascending order
-     * geomAtoms.length must be >= 1
-     */
-    static private void sortGeomAtoms(GeometryAtom geomAtoms[], 
-				      PickShape shape) {
-
-	final double distance[] = new double[geomAtoms.length];
-	Point4d pickPos = new Point4d();
-
-	for (int i=0; i < geomAtoms.length; i++) {
-	    shape.intersect(geomAtoms[i].source.vwcBounds, pickPos);	    
-	    distance[i] = pickPos.w;
-	}
-
-	class Sort {
-	    
-	    GeometryAtom atoms[];
-
-	    Sort(GeometryAtom[] atoms) {
-		this.atoms = atoms;
-	    }
-
-	    void sorting() {
-		if (atoms.length < 7) {
-		    insertSort();
-	    	} else {
-		    quicksort(0, atoms.length-1);
-    		}
-	    }
-
-	    // Insertion sort on smallest arrays
-	    final void insertSort() {
-		for (int i=0; i<atoms.length; i++) {
-		    for (int j=i; j>0 && 
-			     (distance[j-1] > distance[j]); j--) {
-			double t = distance[j];
-			distance[j] = distance[j-1];
-			distance[j-1] = t;
-			GeometryAtom p = atoms[j];
-			atoms[j] = atoms[j-1];
-			atoms[j-1] = p;
-		    }
-		}
-	    }
-
-            final void quicksort( int l, int r ) {
-		int i = l;
-		int j = r;
-		double k = distance[(l+r) / 2];
-
-		do {
-		    while (distance[i]<k) i++;
-		    while (k<distance[j]) j--;
-		    if (i<=j) {
-			double tmp = distance[i];
-			distance[i] =distance[j];
-			distance[j] = tmp;
-			
-			GeometryAtom p=atoms[i];
-			atoms[i]=atoms[j];
-			atoms[j]=p;
-			i++;
-			j--;
-		    }
-		} while (i<=j);
-		
-		if (l<j) quicksort(l,j);
-		if (l<r) quicksort(i,r);
-	    }
-	}
-
-	(new Sort(geomAtoms)).sorting();
-    }
-  
-}
diff --git a/src/classes/share/javax/media/j3d/PointArray.java b/src/classes/share/javax/media/j3d/PointArray.java
index 412f4d5..421960d 100644
--- a/src/classes/share/javax/media/j3d/PointArray.java
+++ b/src/classes/share/javax/media/j3d/PointArray.java
@@ -23,22 +23,21 @@ public class PointArray extends GeometryArray {
     PointArray() {}
 
     /**
-     * Constructs an empty PointArray object with the specified
-     * number of vertices, and vertex format.
-     * @param vertexCount the number of vertex elements in this array
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+     * Constructs an empty PointArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
      * @exception IllegalArgumentException if vertexCount is less than 1
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int)}
+     * for more exceptions that can be thrown
      */
     public PointArray(int vertexCount, int vertexFormat) {
 	super(vertexCount,vertexFormat);
@@ -48,59 +47,29 @@ public class PointArray extends GeometryArray {
     }
 
     /**
-     * Constructs an empty PointArray object with the specified
-     * number of vertices, and vertex format, number of texture coordinate
-     * sets, and texture coordinate mapping array.
-     *
-     * @param vertexCount the number of vertex elements in this array<p>
-     *
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
-     *
-     * @param texCoordSetCount the number of texture coordinate sets
-     * in this GeometryArray object.  If <code>vertexFormat</code>
-     * does not include one of <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetCount</code> parameter is not used.<p>
-     *
-     * @param texCoordSetMap an array that maps texture coordinate
-     * sets to texture units.  The array is indexed by texture unit
-     * number for each texture unit in the associated Appearance
-     * object.  The values in the array specify the texture coordinate
-     * set within this GeometryArray object that maps to the
-     * corresponding texture
-     * unit.  All elements within the array must be less than
-     * <code>texCoordSetCount</code>.  A negative value specifies that
-     * no texture coordinate set maps to the texture unit
-     * corresponding to the index.  If there are more texture units in
-     * any associated Appearance object than elements in the mapping
-     * array, the extra elements are assumed to be -1.  The same
-     * texture coordinate set may be used for more than one texture
-     * unit.  Each texture unit in every associated Appearance must
-     * have a valid source of texture coordinates: either a
-     * non-negative texture coordinate set must be specified in the
-     * mapping array or texture coordinate generation must be enabled.
-     * Texture coordinate generation will take precedence for those
-     * texture units for which a texture coordinate set is specified
-     * and texture coordinate generation is enabled.  If
-     * <code>vertexFormat</code> does not include one of
-     * <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetMap</code> array is not used.
+     * Constructs an empty PointArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
      *
      * @exception IllegalArgumentException if vertexCount is less than 1
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for more exceptions that can be thrown
      *
      * @since Java 3D 1.2
      */
@@ -116,6 +85,52 @@ public class PointArray extends GeometryArray {
 	    throw new IllegalArgumentException(J3dI18N.getString("PointArray0"));
     }
 
+    /**
+     * Constructs an empty PointArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrSizes
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @exception IllegalArgumentException if vertexCount is less than 1
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for more exceptions that can be thrown
+     *
+     * @since Java 3D 1.4
+     */
+    public PointArray(int vertexCount,
+		      int vertexFormat,
+		      int texCoordSetCount,
+		      int[] texCoordSetMap,
+		      int vertexAttrCount,
+		      int[] vertexAttrSizes) {
+
+	super(vertexCount, vertexFormat,
+	      texCoordSetCount, texCoordSetMap,
+	      vertexAttrCount, vertexAttrSizes);
+
+        if (vertexCount < 1 )
+	    throw new IllegalArgumentException(J3dI18N.getString("PointArray0"));
+    }
+
     /**
      * Creates the retained mode PointArrayRetained object that this
      * PointArray object will point to.
@@ -130,21 +145,26 @@ public class PointArray extends GeometryArray {
      * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
      */
     public NodeComponent cloneNodeComponent() {
-	PointArrayRetained rt = (PointArrayRetained) retained;
-	int texSetCount = rt.getTexCoordSetCount();
-        PointArray p;
-	if (texSetCount == 0) {
-	    p = new PointArray(rt.getVertexCount(), 
-			       rt.getVertexFormat());
-	} else {
-	    int texMap[] = new int[rt.getTexCoordSetMapLength()];
-	    rt.getTexCoordSetMap(texMap);
-	    p = new PointArray(rt.getVertexCount(), 
-			       rt.getVertexFormat(),
-			       texSetCount,
-			       texMap);
-	}
-	p.duplicateNodeComponent(this);
+        PointArrayRetained rt = (PointArrayRetained) retained;
+        int texSetCount = rt.getTexCoordSetCount();
+        int[] texMap = null;
+        int vertexAttrCount = rt.getVertexAttrCount();
+        int[] vertexAttrSizes = null;
+        if (texSetCount > 0) {
+            texMap = new int[rt.getTexCoordSetMapLength()];
+            rt.getTexCoordSetMap(texMap);
+        }
+        if (vertexAttrCount > 0) {
+            vertexAttrSizes = new int[vertexAttrCount];
+            rt.getVertexAttrSizes(vertexAttrSizes);
+        }
+        PointArray p = new PointArray(rt.getVertexCount(),
+                rt.getVertexFormat(),
+                texSetCount,
+                texMap,
+                vertexAttrCount,
+                vertexAttrSizes);
+        p.duplicateNodeComponent(this);
         return p;
      }
 }
diff --git a/src/classes/share/javax/media/j3d/PointArrayRetained.java b/src/classes/share/javax/media/j3d/PointArrayRetained.java
index b06e410..319a643 100644
--- a/src/classes/share/javax/media/j3d/PointArrayRetained.java
+++ b/src/classes/share/javax/media/j3d/PointArrayRetained.java
@@ -25,13 +25,15 @@ class PointArrayRetained extends GeometryArrayRetained {
 	this.geoType = GEO_TYPE_POINT_SET;
     }
 
-    boolean intersect(PickShape pickShape, double dist[],  Point3d iPnt) {
+    boolean intersect(PickShape pickShape, PickInfo.IntersectionInfo iInfo,  int flags, Point3d iPnt) {
 	double sdist[] = new double[1];
 	double minDist = Double.MAX_VALUE;
 	double x = 0, y = 0, z = 0;
-	Point3d pnt = new Point3d();
+        int count = 0;
+        int minICount = 0;         
 	int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
 		 initialVertexIndex : initialCoordIndex);
+	Point3d pnt = new Point3d();
     
 	switch (pickShape.getPickType()) {
 	case PickShape.PICKRAY:
@@ -39,13 +41,15 @@ class PointArrayRetained extends GeometryArrayRetained {
 
 	    while (i < validVertexCount) {
 		getVertexData(i++, pnt);
-		if (intersectPntAndRay(pnt, pickRay.origin,
+                count++;
+                if (intersectPntAndRay(pnt, pickRay.origin,
 				       pickRay.direction, sdist)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = pnt.x;
 			y = pnt.y;
 			z = pnt.z;
@@ -61,14 +65,16 @@ class PointArrayRetained extends GeometryArrayRetained {
 			     pickSegment.end.z - pickSegment.start.z);
 	    while (i < validVertexCount) {
 		getVertexData(i++, pnt);
+                count++;
 		if (intersectPntAndRay(pnt, pickSegment.start, 
 					dir, sdist) &&
 		    (sdist[0] <= 1.0)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = pnt.x;
 			y = pnt.y;
 			z = pnt.z;
@@ -84,13 +90,15 @@ class PointArrayRetained extends GeometryArrayRetained {
 
 	    while (i < validVertexCount) {
 		getVertexData(i++, pnt);
+                count++;
 		if (bounds.intersect(pnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    sdist[0] = pickShape.distance(pnt);
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = pnt.x;
 			y = pnt.y;
 			z = pnt.z;
@@ -104,13 +112,14 @@ class PointArrayRetained extends GeometryArrayRetained {
 
 	    while (i < validVertexCount) {
 		getVertexData(i++, pnt);
-
+                count++;
 		if (intersectCylinder(pnt, pickCylinder, sdist)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = pnt.x;
 			y = pnt.y;
 			z = pnt.z;
@@ -123,13 +132,14 @@ class PointArrayRetained extends GeometryArrayRetained {
 
 	    while (i < validVertexCount) {
 		getVertexData(i++, pnt);
-
+                count++;
 		if (intersectCone(pnt, pickCone, sdist)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = pnt.x;
 			y = pnt.y;
 			z = pnt.z;
@@ -145,14 +155,19 @@ class PointArrayRetained extends GeometryArrayRetained {
 	} 
 
 	if (minDist < Double.MAX_VALUE) {
-	    dist[0] = minDist;
+            assert(minICount >=1);
+            int[] vertexIndices = iInfo.getVertexIndices();
+            if (vertexIndices == null) {
+                vertexIndices = new int[1];
+                iInfo.setVertexIndices(vertexIndices);
+            }
+            vertexIndices[0] = minICount - 1;
 	    iPnt.x = x;
 	    iPnt.y = y;
 	    iPnt.z = z;
 	    return true;
 	}
 	return false;
-   
     }
 
     boolean intersect(Point3d[] pnts) {
diff --git a/src/classes/share/javax/media/j3d/PointAttributes.java b/src/classes/share/javax/media/j3d/PointAttributes.java
index 29b9e92..66c4e39 100644
--- a/src/classes/share/javax/media/j3d/PointAttributes.java
+++ b/src/classes/share/javax/media/j3d/PointAttributes.java
@@ -65,6 +65,12 @@ public class PointAttributes extends NodeComponent {
     public static final int
     ALLOW_ANTIALIASING_WRITE = CapabilityBits.POINT_ATTRIBUTES_ALLOW_ANTIALIASING_WRITE;
 
+       // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_SIZE_READ,
+        ALLOW_ANTIALIASING_READ
+    };
+    
     /**
      * Constructs a PointAttributes object with default parameters.
      * The default values are as follows:
@@ -74,6 +80,8 @@ public class PointAttributes extends NodeComponent {
      * </ul>
      */
      public PointAttributes(){
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
       }
 
     /**
@@ -82,8 +90,10 @@ public class PointAttributes extends NodeComponent {
      * @param pointAntialiasing flag to set point antialising ON or OFF
      */
      public PointAttributes(float pointSize, boolean pointAntialiasing){
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
 
-       ((PointAttributesRetained)this.retained).initPointSize(pointSize);
+        ((PointAttributesRetained)this.retained).initPointSize(pointSize);
        ((PointAttributesRetained)this.retained).initPointAntialiasingEnable(pointAntialiasing);
      }
 
diff --git a/src/classes/share/javax/media/j3d/PointLight.java b/src/classes/share/javax/media/j3d/PointLight.java
index 3e87f84..147c186 100644
--- a/src/classes/share/javax/media/j3d/PointLight.java
+++ b/src/classes/share/javax/media/j3d/PointLight.java
@@ -80,6 +80,12 @@ public class PointLight extends Light {
   public static final int
     ALLOW_ATTENUATION_WRITE = CapabilityBits.POINT_LIGHT_ALLOW_ATTENUATION_WRITE;
 
+    // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+	ALLOW_POSITION_READ,
+	ALLOW_ATTENUATION_READ
+    };
+
     /**
      * Constructs a PointLight node with default parameters.
      * The default values are as follows:
@@ -89,6 +95,8 @@ public class PointLight extends Light {
      * </ul>
      */
     public PointLight() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -101,6 +109,10 @@ public class PointLight extends Light {
 		      Point3f position,
 		      Point3f attenuation) { 
 	super(color);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
 	((PointLightRetained)this.retained).initPosition(position);
 	((PointLightRetained)this.retained).initAttenuation(attenuation);
     }
@@ -117,6 +129,10 @@ public class PointLight extends Light {
 		      Point3f position,
 		      Point3f attenuation) { 
 	super(lightOn, color);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
 	((PointLightRetained)this.retained).initPosition(position);
 	((PointLightRetained)this.retained).initAttenuation(attenuation);
     }
diff --git a/src/classes/share/javax/media/j3d/PointSound.java b/src/classes/share/javax/media/j3d/PointSound.java
index 1f005eb..4af92dd 100644
--- a/src/classes/share/javax/media/j3d/PointSound.java
+++ b/src/classes/share/javax/media/j3d/PointSound.java
@@ -99,6 +99,13 @@ public class PointSound extends Sound {
   public static final int
     ALLOW_DISTANCE_GAIN_WRITE = CapabilityBits.POINT_SOUND_ALLOW_DISTANCE_GAIN_WRITE;
 
+
+    // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_POSITION_READ,
+        ALLOW_DISTANCE_GAIN_READ
+    };
+
     /**
      * Constructs and initializes a new PointSound node using default
      * parameters.  The following default values are used:
@@ -111,6 +118,9 @@ public class PointSound extends Sound {
     public PointSound() {
 	// Uses default values defined for Sound and PointSound nodes
         super();
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
     }   
 
     /**
@@ -126,6 +136,10 @@ public class PointSound extends Sound {
                       float initialGain,
                       Point3f position) {
         super(soundData, initialGain);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((PointSoundRetained)this.retained).setPosition(position);
     }   
 
@@ -144,6 +158,10 @@ public class PointSound extends Sound {
                       float initialGain,
                       float posX, float posY, float posZ ) {
         super(soundData, initialGain);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((PointSoundRetained)this.retained).setPosition(posX,posY,posZ);
     }   
 
@@ -180,6 +198,10 @@ public class PointSound extends Sound {
 
         super(soundData, initialGain, loopCount, release, continuous, 
                    enable, region, priority );
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((PointSoundRetained)this.retained).setPosition(position);
         ((PointSoundRetained)this.retained).setDistanceGain(distanceGain);
     }   
@@ -215,6 +237,10 @@ public class PointSound extends Sound {
 
         super(soundData, initialGain, loopCount, release,
               continuous, enable, region, priority );
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((PointSoundRetained)this.retained).setPosition(posX,posY,posZ);
         ((PointSoundRetained)this.retained).setDistanceGain(distanceGain);
     }   
@@ -249,6 +275,10 @@ public class PointSound extends Sound {
 
         super(soundData, initialGain, loopCount, release, continuous, 
                 enable, region, priority );
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((PointSoundRetained)this.retained).setPosition(position);
         ((PointSoundRetained)this.retained).setDistanceGain(
                         attenuationDistance, attenuationGain);
@@ -286,6 +316,10 @@ public class PointSound extends Sound {
 
         super(soundData, initialGain, loopCount, release,
               continuous, enable, region, priority );
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((PointSoundRetained)this.retained).setPosition(posX,posY,posZ);
         ((PointSoundRetained)this.retained).setDistanceGain(
                         attenuationDistance, attenuationGain);
diff --git a/src/classes/share/javax/media/j3d/PolygonAttributes.java b/src/classes/share/javax/media/j3d/PolygonAttributes.java
index 25b1269..ec676e2 100644
--- a/src/classes/share/javax/media/j3d/PolygonAttributes.java
+++ b/src/classes/share/javax/media/j3d/PolygonAttributes.java
@@ -146,6 +146,14 @@ public class PolygonAttributes extends NodeComponent {
      */
     public static final int CULL_FRONT = 2;
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_CULL_FACE_READ,
+        ALLOW_MODE_READ,
+        ALLOW_NORMAL_FLIP_READ,
+        ALLOW_OFFSET_READ
+    };
+    
     /**
      * Constructs a PolygonAttributes object with default parameters.
      * The default values are as follows:
@@ -159,6 +167,8 @@ public class PolygonAttributes extends NodeComponent {
      */
     public PolygonAttributes() {
 	// Just use defaults for all attributes
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -216,6 +226,9 @@ public class PolygonAttributes extends NodeComponent {
        if (cullFace < CULL_NONE || cullFace > CULL_FRONT)
          throw new IllegalArgumentException(J3dI18N.getString("PolygonAttributes12"));  
 
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+       
        ((PolygonAttributesRetained)this.retained).initPolygonMode(polygonMode);
        ((PolygonAttributesRetained)this.retained).initCullFace(cullFace);
        ((PolygonAttributesRetained)this.retained).initPolygonOffset(polygonOffset);
diff --git a/src/classes/share/javax/media/j3d/QuadArray.java b/src/classes/share/javax/media/j3d/QuadArray.java
index 79818d3..ee7ab2f 100644
--- a/src/classes/share/javax/media/j3d/QuadArray.java
+++ b/src/classes/share/javax/media/j3d/QuadArray.java
@@ -25,23 +25,22 @@ public class QuadArray extends GeometryArray {
     QuadArray() {}
 
     /**
-     * Constructs an empty QuadArray object with the specified
-     * number of vertices, and vertex format.
-     * @param vertexCount the number of vertex elements in this array
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4,
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+     * Constructs an empty QuadArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
      * @exception IllegalArgumentException if vertexCount is less than 4
      * or vertexCount is <i>not</i> a multiple of 4
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int)}
+     * for more exceptions that can be thrown
      */
     public QuadArray(int vertexCount, int vertexFormat) {
 	super(vertexCount,vertexFormat);
@@ -51,60 +50,30 @@ public class QuadArray extends GeometryArray {
     }
 
     /**
-     * Constructs an empty QuadArray object with the specified
-     * number of vertices, and vertex format, number of texture coordinate
-     * sets, and texture coordinate mapping array.
-     *
-     * @param vertexCount the number of vertex elements in this array<p>
-     *
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
-     *
-     * @param texCoordSetCount the number of texture coordinate sets
-     * in this GeometryArray object.  If <code>vertexFormat</code>
-     * does not include one of <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3 or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetCount</code> parameter is not used.<p>
-     *
-     * @param texCoordSetMap an array that maps texture coordinate
-     * sets to texture units.  The array is indexed by texture unit
-     * number for each texture unit in the associated Appearance
-     * object.  The values in the array specify the texture coordinate
-     * set within this GeometryArray object that maps to the
-     * corresponding texture
-     * unit.  All elements within the array must be less than
-     * <code>texCoordSetCount</code>.  A negative value specifies that
-     * no texture coordinate set maps to the texture unit
-     * corresponding to the index.  If there are more texture units in
-     * any associated Appearance object than elements in the mapping
-     * array, the extra elements are assumed to be -1.  The same
-     * texture coordinate set may be used for more than one texture
-     * unit.  Each texture unit in every associated Appearance must
-     * have a valid source of texture coordinates: either a
-     * non-negative texture coordinate set must be specified in the
-     * mapping array or texture coordinate generation must be enabled.
-     * Texture coordinate generation will take precedence for those
-     * texture units for which a texture coordinate set is specified
-     * and texture coordinate generation is enabled.  If
-     * <code>vertexFormat</code> does not include one of
-     * <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetMap</code> array is not used.
+     * Constructs an empty QuadArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
      *
      * @exception IllegalArgumentException if vertexCount is less than 4
      * or vertexCount is <i>not</i> a multiple of 4
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for more exceptions that can be thrown
      *
      * @since Java 3D 1.2
      */
@@ -120,6 +89,53 @@ public class QuadArray extends GeometryArray {
 	    throw new IllegalArgumentException(J3dI18N.getString("QuadArray0"));
     }
 
+    /**
+     * Constructs an empty QuadArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrSizes
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @exception IllegalArgumentException if vertexCount is less than 4
+     * or vertexCount is <i>not</i> a multiple of 4
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for more exceptions that can be thrown
+     *
+     * @since Java 3D 1.4
+     */
+    public QuadArray(int vertexCount,
+		     int vertexFormat,
+		     int texCoordSetCount,
+		     int[] texCoordSetMap,
+		     int vertexAttrCount,
+		     int[] vertexAttrSizes) {
+
+	super(vertexCount, vertexFormat,
+	      texCoordSetCount, texCoordSetMap,
+	      vertexAttrCount, vertexAttrSizes);
+
+        if (vertexCount < 4 || ((vertexCount%4) != 0))
+	    throw new IllegalArgumentException(J3dI18N.getString("QuadArray0"));
+    }
+
     /**
      * Creates the retained mode QuadArrayRetained object that this
      * QuadArray object will point to.
@@ -134,21 +150,26 @@ public class QuadArray extends GeometryArray {
      * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
      */
     public NodeComponent cloneNodeComponent() {
-	QuadArrayRetained rt = (QuadArrayRetained) retained;
-	int texSetCount = rt.getTexCoordSetCount();
-        QuadArray q;
-	if (texSetCount == 0) {
-	    q = new QuadArray(rt.getVertexCount(), 
-			      rt.getVertexFormat());
-	} else {
-	    int texMap[] = new int[rt.getTexCoordSetMapLength()];
-	    rt.getTexCoordSetMap(texMap);	
-	    q = new QuadArray(rt.getVertexCount(), 
-			      rt.getVertexFormat(),
-			      texSetCount,
-			      texMap);
-	}
-	q.duplicateNodeComponent(this);
+        QuadArrayRetained rt = (QuadArrayRetained) retained;
+        int texSetCount = rt.getTexCoordSetCount();
+        int[] texMap = null;
+        int vertexAttrCount = rt.getVertexAttrCount();
+        int[] vertexAttrSizes = null;
+        if (texSetCount > 0) {
+            texMap = new int[rt.getTexCoordSetMapLength()];
+            rt.getTexCoordSetMap(texMap);
+        }
+        if (vertexAttrCount > 0) {
+            vertexAttrSizes = new int[vertexAttrCount];
+            rt.getVertexAttrSizes(vertexAttrSizes);
+        }
+        QuadArray q = new QuadArray(rt.getVertexCount(),
+                rt.getVertexFormat(),
+                texSetCount,
+                texMap,
+                vertexAttrCount,
+                vertexAttrSizes);
+        q.duplicateNodeComponent(this);
         return q;
      }
 
diff --git a/src/classes/share/javax/media/j3d/QuadArrayRetained.java b/src/classes/share/javax/media/j3d/QuadArrayRetained.java
index b33f605..5e95b76 100644
--- a/src/classes/share/javax/media/j3d/QuadArrayRetained.java
+++ b/src/classes/share/javax/media/j3d/QuadArrayRetained.java
@@ -27,14 +27,15 @@ class QuadArrayRetained extends GeometryArrayRetained {
 	this.geoType = GEO_TYPE_QUAD_SET;
     }
 
-    boolean intersect(PickShape pickShape, double dist[],  Point3d iPnt) {
+    boolean intersect(PickShape pickShape, PickInfo.IntersectionInfo iInfo,  int flags, Point3d iPnt) {
 	Point3d pnts[] = new Point3d[4];
 	double sdist[] = new double[1];
 	double minDist = Double.MAX_VALUE;
 	double x = 0, y = 0, z = 0;
+        int count = 0;
+        int minICount = 0; 
 	int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
 		 initialVertexIndex : initialCoordIndex);
-
 	pnts[0] = new Point3d();
 	pnts[1] = new Point3d();
 	pnts[2] = new Point3d();
@@ -49,12 +50,14 @@ class QuadArrayRetained extends GeometryArrayRetained {
 		getVertexData(i++, pnts[1]);
 		getVertexData(i++, pnts[2]);
 		getVertexData(i++, pnts[3]);
+                count += 4;
 		if (intersectRay(pnts, pickRay, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -70,18 +73,19 @@ class QuadArrayRetained extends GeometryArrayRetained {
 		getVertexData(i++, pnts[1]);
 		getVertexData(i++, pnts[2]);
 		getVertexData(i++, pnts[3]);
+                count += 4;
 		if (intersectSegment(pnts, pickSegment.start,
 				     pickSegment.end, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
 		    }
-
 		}
 	    }
 	    break;
@@ -93,20 +97,20 @@ class QuadArrayRetained extends GeometryArrayRetained {
 		getVertexData(i++, pnts[1]);
 		getVertexData(i++, pnts[2]);
 		getVertexData(i++, pnts[3]);
-
+                count += 4;
 		if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
 		    }
 		}
 	    }
-
 	    break;
 	case PickShape.PICKBOUNDINGSPHERE:
 	    BoundingSphere bsphere = (BoundingSphere) 
@@ -117,13 +121,14 @@ class QuadArrayRetained extends GeometryArrayRetained {
 		getVertexData(i++, pnts[1]);
 		getVertexData(i++, pnts[2]);
 		getVertexData(i++, pnts[3]);
-
+                count += 4;
 		if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -141,13 +146,14 @@ class QuadArrayRetained extends GeometryArrayRetained {
 		getVertexData(i++, pnts[1]);
 		getVertexData(i++, pnts[2]);
 		getVertexData(i++, pnts[3]);
-
+                count += 4;
 		if (intersectBoundingPolytope(pnts, bpolytope, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -163,13 +169,14 @@ class QuadArrayRetained extends GeometryArrayRetained {
 		getVertexData(i++, pnts[1]);
 		getVertexData(i++, pnts[2]);
 		getVertexData(i++, pnts[3]);
-
+                count += 4;
 		if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -185,12 +192,14 @@ class QuadArrayRetained extends GeometryArrayRetained {
 		getVertexData(i++, pnts[1]);
 		getVertexData(i++, pnts[2]);
 		getVertexData(i++, pnts[3]);
+                count += 4;
 		if (intersectCone(pnts, pickCone, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -206,7 +215,16 @@ class QuadArrayRetained extends GeometryArrayRetained {
 	} 
 
 	if (minDist < Double.MAX_VALUE) {
-	    dist[0] = minDist;
+            assert(minICount >= 4);
+            int[] vertexIndices = iInfo.getVertexIndices();
+            if (vertexIndices == null) {
+                vertexIndices = new int[4];
+                iInfo.setVertexIndices(vertexIndices);
+            }
+            vertexIndices[0] = minICount - 4;
+            vertexIndices[1] = minICount - 3;
+            vertexIndices[2] = minICount - 2;
+            vertexIndices[3] = minICount - 1;
 	    iPnt.x = x;
 	    iPnt.y = y;
 	    iPnt.z = z;
@@ -215,7 +233,7 @@ class QuadArrayRetained extends GeometryArrayRetained {
 	return false;
    
     }
-  
+
     // intersect pnts[] with every quad in this object
     boolean intersect(Point3d[] pnts) {
 	Point3d[] points = new Point3d[4];
diff --git a/src/classes/share/javax/media/j3d/Raster.java b/src/classes/share/javax/media/j3d/Raster.java
index d14eaf7..d95fee7 100644
--- a/src/classes/share/javax/media/j3d/Raster.java
+++ b/src/classes/share/javax/media/j3d/Raster.java
@@ -180,6 +180,17 @@ public class Raster extends Geometry {
     ALLOW_CLIP_MODE_WRITE = CapabilityBits.RASTER_ALLOW_CLIP_MODE_WRITE;
 
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+	ALLOW_POSITION_READ,
+	ALLOW_OFFSET_READ,
+	ALLOW_IMAGE_READ,
+	ALLOW_DEPTH_COMPONENT_READ,
+	ALLOW_SIZE_READ,
+	ALLOW_TYPE_READ,
+	ALLOW_CLIP_MODE_READ
+    };
+
     /**
      * Constructs a Raster object with default parameters.
      * The default values are as follows:
@@ -195,6 +206,8 @@ public class Raster extends Geometry {
      * </ul>
      */
     public Raster() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -223,6 +236,9 @@ public class Raster extends Geometry {
                   ImageComponent2D image,
                   DepthComponent depthComponent) {
 
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((RasterRetained)this.retained).setPosition(pos);
         ((RasterRetained)this.retained).setType(type);
         ((RasterRetained)this.retained).setSrcOffset(xSrcOffset, ySrcOffset);
@@ -252,6 +268,9 @@ public class Raster extends Geometry {
                   ImageComponent2D image, 
                   DepthComponent depthComponent) { 
 
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((RasterRetained)this.retained).setPosition(pos);
         ((RasterRetained)this.retained).setType(type);
         ((RasterRetained)this.retained).setSrcOffset(srcOffset.x, srcOffset.y);
@@ -289,6 +308,9 @@ public class Raster extends Geometry {
 		  ImageComponent2D image,
 		  DepthComponent depthComponent) {
 
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((RasterRetained)this.retained).setPosition(pos);
         ((RasterRetained)this.retained).setType(type);
         ((RasterRetained)this.retained).setClipMode(clipMode);
diff --git a/src/classes/share/javax/media/j3d/RasterRetained.java b/src/classes/share/javax/media/j3d/RasterRetained.java
index d580cd7..bd591b9 100644
--- a/src/classes/share/javax/media/j3d/RasterRetained.java
+++ b/src/classes/share/javax/media/j3d/RasterRetained.java
@@ -687,9 +687,10 @@ class RasterRetained extends GeometryRetained {
                                         ImageComponentUpdateInfo value) {
     }
 
-    boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {	
+     boolean intersect(PickShape pickShape, PickInfo.IntersectionInfo iInfo,  int flags, Point3d iPnt) {
 	return false;
     }
+    
     boolean intersect(Bounds targetBound) {
 	return false;
     }
diff --git a/src/classes/share/javax/media/j3d/RenderBin.java b/src/classes/share/javax/media/j3d/RenderBin.java
index e252bef..23ba119 100644
--- a/src/classes/share/javax/media/j3d/RenderBin.java
+++ b/src/classes/share/javax/media/j3d/RenderBin.java
@@ -45,6 +45,12 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
     ArrayList rmUpdateList = new ArrayList();
     ArrayList aBinUpdateList = new ArrayList();
 
+    /**
+     * List of ShaderBin that are soleUser that
+     * needs to have its components updated @updateObject time
+     */
+    ArrayList sBinUpdateList = new ArrayList();
+
     /**
      * List of TextureBin that are soleUser that
      * needs to have its components updated @updateObject time
@@ -95,6 +101,7 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
     ArrayList lightBinFreelist = new ArrayList(5);
     ArrayList envSetFreelist = new ArrayList(5);
     ArrayList attrBinFreelist = new ArrayList(5);
+    ArrayList shaderBinFreelist = new ArrayList(5);
     ArrayList textureBinFreelist = new ArrayList(5);
     ArrayList renderMoleculeFreelist = new ArrayList(5);
     ArrayList transparentInfoFreeList = new ArrayList(5);
@@ -167,6 +174,8 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
      */
     View view = null;
     
+    private Comparator transparencySortComparator = null;
+    
     ArrayList toBeAddedTextureResourceFreeList = new ArrayList(5);
     ArrayList displayListResourceFreeList = new ArrayList(5);
     boolean resourceToFree = false;
@@ -526,10 +535,15 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
 	    for (i = 0; i < size; i++) {
 		AttributeBin abin = (AttributeBin)aBinUpdateList.get(i);
 		abin.updateNodeComponent();
-		
 	    }
 	}
 
+	if ((size = sBinUpdateList.size()) > 0) {
+	    for (i = 0; i < size; i++) {
+		ShaderBin sbin = (ShaderBin)sBinUpdateList.get(i);
+		sbin.updateNodeComponent();
+	    }
+	}
 
 	// Update the sole user TextureBins.
 	if (tbUpdateList.size() > 0) {
@@ -544,12 +558,12 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
 	    // texture in the first texture unit state
 	    for (i = 0; i < size; i++) {
 		tb = (TextureBin) tbUpdateList.get(i);
-		// Bug Id : 4701430 - Have to be sure tb.attributeBin is
+		// Bug Id : 4701430 - Have to be sure tb.shaderBin is
 		// not equal to null. This is a temporary fix for j3d1.3.
 		if (((tb.tbFlag & TextureBin.RESORT) != 0) && 
-		    (tb.attributeBin != null)) {
+		    (tb.shaderBin != null)) {
 
-		    tb.attributeBin.reInsertTextureBin(tb);
+		    tb.shaderBin.reInsertTextureBin(tb);
 		    tb.tbFlag &= ~TextureBin.RESORT;
 		}
 	    }
@@ -823,7 +837,7 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
 			continue;
 		    zVal = renderAtom.geometryAtom.centroid[k].distanceSquared(eyeInVworld);
 		    renderAtom.parentTInfo[k].zVal = zVal;
-		    
+		    renderAtom.parentTInfo[k].geometryAtom = renderAtom.geometryAtom;	    
 		}
 	    }
 	    
@@ -923,7 +937,7 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
 	    }
 	    
 	    // lock list of dlist
-	    // TODO: Instead of copying could we keep 2 arrays
+	    // XXXX: Instead of copying could we keep 2 arrays
 	    // and just toggle?
 	    size = dirtyRenderMoleculeList.size();
 	    for (i = 0; i < size; i++) {
@@ -964,7 +978,7 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
 	RenderAtomListInfo arr[];
 	RenderAtomListInfo ra;
 	
-	// TODO: there is a possible problem in the case of multiple
+	// XXXX: there is a possible problem in the case of multiple
 	// renderers (i.e., multiple screens).  Unless the
 	// MasterControl sends us a separate message for each
 	// renderer, we won't create a new display list for renderers
@@ -1125,6 +1139,7 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
 	rmUpdateList.clear();
 	ogCIOList.clear();
 	aBinUpdateList.clear();
+	sBinUpdateList.clear();
 	tbUpdateList.clear();
 	removeRenderAtomInRMList.clear();
 	addOpaqueBin = null;
@@ -1334,6 +1349,10 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
 			    }
 			}			
 			texIdObj = new Integer(id);
+                        // XXXX: The following code seems wrong -- why add it to
+                        // the list if it is already there? Maybe one is for the
+                        // texture and the other (idential value) is for the
+                        // detail texture?
 			if (cv.textureIdResourceFreeList.contains(texIdObj)) {
 			    cv.textureIdResourceFreeList.add(texIdObj);
 			    dtex.resourceCreationMask[tex.format] &= ~cv.canvasBit;
@@ -1616,8 +1635,13 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
 		    // Texture is always in a sole user position
 		    processTextureChanged((NodeComponentRetained) m.args[0],
 					  (GeometryAtom[])m.args[3],
-					  m.args);
-		    
+					  m.args);		    
+		    m.decRefcount();
+		    break;  
+     		case J3dMessage.SHADER_APPEARANCE_CHANGED:
+     		case J3dMessage.SHADER_ATTRIBUTE_SET_CHANGED:
+     		case J3dMessage.SHADER_ATTRIBUTE_CHANGED:	    
+		    processShaderComponentChanged(m.args);
 		    m.decRefcount();
 		    break;
 		case J3dMessage.RENDERINGATTRIBUTES_CHANGED:
@@ -1695,7 +1719,7 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
 			    updateViewPlatform((ViewPlatformRetained)vp.retained, 
 					       ((Float)m.args[1]).floatValue());
 			    visQuery = true;
-			    // TODO : Handle view.visibilityPolicy changed.
+			    // XXXX : Handle view.visibilityPolicy changed.
 			    if(((View.VISIBILITY_POLICY_DIRTY != 0) &&
 				(View.VISIBILITY_DRAW_ALL != view.viewCache.visibilityPolicy)) ||
 			       locale != ((ViewPlatformRetained) (vp.retained)).locale) {
@@ -2050,7 +2074,7 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
 
 	RenderAtom ra = null;
 	TextureBin tb;
-        AttributeBin ab;
+        ShaderBin sb;
 	boolean reInsertNeeded = false;
 
 	if (nc.mirror.changedFrequent == 0) {
@@ -2101,9 +2125,9 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
 		tb.soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_TA;
 
 	    } else {
-	        ab= ra.renderMolecule.textureBin.attributeBin;
+	        sb= ra.renderMolecule.textureBin.shaderBin;
 	        ra.renderMolecule.removeRenderAtom(ra);
-	        reInsertTextureBin(ab, ra);
+	        reInsertTextureBin(sb, ra);
 	    }
 	}
     }
@@ -2113,7 +2137,7 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
 
 	RenderAtom ra = null;
 	TextureBin tb;
-        AttributeBin ab;
+        ShaderBin sb;
 	boolean reInsertNeeded = false;
 
 	if (nc.mirror.changedFrequent == 0) {
@@ -2164,9 +2188,9 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
 		tb.soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_TC;
 
 	    } else {
-	        ab= ra.renderMolecule.textureBin.attributeBin;
+	        sb= ra.renderMolecule.textureBin.shaderBin;
 	        ra.renderMolecule.removeRenderAtom(ra);
-	        reInsertTextureBin(ab, ra);
+	        reInsertTextureBin(sb, ra);
 	    }
 	}
     }
@@ -2178,7 +2202,7 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
 
 	RenderAtom ra = null;
 	TextureBin tb;
-        AttributeBin ab;
+        ShaderBin sb;
 	boolean reInsertNeeded = false;
         int command = ((Integer)args[1]).intValue();
 
@@ -2275,7 +2299,7 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
 					GeometryAtom[] gaArr) {
 	RenderAtom ra = null;
 	TextureBin tb;
-        AttributeBin ab;
+        ShaderBin sb;
 	boolean mirrorSet = false;
 	boolean firstTextureBin = true;
 
@@ -2313,9 +2337,9 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
                 tb.soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_TUS;
 
 	    } else {
-	        ab= ra.renderMolecule.textureBin.attributeBin;
+	        sb = ra.renderMolecule.textureBin.shaderBin;
 	        ra.renderMolecule.removeRenderAtom(ra);
-	        reInsertTextureBin(ab, ra);
+	        reInsertTextureBin(sb, ra);
 	    }
 	}
     }
@@ -2359,7 +2383,7 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
 		    if (ra== null || !ra.inRenderBin())
 			continue;
 		    if (restructure && !ra.renderMolecule.textureBin.attributeBin.soleUser) {
-			EnvironmentSet e= ra.renderMolecule.textureBin.attributeBin.environmentSet;
+			EnvironmentSet e= ra.renderMolecule.textureBin.environmentSet;
 			ra.renderMolecule.removeRenderAtom(ra);
 			reInsertAttributeBin(e, ra);
 			/*
@@ -2400,6 +2424,131 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
 	}
     }
 
+    /**
+     * This processes a shader component change.
+     */
+    void processShaderComponentChanged(Object[] args) {
+
+	// System.out.println("RenderBin : processShaderComponentChanged");
+
+	int component = ((Integer)args[1]).intValue();
+	int i;
+	GeometryAtom[] gaArr = (GeometryAtom[] )args[3];
+	GeometryAtom  ga;
+	RenderAtom ra = null;
+	/* TODO : JADA - Sole user logic is incomplete. Will disable for JavaOne */
+	// Note : args[0] may be a ShaderAppearanceRetained or ShaderAttributeSetRetained
+	//ShaderAppearanceRetained sApp = (ShaderAppearanceRetained) args[0];
+	int start = -1;
+
+
+	// Get the first ra that is visible
+	for (i = 0; (i < gaArr.length && (start < 0)); i++) {
+	    ra = gaArr[i].getRenderAtom(view);
+	    if (ra== null || !ra.inRenderBin()) {
+		continue;
+	    }
+	    else {
+		start = i;
+	    }
+	}
+	if (start >= 0) {
+
+	    
+
+	    boolean spUpdate = 
+		((component & ShaderAppearanceRetained.SHADER_PROGRAM) != 0);
+	    boolean sasUpdate = 
+		(((component & ShaderAppearanceRetained.SHADER_ATTRIBUTE_SET) != 0) ||
+		 ((component & ShaderAttributeSetRetained.ATTRIBUTE_SET_PUT) != 0) ||
+		 ((component & ShaderAttributeSetRetained.ATTRIBUTE_SET_REMOVE) != 0) ||
+		 ((component & ShaderAttributeSetRetained.ATTRIBUTE_SET_CLEAR) != 0) ||
+		 ((component & ShaderAttributeRetained.SHADER_ATTRIBUTE_VALUE_UPDATE) != 0));
+	    
+	    if (spUpdate) {
+		/* TODO : JADA - Sole user logic is incomplete. Will disable for JavaOne */
+		//if (false && (sApp.mirror.changedFrequent & component) != 0) {
+                if(false) {
+		    /*
+		      System.out.println("RenderBin : Shader sole user (SHADER_PROGRAM)" +
+		      ra.renderMolecule.textureBin.shaderBin);
+		    */
+
+		    ShaderBin sBin;
+		
+		    for (i = start; i < gaArr.length; i++) {
+                        ra = gaArr[i].getRenderAtom(view);
+                        if (ra== null || !ra.inRenderBin())
+                            continue;
+                        
+                        sBin = ra.renderMolecule.textureBin.shaderBin;
+                        
+                        if (sBin.componentDirty == 0) {
+                            sBinUpdateList.add(sBin);
+                            sBin.componentDirty |= ShaderBin.SHADER_PROGRAM_DIRTY;
+                        }
+                    }
+		} else {
+		    /*
+		      System.out.println("RenderBin : not soleUser (SHADER_PROGRAM)" +
+		      ra.renderMolecule.textureBin.shaderBin);
+		    */
+
+		    for (i = 0; i < gaArr.length; i++) {
+			ra = gaArr[i].getRenderAtom(view);
+			if (ra== null || !ra.inRenderBin())
+			    continue;
+			
+			AttributeBin attrBin = ra.renderMolecule.textureBin.attributeBin;
+			ra.renderMolecule.removeRenderAtom(ra);
+			reInsertShaderBin(attrBin, ra);
+		    }
+		}
+	    } else if (sasUpdate) {
+		/* TODO : JADA - Sole user logic is incomplete. Will disable for JavaOne */
+		//if (false && (sApp.mirror.changedFrequent & component) != 0) {
+                  if(false) {
+                    /*
+		      System.out.println("RenderBin : sole user (SHADER_ATTRIBUTE_SET)" +
+		      ra.renderMolecule.textureBin.shaderBin);
+		    */
+
+		    ShaderBin sBin;
+		    
+		    for (i = 0; i < gaArr.length; i++) {
+			ra = gaArr[i].getRenderAtom(view);
+			if (ra== null || !ra.inRenderBin())
+			    continue;
+
+			
+			sBin = ra.renderMolecule.textureBin.shaderBin;
+			
+			if (sBin.componentDirty == 0) {
+			    sBinUpdateList.add(sBin);
+			    sBin.componentDirty |= ShaderBin.SHADER_ATTRIBUTE_SET_DIRTY;
+			}
+		    }
+		} else {
+		    /*
+		       System.out.println("RenderBin :not soleUser (SHADER_ATTRIBUTE_SET) " +
+		       ra.renderMolecule.textureBin.shaderBin);
+		    */
+
+		    for (i = 0; i < gaArr.length; i++) {
+			ra = gaArr[i].getRenderAtom(view);
+			if (ra== null || !ra.inRenderBin())
+			    continue;
+			
+			AttributeBin attrBin = ra.renderMolecule.textureBin.attributeBin;
+			ra.renderMolecule.removeRenderAtom(ra);
+			reInsertShaderBin(attrBin, ra);
+		    }
+		}
+	    }
+	}
+	    
+    }
+    
 
     void processFogChanged(Object[] args) {
 	FogRetained fog = (FogRetained)args[0];
@@ -2464,7 +2613,7 @@ class RenderBin extends J3dStructure  implements ObjectUpdate {
 	    if ((component & TEXTURE_STATE_CHANGED) != 0) {
 
 
-	        if (((app.changedFrequent & TEXTURE_STATE_CHANGED) != 0) &&
+	        if (((app.mirror.changedFrequent & TEXTURE_STATE_CHANGED) != 0) &&
 			((ra.renderMolecule.textureBin.tbFlag & 
 				TextureBin.SOLE_USER) != 0))  {
 
@@ -2501,8 +2650,8 @@ System.out.println("renderbin. texture state changed  tb not sole user " +
 
 System.out.println("......tb.soleUser= " + 
         ((ra.renderMolecule.textureBin.tbFlag & TextureBin.SOLE_USER) != 0) +
-        " app.changedFrequent= " +
-        ((app.changedFrequent & TEXTURE_STATE_CHANGED) != 0));
+        " app.mirror.changedFrequent= " +
+        ((app.mirror.changedFrequent & TEXTURE_STATE_CHANGED) != 0));
 
 */
 
@@ -2510,9 +2659,9 @@ System.out.println("......tb.soleUser= " +
 		        ra = gaArr[i].getRenderAtom(view);
 		        if (ra== null || !ra.inRenderBin())
 		            continue;
-		        AttributeBin ab = ra.renderMolecule.textureBin.attributeBin;
+		        ShaderBin sb = ra.renderMolecule.textureBin.shaderBin;
 		        ra.renderMolecule.removeRenderAtom(ra);
-		        reInsertTextureBin(ab, ra);
+		        reInsertTextureBin(sb, ra);
 	            }
 	        }
 	    } else if ((component & AppearanceRetained.RENDERING) != 0) {
@@ -2551,7 +2700,7 @@ System.out.println("......tb.soleUser= " +
 			    ra = gaArr[i].getRenderAtom(view);
 			    if (ra== null || !ra.inRenderBin())
 				continue;
-			    EnvironmentSet e = ra.renderMolecule.textureBin.attributeBin.environmentSet;
+			    EnvironmentSet e = ra.renderMolecule.textureBin.environmentSet;
 			    ra.renderMolecule.removeRenderAtom(ra);
 			    reInsertAttributeBin(e, ra);
 			}
@@ -2656,7 +2805,7 @@ System.out.println("......tb.soleUser= " +
 	Object[] users = (Object[])(args[3]);
 	int i;
 
-	// TODO: Handle other object affected by bounding leaf changes
+	// XXXX: Handle other object affected by bounding leaf changes
 	for (i = 0; i < users.length; i++) {
 	    LeafRetained leaf = (LeafRetained)users[i];
 	    switch(leaf.nodeType) {
@@ -2725,7 +2874,7 @@ System.out.println("......tb.soleUser= " +
 			    continue;
 			}
 			ra.app = ra.geometryAtom.source.appearance;
-			e = ra.renderMolecule.textureBin.attributeBin.environmentSet;
+			e = ra.renderMolecule.textureBin.environmentSet;
 			ra.renderMolecule.removeRenderAtom(ra);
 			reInsertAttributeBin(e, ra);
 		    }
@@ -2740,7 +2889,7 @@ System.out.println("......tb.soleUser= " +
 			if (ra.app == ra.geometryAtom.source.otherAppearance)
 			    continue;
 			ra.app = ra.geometryAtom.source.appearance;
-			e = ra.renderMolecule.textureBin.attributeBin.environmentSet;
+			e = ra.renderMolecule.textureBin.environmentSet;
 			ra.renderMolecule.removeRenderAtom(ra);
 			reInsertAttributeBin(e, ra);
 		    }
@@ -2793,7 +2942,7 @@ System.out.println("......tb.soleUser= " +
 		    app = saveApp;
 		}		
 		ra.app = app;
-		e = ra.renderMolecule.textureBin.attributeBin.environmentSet;
+		e = ra.renderMolecule.textureBin.environmentSet;
 		ra.renderMolecule.removeRenderAtom(ra);
 		reInsertAttributeBin(e, ra);
 	    }
@@ -2860,7 +3009,7 @@ System.out.println("......tb.soleUser= " +
 			    continue;
 			}
 			ra.app = ra.geometryAtom.source.appearance;
-			e = ra.renderMolecule.textureBin.attributeBin.environmentSet;
+			e = ra.renderMolecule.textureBin.environmentSet;
 			ra.renderMolecule.removeRenderAtom(ra);
 			reInsertAttributeBin(e, ra);
 		    }
@@ -2875,7 +3024,7 @@ System.out.println("......tb.soleUser= " +
 			if (ra.app == ra.geometryAtom.source.otherAppearance)
 			    continue;
 			ra.app = ra.geometryAtom.source.appearance;
-			e = ra.renderMolecule.textureBin.attributeBin.environmentSet;
+			e = ra.renderMolecule.textureBin.environmentSet;
 			ra.renderMolecule.removeRenderAtom(ra);
 			reInsertAttributeBin(e, ra);
 		    }
@@ -2926,7 +3075,7 @@ System.out.println("......tb.soleUser= " +
 		    app = saveApp;
 		}		
 		ra.app = app;
-		e = ra.renderMolecule.textureBin.attributeBin.environmentSet;
+		e = ra.renderMolecule.textureBin.environmentSet;
 		ra.renderMolecule.removeRenderAtom(ra);
 		reInsertAttributeBin(e, ra);
 	    }
@@ -3243,7 +3392,7 @@ System.out.println("......tb.soleUser= " +
 	}
     }
 
-    void processText3DTransformChanged(Object[] list, 
+    private void processText3DTransformChanged(Object[] list, 
 				       Object[] transforms,
 				       long referenceTime) {
 	int i, j, numShapes;
@@ -3290,7 +3439,7 @@ System.out.println("......tb.soleUser= " +
     }
 
 
-    void processOrderedGroupRemoved(J3dMessage m) {
+    private void processOrderedGroupRemoved(J3dMessage m) {
 	int i, n;
 	Object[] ogList = (Object[])m.args[0];
 	Object[] ogChildIdList = (Object[])m.args[1];
@@ -3329,7 +3478,7 @@ System.out.println("......tb.soleUser= " +
     }
 
 
-    void processOrderedGroupInserted(J3dMessage m) {
+    private void processOrderedGroupInserted(J3dMessage m) {
 	Object[] ogList = (Object[])m.args[0];
 	Object[] ogChildIdList = (Object[])m.args[1];
 	Object[] ogOrderedIdList = (Object[])m.args[2];
@@ -3368,7 +3517,7 @@ System.out.println("......tb.soleUser= " +
 	}
     }
     
-    void processTransformChanged(long referenceTime) {
+    private void processTransformChanged(long referenceTime) {
 	int i, j, k, numRenderMolecules, n;
 	Shape3DRetained s;
 	RenderMolecule rm;
@@ -3435,8 +3584,7 @@ System.out.println("......tb.soleUser= " +
 			else {
 			    app = ra.geometryAtom.source.appearance;
 			}
-			// TODO: Should we do a more extensive equals
-			// app?
+			// XXXX: Should we do a more extensive equals app?
 			if (ra.envSet.equals(ra, lights, fog, modelClip) &&
 			    app == ra.app) {
 
@@ -3614,7 +3762,7 @@ System.out.println("......tb.soleUser= " +
     /**
      * This processes a LIGHT change.
      */
-    void processLightChanged() {
+    private void processLightChanged() {
 	int i, j, k, l, n;
 	LightRetained lt;
 	EnvironmentSet e;
@@ -3785,7 +3933,7 @@ System.out.println("......tb.soleUser= " +
 
 
     
-    void processBgGeometryAtoms(GeometryAtom[] nodes, long referenceTime) {
+    private void processBgGeometryAtoms(GeometryAtom[] nodes, long referenceTime) {
         int i;
 	GeometryAtom ga;
 	RenderAtom renderAtom;
@@ -3826,7 +3974,7 @@ System.out.println("......tb.soleUser= " +
      * This method looks through the list of RenderAtoms to see if
      * compaction is needed.
      */
-    void checkForCompaction() {
+    private void checkForCompaction() {
 	int i, numRas;
 	int numDead = 0;
 	int numAlive = 0;
@@ -3879,7 +4027,7 @@ System.out.println("......tb.soleUser= " +
 
     }
 
-    void reEvaluateAlternateAppearance() {
+    private void reEvaluateAlternateAppearance() {
 	AppearanceRetained app;
 	EnvironmentSet e;
 	Object[] retVal;
@@ -3924,7 +4072,7 @@ System.out.println("......tb.soleUser= " +
 	
     }
 
-    void reEvaluateAllRenderAtoms(boolean altAppDirty) {
+    private void reEvaluateAllRenderAtoms(boolean altAppDirty) {
 	
 	int sz = renderAtoms.size();
 
@@ -3966,7 +4114,7 @@ System.out.println("......tb.soleUser= " +
 	    // If the lights/fog/model_clip of the render atom is the same
 	    // as the old set of lights/fog/model_clip, then move on to the
 	    // next renderAtom
-	    // TODO: Should app test for equivalent?
+	    // XXXX: Should app test for equivalent?
 	    if (ra.envSet.equals(ra, lights, newfog, newModelClip) &&
 		app == ra.app)
 		continue;
@@ -3996,7 +4144,7 @@ System.out.println("......tb.soleUser= " +
 
 
 
-    void getNewEnvironment(RenderAtom ra, LightRetained[] lights,
+    private void getNewEnvironment(RenderAtom ra, LightRetained[] lights,
 			   FogRetained fog, ModelClipRetained modelClip,
 			   AppearanceRetained app) {
 
@@ -4176,28 +4324,36 @@ System.out.println("......tb.soleUser= " +
 	reInsertAttributeBin(eNew, ra);
 
     }
-    void reInsertAttributeBin(EnvironmentSet e, RenderAtom ra) {
+
+    private void reInsertAttributeBin(EnvironmentSet e, RenderAtom ra) {
 	AttributeBin ab;
 	// Just go up to the environment and re-insert
 	ab = findAttributeBin(e, ra);
-	reInsertTextureBin(ab, ra);
+	reInsertShaderBin(ab, ra);
     }
 
+    private void reInsertShaderBin(AttributeBin ab, RenderAtom ra) {
+	ShaderBin sb;
+	
+	// System.out.println("RenderBin.reInsertShaderBin() ra= " + ra);
+	sb = findShaderBin(ab, ra);
+	reInsertTextureBin(sb, ra);
+    }
 
-    void reInsertTextureBin(AttributeBin ab, RenderAtom ra) {
+    private void reInsertTextureBin(ShaderBin sb, RenderAtom ra) {
 	TextureBin tb;
 
-	tb = findTextureBin(ab, ra);
+	tb = findTextureBin(sb, ra);
 	reInsertRenderAtom(tb, ra);
     }
 
-    void reInsertRenderAtom(TextureBin tb, RenderAtom ra) {
+    private void reInsertRenderAtom(TextureBin tb, RenderAtom ra) {
 	RenderMolecule newRm;
 	// Just go up to the texture bin and re-insert
 	newRm = findRenderMolecule(tb, ra);
     }
 
-    void computeViewFrustumBBox(BoundingBox viewFrustumBBox) {
+    private void computeViewFrustumBBox(BoundingBox viewFrustumBBox) {
 	//Initial view frustumBBox BBox
 	viewFrustumBBox.lower.x = Float.POSITIVE_INFINITY;
 	viewFrustumBBox.lower.y = Float.POSITIVE_INFINITY;
@@ -4241,10 +4397,11 @@ System.out.println("......tb.soleUser= " +
     /**
      * This inserts a RenderAtom into the appropriate bin.
      */
-    RenderMolecule insertRenderAtom(RenderAtom ra) {
+    private RenderMolecule insertRenderAtom(RenderAtom ra) {
 	LightBin lightBin;
 	EnvironmentSet environmentSet;
 	AttributeBin attributeBin;
+	ShaderBin shaderBin;
 	TextureBin textureBin;
 	RenderMolecule renderMolecule;
 	OrderedCollection oc;
@@ -4301,7 +4458,11 @@ System.out.println("......tb.soleUser= " +
 	// determined
 	environmentSet = findEnvironmentSet(ra);
 	attributeBin = findAttributeBin(environmentSet, ra);
-	textureBin = findTextureBin(attributeBin, ra);
+
+	// System.out.println("RenderBin : findShaderBin()");
+	shaderBin = findShaderBin(attributeBin, ra);
+
+	textureBin = findTextureBin(shaderBin, ra);
 	renderMolecule = findRenderMolecule(textureBin, ra);
         ra.setRenderBin(true);
 	renderAtoms.add(ra);
@@ -4347,7 +4508,7 @@ System.out.println("......tb.soleUser= " +
 	return (renderMolecule);
     }
     
-    OrderedCollection findOrderedCollection(GeometryAtom ga, 
+    private OrderedCollection findOrderedCollection(GeometryAtom ga, 
 					    boolean doBackground) {
         int i, n;
         int oi; // an id which identifies a children of the orderedGroup
@@ -4509,7 +4670,7 @@ System.out.println("......tb.soleUser= " +
         return (oc);
     }
 
-    void removeOrderedHeadLightBin(LightBin lightBin) {
+    private void removeOrderedHeadLightBin(LightBin lightBin) {
         int i, k;
         int oi; // an id which identifies a children of the orderedGroup
         int ci; // child index of the ordered group
@@ -4541,7 +4702,7 @@ System.out.println("......tb.soleUser= " +
      * This gets a new EnviornmentSet.  It creates one if there are none
      * on the freelist.
      */
-    EnvironmentSet getEnvironmentSet(RenderAtom ra, LightRetained[] lights, 
+    private EnvironmentSet getEnvironmentSet(RenderAtom ra, LightRetained[] lights, 
 				     FogRetained fog, ModelClipRetained modelClip) {
 	EnvironmentSet envSet;
 
@@ -4554,10 +4715,11 @@ System.out.println("......tb.soleUser= " +
 	}
 	return (envSet);
     }
+
     /**
      * This finds or creates an AttributeBin for a given RenderAtom.
      */
-    AttributeBin findAttributeBin(EnvironmentSet envSet, RenderAtom ra) {
+    private AttributeBin findAttributeBin(EnvironmentSet envSet, RenderAtom ra) {
 	int i;
 	AttributeBin currentBin;
 	RenderingAttributesRetained renderingAttributes;
@@ -4566,7 +4728,6 @@ System.out.println("......tb.soleUser= " +
 	} else {
 	    renderingAttributes = ra.app.renderingAttributes;
 	}
-
 	
 	currentBin = envSet.attributeBinList;
 	while (currentBin != null) {
@@ -4575,7 +4736,6 @@ System.out.println("......tb.soleUser= " +
 	    }
 	    currentBin = currentBin.next;
 	}
-
 	// Check the "to-be-added" list of attributeBins for a match
 	for (i = 0; i < envSet.addAttributeBins.size(); i++) {
 	    currentBin = (AttributeBin)envSet.addAttributeBins.get(i);
@@ -4583,59 +4743,88 @@ System.out.println("......tb.soleUser= " +
 		return(currentBin);
 	    }
 	}
-
 	currentBin = getAttributeBin(ra.app, renderingAttributes);
 	envSet.addAttributeBin(currentBin, this);
 	return(currentBin);
     }
 
     /**
-     * This finds or creates a TextureBin for a given RenderAtom.
+     * This finds or creates an ShaderBin for a given RenderAtom.
      */
-    TextureBin findTextureBin(AttributeBin attributeBin, RenderAtom ra) {
+    private ShaderBin findShaderBin(AttributeBin attributeBin, RenderAtom ra) {	
+	int i, size;
+	ShaderBin currentBin;
+	ShaderAppearanceRetained sApp;
 
+	if((ra != null) && (ra.app instanceof ShaderAppearanceRetained))
+	    sApp = (ShaderAppearanceRetained)ra.app;
+	else 
+	    sApp = null;
 
-	int i;
+	currentBin = attributeBin.shaderBinList;
+	while (currentBin != null) {
+	    if (currentBin.equals(sApp)) {
+		return currentBin;
+	    }
+	    currentBin = currentBin.next;
+	}
+
+	// Check the "to-be-added" list of shaderBins for a match
+	size = attributeBin.addShaderBins.size();
+	for (i = 0; i < size; i++) {
+	    currentBin = (ShaderBin)attributeBin.addShaderBins.get(i);
+	    if (currentBin.equals(sApp)) {
+		return currentBin;
+	    }
+	}
+	
+	currentBin = getShaderBin(sApp);
+	attributeBin.addShaderBin(currentBin, this, sApp);
+	return currentBin;
+    }
+
+    /**
+     * This finds or creates a TextureBin for a given RenderAtom.
+     */
+    private TextureBin findTextureBin(ShaderBin shaderBin, RenderAtom ra) {
+	int i, size;
 	TextureBin currentBin;
 	TextureRetained texture;
 	TextureUnitStateRetained texUnitState[];
 
-	RenderingAttributesRetained rAttrs =
-	    (ra.geometryAtom.source.appearance == null)? null:
-	    ra.geometryAtom.source.appearance.renderingAttributes;
-
 	if (ra.app == null) {
 	    texUnitState = null;
 	} else {
 	    texUnitState = ra.app.texUnitState;
 	}
 
-	currentBin = attributeBin.textureBinList;
+	currentBin = shaderBin.textureBinList;
 	while (currentBin != null) {
 	    if (currentBin.equals(texUnitState, ra)) {
-				//System.out.println("1: Equal");
+		//System.out.println("1: Equal");
 		return(currentBin);
 	    }
 	    currentBin = currentBin.next;
 	}
 	// Check the "to-be-added" list of TextureBins for a match
-	for (i = 0; i < attributeBin.addTBs.size(); i++) {
-	    currentBin = (TextureBin)attributeBin.addTBs.get(i);
+	size = shaderBin.addTextureBins.size();
+	for (i = 0; i < size; i++) {
+	    currentBin = (TextureBin)shaderBin.addTextureBins.get(i);
 	    if (currentBin.equals(texUnitState, ra)) {
-				//System.out.println("2: Equal");
+		//System.out.println("2: Equal");
 		return(currentBin);
 	    }
 	}
 	// get a new texture bin for this texture unit state
 	currentBin = getTextureBin(texUnitState, ra.app);
-	attributeBin.addTextureBin(currentBin, this, ra);
+	shaderBin.addTextureBin(currentBin, this, ra);
 	return(currentBin);
     }
 
     /**
      * This finds or creates a RenderMolecule for a given RenderAtom.
      */
-    RenderMolecule findRenderMolecule(TextureBin textureBin, 
+    private RenderMolecule findRenderMolecule(TextureBin textureBin, 
 				      RenderAtom ra) {
 
 	RenderMolecule currentBin;
@@ -4691,7 +4880,7 @@ System.out.println("......tb.soleUser= " +
 				  ra.geometryAtom.source.localToVworld[0])) {
 
 		currentBin.addRenderAtom(ra, this);
-		ra.envSet = ra.renderMolecule.textureBin.attributeBin.environmentSet;
+		ra.envSet = ra.renderMolecule.textureBin.environmentSet;
 		// If the locale has changed for an existing renderMolecule
 		// handle the RmlocaleToVworld
 		return(currentBin);
@@ -4731,12 +4920,26 @@ System.out.println("......tb.soleUser= " +
 	return(currentBin);
     }
 
+    /**
+     * This gets a new ShaderBin.  It creates one if there are none
+     * on the freelist.
+     */
+    private ShaderBin getShaderBin(ShaderAppearanceRetained sApp) {
+	ShaderBin shaderBin;
+	if (shaderBinFreelist.size() > 0) {
+	    shaderBin = (ShaderBin)shaderBinFreelist.remove(shaderBinFreelist.size()-1);
+	    shaderBin.reset(sApp, this);
+	} else {
+	    shaderBin = new ShaderBin( sApp, this);
+	}
+	return (shaderBin);
+    }
 
     /**
      * This gets a new AttributeBin.  It creates one if there are none
      * on the freelist.
      */
-    AttributeBin getAttributeBin(AppearanceRetained app, RenderingAttributesRetained ra) {
+    private AttributeBin getAttributeBin(AppearanceRetained app, RenderingAttributesRetained ra) {
 	AttributeBin attrBin;
 	if (attrBinFreelist.size() > 0) {
 	    attrBin = (AttributeBin)attrBinFreelist.remove(
@@ -4754,7 +4957,7 @@ System.out.println("......tb.soleUser= " +
      * This gets a new LightBin.  It creates one if there are none
      * on the freelist.
      */
-    LightBin getLightBin(int maxLights, BackgroundRetained bg, boolean inOpaque) {
+    private LightBin getLightBin(int maxLights, BackgroundRetained bg, boolean inOpaque) {
 	LightBin lightBin;
 
 	if (lightBinFreelist.size() > 0) {
@@ -4772,7 +4975,7 @@ System.out.println("......tb.soleUser= " +
      * This gets a new TextureBin.  It creates one if there are none
      * on the freelist.
      */
-    TextureBin getTextureBin(TextureUnitStateRetained texUnitState[],
+    private TextureBin getTextureBin(TextureUnitStateRetained texUnitState[],
 				AppearanceRetained app) {
 	TextureBin textureBin;
 
@@ -4791,7 +4994,7 @@ System.out.println("......tb.soleUser= " +
      * This gets a new RenderMolecule.  It creates one if there are none
      * on the freelist.
      */
-    RenderMolecule getRenderMolecule(GeometryAtom ga,
+   private  RenderMolecule getRenderMolecule(GeometryAtom ga,
 				     PolygonAttributesRetained polya,
 				     LineAttributesRetained linea,
 				     PointAttributesRetained pointa,
@@ -4823,7 +5026,7 @@ System.out.println("......tb.soleUser= " +
      * This finds or creates an EnviornmentSet for a given RenderAtom.
      * This also deals with empty LightBin lists.  
      */
-    EnvironmentSet findEnvironmentSet(RenderAtom ra) {
+    private EnvironmentSet findEnvironmentSet(RenderAtom ra) {
 	LightBin currentBin, lightBin ;
 	EnvironmentSet currentEnvSet, newBin;
 	int i;
@@ -5040,9 +5243,9 @@ System.out.println("......tb.soleUser= " +
      */
     void renderOpaque(Canvas3D cv) {
 	LightBin currentBin = opaqueBin;
-	//	System.out.println("========> renderOpaque");
+	//System.out.println("========> renderOpaque");
 	while (currentBin != null) {
-	    //      System.out.println("====> rendering Opaque Bin ");
+	    //System.out.println("====> rendering Opaque Bin ");
 	    currentBin.render(cv);
 	    currentBin = currentBin.next;
 	}
@@ -5055,9 +5258,10 @@ System.out.println("......tb.soleUser= " +
     void renderTransparent(Canvas3D cv) {
         boolean savedDepthBufferWriteEnable = true;
 
+        //System.out.println("====> renderTransparent");
 	TransparentRenderingInfo tinfo = transparentInfo;
 	if (tinfo != null) {
-	    //      System.out.println("====> rendering transparent Bin");
+	    //System.out.println("====> rendering transparent Bin");
 
 	    if (cv.view.depthBufferFreezeTransparent) {
 		cv.setDepthBufferWriteEnableOverride(true);
@@ -5110,9 +5314,7 @@ System.out.println("......tb.soleUser= " +
         OrderedCollection oc;
         boolean depthBufferEnable = true;
 	OrderedGroupRetained og = orderedBin.source;
-	boolean isDecal = (og instanceof DecalGroupRetained) &&
-	    ((cv.extensionsSupported & Canvas3D.STENCIL_BUFFER) != 0);
-
+	boolean isDecal = (og instanceof DecalGroupRetained) && cv.systemStencilAvailable;
 	int size = orderedBin.orderedCollections.size();
 	
 	// System.out.println("RB : orderedBin.orderedCollections.size() " + size);
@@ -5198,9 +5400,14 @@ System.out.println("......tb.soleUser= " +
 	Canvas3D canvases[] = view.getCanvases();	
 	for (int i=0; i< canvases.length; i++) {
 	    Canvas3D canvas = canvases[i];
-	    if(cvDirty)
-		canvas.cvDirtyMask |= Canvas3D.BACKGROUND_DIRTY;
-	    canvas.cvDirtyMask |= Canvas3D.BACKGROUND_IMAGE_DIRTY;
+            synchronized (canvas.dirtyMaskLock) {
+                if(cvDirty) {
+                    canvas.cvDirtyMask[0] |= Canvas3D.BACKGROUND_DIRTY;
+                    canvas.cvDirtyMask[1] |= Canvas3D.BACKGROUND_DIRTY;
+                }
+                canvas.cvDirtyMask[0] |= Canvas3D.BACKGROUND_IMAGE_DIRTY;
+                canvas.cvDirtyMask[1] |= Canvas3D.BACKGROUND_IMAGE_DIRTY;
+            }
 	}
     }
 
@@ -5801,6 +6008,7 @@ System.out.println("......tb.soleUser= " +
 	lightBinFreelist.clear();
 	envSetFreelist.clear();
 	attrBinFreelist.clear();
+	shaderBinFreelist.clear();
 	textureBinFreelist.clear();
 	renderMoleculeFreelist.clear();
 	
@@ -5848,22 +6056,26 @@ System.out.println("......tb.soleUser= " +
 		AttributeBin abin = envSet.attributeBinList;
 		while (abin != null) {
 		    System.out.println("      ABin = "+abin);
-		    TextureBin tbin = abin.textureBinList;
-		    while (tbin != null) {
-			System.out.println("         Tbin = "+tbin);
-			RenderMolecule rm = tbin.opaqueRMList;
-			System.out.println("===> Begin Dumping OpaqueBin");
-			dumpRM(rm);
-			System.out.println("===> End Dumping OpaqueBin");
-			rm = tbin.transparentRMList;
-			System.out.println("===> Begin Dumping transparentBin");
-			dumpRM(rm);
-			System.out.println("===> End Dumping transparentBin");
-			tbin = tbin.next;
+		    ShaderBin sbin = abin.shaderBinList;
+		    while (sbin != null) {
+			System.out.println("         SBin = "+sbin);
+			TextureBin tbin = sbin.textureBinList;
+			while (tbin != null) {
+			    System.out.println("             Tbin = "+tbin);
+			    RenderMolecule rm = tbin.opaqueRMList;
+			    System.out.println("===> Begin Dumping OpaqueBin");
+			    dumpRM(rm);
+			    System.out.println("===> End Dumping OpaqueBin");
+			    rm = tbin.transparentRMList;
+			    System.out.println("===> Begin Dumping transparentBin");
+			    dumpRM(rm);
+			    System.out.println("===> End Dumping transparentBin");
+			    tbin = tbin.next;
+			}
+			sbin = sbin.next;
 		    }
 		    abin = abin.next;
 		}
-		
 		envSet = envSet.next;
 	    }
 	    obin = obin.next;
@@ -5914,7 +6126,7 @@ System.out.println("......tb.soleUser= " +
 	// System.out.println("&&&&&&&&&&&&removeTransparentObject r = "+obj);
 	if (obj instanceof TextureBin) {
 	    TextureBin tb = (TextureBin) obj;
-	    if (tb.attributeBin.environmentSet.lightBin.geometryBackground != null) {
+	    if (tb.environmentSet.lightBin.geometryBackground != null) {
 		TransparentRenderingInfo t = tb.parentTInfo;
 
 		// Remove the element from the transparentInfo struct
@@ -6018,7 +6230,7 @@ System.out.println("......tb.soleUser= " +
 	if (obj instanceof TextureBin) {
 	    TextureBin tb = (TextureBin) obj;
 	    // Background geometry
-	    if (tb.attributeBin.environmentSet.lightBin.geometryBackground != null) {
+	    if (tb.environmentSet.lightBin.geometryBackground != null) {
 		bgTransparentInfo = computeDirtyAcrossTransparentBins(tb, bgTransparentInfo);		
 	    }
 	    else {
@@ -6063,8 +6275,8 @@ System.out.println("......tb.soleUser= " +
     TransparentRenderingInfo computeDirtyAcrossTransparentBins(TextureBin tb, TransparentRenderingInfo startinfo) {
 	TransparentRenderingInfo tinfo = getTransparentInfo();
 	/*
-	  tinfo.lightBin = tb.attributeBin.environmentSet.lightBin;
-	  tinfo.envSet = tb.attributeBin.environmentSet;
+	  tinfo.lightBin = tb.environmentSet.lightBin;
+	  tinfo.envSet = tb.environmentSet;
 	  tinfo.aBin = tb.attributeBin;
 	*/
 	tinfo.rm = tb.transparentRMList;
@@ -6257,7 +6469,16 @@ System.out.println("......tb.soleUser= " +
 	    zval1 = input1.zVal;
 	    zval2 = input2.zVal;
 	    // Put the newList before the current one
-	    if (zval2 > zval1) {
+            
+//            System.out.print("Code path 1 ");
+//            if (transparencySortComparator!=null) 
+//                if (zval2 > zval1 && (transparencySortComparator.compare(input2, input1)>0))
+//                    System.out.println("PASS");
+//                else
+//                    System.out.println("FAIL");
+        
+            if ((transparencySortComparator==null && zval2 > zval1) ||
+                (transparencySortComparator!=null && (transparencySortComparator.compare(input2, input1)>0))){
 		//		System.out.println("===> path1");
 		if (input1.prev == null) {
 		    input1.prev = input2;
@@ -6293,38 +6514,48 @@ System.out.println("......tb.soleUser= " +
 	return oldList;
     }
 
-    void insertDepthSort(RenderAtom r) {
-	TransparentRenderingInfo tinfo = null;
-	//	System.out.println("&&&&&&&&insertDepthSort");
-	for (int i = 0; i < r.rListInfo.length; i++) {
-	    if (r.parentTInfo[i] == null)
-		continue;
-	    
-	    if (transparentInfo == null) {
-		transparentInfo = r.parentTInfo[i];
-		transparentInfo.prev = null;
-		transparentInfo.next = null;
-	    }
-	    else {
-		tinfo = transparentInfo;
-		TransparentRenderingInfo prevInfo = transparentInfo;
-		while (tinfo != null&& r.parentTInfo[i].zVal < tinfo.zVal) {
-		    prevInfo = tinfo;
-		    tinfo = tinfo.next;
-		}
-		r.parentTInfo[i].prev = prevInfo;
-		if (prevInfo.next != null) {
-		    prevInfo.next.prev = r.parentTInfo[i];
-		}
-		r.parentTInfo[i].next = prevInfo.next;
-		prevInfo.next = r.parentTInfo[i];
-		
-	    }
-	    
-	}
-    }
-
-
+//    void insertDepthSort(RenderAtom r) {
+//	TransparentRenderingInfo tinfo = null;
+//	//	System.out.println("&&&&&&&&insertDepthSort");
+//	for (int i = 0; i < r.rListInfo.length; i++) {
+//	    if (r.parentTInfo[i] == null)
+//		continue;
+//	    
+//	    if (transparentInfo == null) {
+//		transparentInfo = r.parentTInfo[i];
+//		transparentInfo.prev = null;
+//		transparentInfo.next = null;
+//	    }
+//	    else {
+//		tinfo = transparentInfo;
+//		TransparentRenderingInfo prevInfo = transparentInfo;
+//                if (transparencySortComparator==null)
+//                    while (tinfo != null && r.parentTInfo[i].zVal < tinfo.zVal) {
+//                        prevInfo = tinfo;
+//                        tinfo = tinfo.next;
+//                    }
+//                else {
+//                    System.out.println("Code Path 2 ");
+//                    if (tinfo!=null && (transparencySortComparator.compare(r.parentTInfo[i], tinfo)<0)==r.parentTInfo[i].zVal < tinfo.zVal)
+//                        System.out.println("PASS");
+//                    else
+//                        System.out.println("FAIL");
+//                    while (tinfo != null && transparencySortComparator.compare(r.parentTInfo[i], tinfo)<0) {
+//                        prevInfo = tinfo;
+//                        tinfo = tinfo.next;
+//                    }
+//                }
+//		r.parentTInfo[i].prev = prevInfo;
+//		if (prevInfo.next != null) {
+//		    prevInfo.next.prev = r.parentTInfo[i];
+//		}
+//		r.parentTInfo[i].next = prevInfo.next;
+//		prevInfo.next = r.parentTInfo[i];
+//		
+//	    }
+//	    
+//	}
+//    }
 
     TransparentRenderingInfo collectDirtyTRInfo( TransparentRenderingInfo dirtyList,
 						 RenderAtom r) {
@@ -6371,6 +6602,7 @@ System.out.println("......tb.soleUser= " +
  
 
     TransparentRenderingInfo depthSortAll(TransparentRenderingInfo startinfo) {
+        transparencySortComparator = com.sun.j3d.utils.scenegraph.transparency.TransparencySortController.getComparator(view);
 	TransparentRenderingInfo tinfo, previnfo, nextinfo;
 	double curZ;
 	//	System.out.println("&&&&&&&&&&&depthSortAll");
@@ -6390,10 +6622,21 @@ System.out.println("......tb.soleUser= " +
 	    previnfo = tinfo.prev;
 	    // Find the correct location for tinfo
 
-	    while (previnfo != null && previnfo.zVal < curZ) {
-		previnfo = previnfo.prev;
-
-	    }
+            if (transparencySortComparator==null) {
+                while (previnfo != null && previnfo.zVal < curZ) {
+                    previnfo = previnfo.prev;
+                }
+            } else {
+//                    System.out.println("Code Path 3 ");
+//                    if (tinfo!=null && (transparencySortComparator.compare(previnfo, tinfo)<0)==previnfo.zVal < curZ)
+//                        System.out.println("PASS");
+//                    else
+//                        System.out.println("FAIL");
+                while (previnfo != null && transparencySortComparator.compare(previnfo,tinfo)<0) {
+                    previnfo = previnfo.prev;
+                }               
+            }
+                      
 	    if (tinfo.prev != previnfo) {
 		if (previnfo == null) {
 		    if (tinfo.next != null) {
@@ -6764,7 +7007,7 @@ System.out.println("......tb.soleUser= " +
 		// context creation bits for this canvas, but don't do anything
 		// with the geo's user list.
 		if (geo.dlistId > 0) {
-		    // TODO: for the shared ctx case, we really should
+		    // XXXX: for the shared ctx case, we really should
 		    // only free the display lists if this is the last
 		    // Canvas in the renderer.  However, since the
 		    // display lists will be recreated, it doesn't
@@ -6936,14 +7179,14 @@ System.out.println("......tb.soleUser= " +
 		if (ra== null || !ra.inRenderBin())
 		    continue;
 	    
-		EnvironmentSet e= ra.renderMolecule.textureBin.attributeBin.environmentSet;
+		EnvironmentSet e= ra.renderMolecule.textureBin.environmentSet;
 		ra.renderMolecule.removeRenderAtom(ra);
 		reInsertAttributeBin(e, ra);
 	    }
 	}
 	else {
 	    
-	    // TODO: handle texture
+	    // XXXX: handle texture
 	}
 
 	
diff --git a/src/classes/share/javax/media/j3d/RenderMolecule.java b/src/classes/share/javax/media/j3d/RenderMolecule.java
index d52cd93..61672cf 100644
--- a/src/classes/share/javax/media/j3d/RenderMolecule.java
+++ b/src/classes/share/javax/media/j3d/RenderMolecule.java
@@ -45,7 +45,7 @@ class RenderMolecule extends IndexedObject implements ObjectUpdate, NodeComponen
 				AppearanceRetained.TRANSPARENCY|
 				AppearanceRetained.COLOR);
 
-    // TODO: use definingMaterial etc. instead of these
+    // XXXX: use definingMaterial etc. instead of these
     // when sole user is completely implement
     PolygonAttributesRetained polygonAttributes = null;
     LineAttributesRetained lineAttributes = null;
@@ -740,7 +740,7 @@ class RenderMolecule extends IndexedObject implements ObjectUpdate, NodeComponen
 	    k++;
 	}
 
-	// TODO: Add tags
+	// XXXX: Add tags
 	switch (ga.geoType) {
 	case GeometryRetained.GEO_TYPE_POINT_SET:
 	case GeometryRetained.GEO_TYPE_INDEXED_POINT_SET:
@@ -788,7 +788,7 @@ class RenderMolecule extends IndexedObject implements ObjectUpdate, NodeComponen
 	    return (false);
 	}
 	/*
-	// TODO : Check this 
+	// XXXX : Check this 
 	if (useDisplayList &&
 	    (ga.geometry.isEditable ||
 	     ga.geometry.refCount > 1 ||
@@ -811,7 +811,7 @@ class RenderMolecule extends IndexedObject implements ObjectUpdate, NodeComponen
 	    return (false);
 	}
 
-	// TODO: Its is necessary to have same vformat for dl,
+	// XXXX: Its is necessary to have same vformat for dl,
 	// Howabout iteration, should we have 2 vformats in rm?
 	if (geo instanceof GeometryArrayRetained) {
 	    GeometryArrayRetained gr = (GeometryArrayRetained)geo;
@@ -845,7 +845,7 @@ class RenderMolecule extends IndexedObject implements ObjectUpdate, NodeComponen
 	        return (false);
 	    }
 	} else {
-            //TODO: compare isEditable
+            //XXXX: compare isEditable
 	    if (this.vertexFormat != -1) {
 	        return (false);
 	    }
@@ -1143,7 +1143,7 @@ class RenderMolecule extends IndexedObject implements ObjectUpdate, NodeComponen
 		renderBin.orientedRAs.remove(renderBin.orientedRAs.indexOf(r));
 	    }
 
-	    if ((textureBin.attributeBin.environmentSet.lightBin.geometryBackground == null) &&
+	    if ((textureBin.environmentSet.lightBin.geometryBackground == null) &&
 		!isOpaqueOrInOG && renderBin.transpSortMode == View.TRANSPARENCY_SORT_GEOMETRY) {
 		renderBin.removeTransparentObject(r);
 	    }
@@ -1318,7 +1318,7 @@ class RenderMolecule extends IndexedObject implements ObjectUpdate, NodeComponen
 
 		}
 		// If transparent and not in bg geometry and is depth sorted transparency
-		if (!isOpaqueOrInOG && (textureBin.attributeBin.environmentSet.lightBin.geometryBackground == null)&&
+		if (!isOpaqueOrInOG && (textureBin.environmentSet.lightBin.geometryBackground == null)&&
 		    (renderBin.transpSortMode == View.TRANSPARENCY_SORT_GEOMETRY)) {
 		    GeometryRetained geo = null;
 		    int k = 0;
@@ -1401,36 +1401,11 @@ class RenderMolecule extends IndexedObject implements ObjectUpdate, NodeComponen
     }
 
     boolean canBeInDisplayList(GeometryRetained geo, GeometryAtom ga) {
-	boolean inDL = false;
-	inDL =  geo.canBeInDisplayList(ga.alphaEditable);
-	// If can not in DL, then check if all the attrs affecting
-	// it are infrequently changing, if yes then put it
-	// Exclude Morph and indexed-by-ref-use-index-coord only  for now
-	// in displayList if OptimizeForSpace if false
-
-	//	System.out.println("inDL = "+inDL);
-	//	System.out.println("geo.cachedChangedFrequent = "+geo.cachedChangedFrequent);
-	//	System.out.println("inDL = "+inDL);
-	//	System.out.println("COLOR = "+((((GeometryArrayRetained)geo).vertexFormat&
-	//					     GeometryArray.COLOR) != 0));
-	//	System.out.println("Before: inDL = "+inDL);
-	//	System.out.println("VirtualUniverse.mc.buildDisplayListIfPossible = "+VirtualUniverse.mc.buildDisplayListIfPossible);
-	if (VirtualUniverse.mc.buildDisplayListIfPossible &&
-	    !inDL &&
-	    !(ga.source.sourceNode instanceof MorphRetained ||
-	      (geo instanceof GeometryArrayRetained &&
-	       ((((GeometryArrayRetained)geo).vertexFormat & (GeometryArray.USE_NIO_BUFFER|GeometryArray.INTERLEAVED)) ==(GeometryArray.USE_NIO_BUFFER|GeometryArray.INTERLEAVED))) ||
-	      (geo instanceof IndexedGeometryArrayRetained &&
-	       ((((IndexedGeometryArrayRetained)geo).vertexFormat & (GeometryArray.BY_REFERENCE|GeometryArray.USE_COORD_INDEX_ONLY)) == (GeometryArray.BY_REFERENCE|GeometryArray.USE_COORD_INDEX_ONLY))))) {
-	    // Check if geometry is frequentlyEditable
-	    boolean alphaFreqEditable = ga.source.isAlphaFrequentlyEditable(geo);
-	    inDL = !((geo.cachedChangedFrequent != 0) ||
-		     ((!(geo instanceof GeometryArrayRetained) && alphaFreqEditable)||
-		      (alphaFreqEditable && ((((GeometryArrayRetained)geo).vertexFormat&
-					      GeometryArray.COLOR) != 0))));
-	}
-	//	System.out.println("After: inDL = "+inDL);
-	return inDL;
+        if (ga.source.sourceNode instanceof MorphRetained) {
+            return false;
+        }
+
+	return geo.canBeInDisplayList(ga.alphaEditable);
     }
 
     // If dlist will be altered due to alpha or ignoreVertexColors, then don't
@@ -1498,7 +1473,7 @@ class RenderMolecule extends IndexedObject implements ObjectUpdate, NodeComponen
 	RenderAtomListInfo r;
 	int index;
 
-	renderAtom.envSet = textureBin.attributeBin.environmentSet;
+	renderAtom.envSet = textureBin.environmentSet;
 	renderAtom.renderMolecule = this;
 	renderAtom.dirtyMask &= ~RenderAtom.NEED_SEPARATE_LOCALE_VWC_BOUNDS;
 
@@ -1732,8 +1707,6 @@ class RenderMolecule extends IndexedObject implements ObjectUpdate, NodeComponen
 
     void evalAlphaUsage(RenderingAttributesRetained renderAttrs,
 			TextureUnitStateRetained[] texUnits) {
-        RenderingAttributesRetained ra;
-        TextureAttributesRetained ta;
         boolean alphaBlend, alphaTest, textureBlend = false;
 
         alphaBlend =
@@ -1847,7 +1820,7 @@ class RenderMolecule extends IndexedObject implements ObjectUpdate, NodeComponen
 	// we'll have to punt to vertex array as well.
 
 	if ((pass != TextureBin.USE_DISPLAYLIST) ||	
-	    (texCoordSetMapLen > cv.numTexCoordSupported) ||
+	    (texCoordSetMapLen > cv.maxTexCoordSets) ||
 	    (VirtualUniverse.mc.isD3D() &&
 	      (((definingPolygonAttributes != null) &&
 		((isQuadGeometryArray &&
@@ -1862,7 +1835,7 @@ class RenderMolecule extends IndexedObject implements ObjectUpdate, NodeComponen
 
 	/*
 	System.out.println("texCoord " + texCoordSetMapLen + " " +
-			   cv.numTexCoordSupported + " " + modeSupportDL);
+			   cv.maxTexCoordSets + " " + modeSupportDL);
 
 	System.out.println("primaryMoleculeType = "+primaryMoleculeType+" primaryRenderAtomList ="+primaryRenderAtomList+" separateDlistRenderAtomList ="+separateDlistRenderAtomList+" vertexArrayRenderAtomList ="+vertexArrayRenderAtomList);
 	*/
@@ -1934,7 +1907,7 @@ class RenderMolecule extends IndexedObject implements ObjectUpdate, NodeComponen
 	    
 	}
 	
-	// TODO: In the case of independent primitives such as quads,
+	// XXXX: In the case of independent primitives such as quads,
 	// it would still be better to call multi draw arrays
 	if (vertexArrayRenderAtomList != null) {
 	    if (pass == TextureBin.USE_DISPLAYLIST) {
@@ -2171,7 +2144,7 @@ class RenderMolecule extends IndexedObject implements ObjectUpdate, NodeComponen
         // instead.
 
 	if ((pass != TextureBin.USE_DISPLAYLIST) ||
-	    (texCoordSetMapLen > cv.numTexCoordSupported) ||
+	    (texCoordSetMapLen > cv.maxTexCoordSets) ||
                        	     (VirtualUniverse.mc.isD3D() &&
 			     (((definingPolygonAttributes != null) &&
 			       ((isQuadGeometryArray &&
@@ -2188,7 +2161,7 @@ class RenderMolecule extends IndexedObject implements ObjectUpdate, NodeComponen
 	//	System.out.println("r.isOpaque = "+isOpaque+" rinfo = "+tinfo.rInfo+" groupType = "+tinfo.rInfo.groupType);
 	// Only support individual dlist or varray
 	// If this rInfo is a part of a bigger dlist, render as VA
-	// TODO: What to do with Text3D, Raster, CG?
+	// XXXX: What to do with Text3D, Raster, CG?
 	if ((tinfo.rInfo.groupType & RenderAtom.SEPARATE_DLIST_PER_RINFO) != 0) {
 	    RenderAtomListInfo save= tinfo.rInfo.next;
 	    // Render only one geometry
@@ -2353,7 +2326,7 @@ class RenderMolecule extends IndexedObject implements ObjectUpdate, NodeComponen
 
     void checkEquivalenceWithLeftNeighbor(RenderMolecule rm, int dirtyBits) {
 	boolean reload_color = reloadColor(rm);
-	// TODO : For now ignore the dirtyBits being sent in
+	// XXXX : For now ignore the dirtyBits being sent in
 	dirtyAttrsAcrossRms = ALL_DIRTY_BITS ;
 	
 	
@@ -2703,6 +2676,30 @@ class RenderMolecule extends IndexedObject implements ObjectUpdate, NodeComponen
 	
     }
 
+    // Issue 129: method to add or remove all rendering atoms in this
+    // RenderMolecule to or from the transparent info list when we are
+    // in depth sorted transparency mode and the RenderMolecule
+    // changes from opaque to transparent or vice versa.
+    void addRemoveTransparentObject(RenderBin renderBin, boolean add) {
+	addRemoveTransparentObject(renderBin, add, primaryRenderAtomList);
+	addRemoveTransparentObject(renderBin, add, separateDlistRenderAtomList);
+	addRemoveTransparentObject(renderBin, add, vertexArrayRenderAtomList);
+    }
+    
+    private void addRemoveTransparentObject(RenderBin renderBin,
+					    boolean add,
+					    RenderAtomListInfo rinfo) {
+	while (rinfo != null) {
+	    if (add) {
+		renderBin.addTransparentObject(rinfo.renderAtom);
+	    }
+	    else {
+		renderBin.removeTransparentObject(rinfo.renderAtom);
+	    }
+	    rinfo = rinfo.next;
+	}
+    }
+
     void evalMaterialCachedState() {
 	if (definingMaterial == null) {
 	    enableLighting = false;;
@@ -3118,7 +3115,3 @@ class RenderMolecule extends IndexedObject implements ObjectUpdate, NodeComponen
 	}
     }
 }
-
-
-
-
diff --git a/src/classes/share/javax/media/j3d/Renderer.java b/src/classes/share/javax/media/j3d/Renderer.java
index 4f52a06..bb61822 100644
--- a/src/classes/share/javax/media/j3d/Renderer.java
+++ b/src/classes/share/javax/media/j3d/Renderer.java
@@ -96,6 +96,8 @@ class Renderer extends J3dThread {
 
     // an unique bit to identify this renderer
     int rendererBit = 0;
+    // an unique number to identify this renderer : ( rendererBit = 1 << rendererId)
+    int rendererId = 0;
 
     // List of renderMolecules that are dirty due to additions
     // or removal of renderAtoms from their display list set
@@ -117,10 +119,6 @@ class Renderer extends J3dThread {
     ArrayList textureReloadList = new ArrayList();
 
 
-    // This is a local copy of canvas view cache. It is used as a data storage for the
-    // renderer. Note: This isn't the "real" canvasViewCache references by the Canvas.
-    CanvasViewCache copyOfCvCache = new CanvasViewCache(null, null, null);
-
     J3dMessage[] renderMessage;
 
     // The screen for this Renderer. Note that this renderer may share
@@ -178,7 +176,8 @@ class Renderer extends J3dThread {
 	setName("J3D-Renderer-" + getInstanceNum());
 
 	type = J3dThread.RENDER_THREAD;
-        rendererBit = VirtualUniverse.mc.getRendererBit();
+	rendererId = VirtualUniverse.mc.getRendererId();
+        rendererBit = (1 << rendererId);
         renderMessage = new J3dMessage[1];
     }
 
@@ -391,7 +390,25 @@ class Renderer extends J3dThread {
 				c.doubleBufferAvailable = nct.hasDoubleBuffer(c);
 				c.stereoAvailable = nct.hasStereo(c);
 			    }
-			    c.sceneAntialiasingMultiSamplesAvailable =
+
+			    // Setup stencil related variables.
+                            c.actualStencilSize = nct.getStencilSize(c);
+                            boolean userOwnsStencil = c.requestedStencilSize > 0;
+                            
+                            c.userStencilAvailable = 
+                                    (userOwnsStencil && (c.actualStencilSize > 0));
+                            c.systemStencilAvailable =
+                                    (!userOwnsStencil && (c.actualStencilSize > 0));
+
+                            /*
+			      System.out.println("Renderer :check for nct configuration");
+			      System.out.println("-- userStencilAvailable " + 
+			      c.userStencilAvailable);
+			      System.out.println("-- systemStencilAvailable " + 
+			      c.systemStencilAvailable);
+			    */
+
+                            c.sceneAntialiasingMultiSamplesAvailable =
 				nct.hasSceneAntialiasingMultisample(c);
 
 			    if (c.sceneAntialiasingMultiSamplesAvailable) {
@@ -667,13 +684,7 @@ class Renderer extends J3dThread {
 			    }
 
 			    synchronized (VirtualUniverse.mc.contextCreationLock) {
-				sharedCtx =
-				    canvas.createNewContext(canvas.screen.display,
-							 canvas.window,
-							 canvas.vid,
-							 canvas.fbConfig,
-							 0, true,
-							 canvas.offScreen);
+				sharedCtx = canvas.createNewContext(0, true);
 				if (sharedCtx == 0) {
 				    canvas.drawingSurfaceObject.unLock();
 				    if ((offBufRetained != null) &&
@@ -706,15 +717,9 @@ class Renderer extends J3dThread {
 			}
 
 			synchronized (VirtualUniverse.mc.contextCreationLock) {
-			    canvas.ctx =
-				canvas.createNewContext(canvas.screen.display, 
-						     canvas.window, canvas.vid,
-						     canvas.fbConfig, sharedCtx,
-						     false, canvas.offScreen);
-
-
+			    canvas.ctx = canvas.createNewContext(sharedCtx, false);
 
-			    if (canvas.ctx == 0) {
+                            if (canvas.ctx == 0) {
 				canvas.drawingSurfaceObject.unLock();			    
 				if ((offBufRetained != null) &&
 				    offBufRetained.isByReference()) {
@@ -737,56 +742,29 @@ class Renderer extends J3dThread {
 				    NodeComponentRetained nc = (NodeComponentRetained)renderBin.nodeComponentList.get(i);
 				    nc.evaluateExtensions(canvas.extensionsSupported);
 				}
-			    }
-		
-
-			    // query for the number of texture units supported
-			    if (canvas.multiTexAccelerated) {
-			        canvas.numTexUnitSupported = 
-					canvas.getTextureUnitCount(canvas.ctx);
-				if (VirtualUniverse.mc.textureUnitMax < canvas.numTexUnitSupported) {
-				    canvas.numTexUnitSupported = VirtualUniverse.mc.textureUnitMax;
-				}
-			    }
+                            }
 
-			    // enable separate specular color
+                            // enable separate specular color
 			    canvas.enableSeparateSpecularColor();
-
 			}
 
+                        // create the cache texture state in canvas
+                        // for state download checking purpose
+                        if (canvas.texUnitState == null) {
+                            canvas.createTexUnitState();
+                        }
 
-			// create the cache texture state in canvas
-			// for state download checking purpose
-
-			if (canvas.texUnitState == null) {
-			    canvas.texUnitState = 
-				new TextureUnitStateRetained[
-					canvas.numTexCoordSupported];
-			    for (int t = 0; t < canvas.numTexCoordSupported;
-					t++) {
-				canvas.texUnitState[t] = 
-					new TextureUnitStateRetained();
-				canvas.texUnitState[t].texture = null;
-				canvas.texUnitState[t].mirror = null;
-			    }
-			}
-
+                        // Create the texture unit state map
+                        if (canvas.texUnitStateMap == null) {
+                            canvas.createTexUnitStateMap();
+                        }
 
-			// also create the texture unit state map
-			// which is a mapping from texture unit state to
-			// the actual underlying texture unit
+                        canvas.resetImmediateRendering(Canvas3D.NOCHANGE);
+                        canvas.drawingSurfaceObject.contextValidated();
 
-        		if (canvas.texUnitStateMap == null) {
-            		    canvas.texUnitStateMap = 
-					new int[canvas.numTexCoordSupported];
-        		}
-			
-			canvas.resetImmediateRendering(Canvas3D.NOCHANGE);
-			canvas.drawingSurfaceObject.contextValidated();
-			
-			if (!canvas.useSharedCtx) {
-			    canvas.needToRebuildDisplayList = true;
-			}
+                        if (!canvas.useSharedCtx) {
+                            canvas.needToRebuildDisplayList = true;
+                        }
 			canvas.drawingSurfaceObject.unLock();			    
             	    } else {
 
@@ -819,30 +797,22 @@ class Renderer extends J3dThread {
 
 			// save the BACKGROUND_IMAGE_DIRTY before canvas.updateViewCache
 			// clean it
-			background_image_update = 
-			    ((canvas.cvDirtyMask & Canvas3D.BACKGROUND_IMAGE_DIRTY) != 0);
-
-			// copyOfcvCache is a copy of canvas view
-		        // cache.  It is used as a data storage for the
-		        // renderer.  Note: This isn't the "real"
-		        // canvasViewCache references by the Canvas.
-		        //
-		        // Note : For performance reason, copyOfcvCache
-		        // doesn't contain are valid canvasViewCache info.,
-		        // only data needed by the renderer are stored.
-		        //
-		        // The valid data are : useStereo, canvasWidth,
-		        // canvasHeight, leftProjection, rightProjection,
-		        // leftVpcToEc, rightVpcToEc, leftFrustumPlanes,
-		        // rightFrustumPlanes, vpcToVworld and vworldToVpc.
+                        synchronized (canvas.dirtyMaskLock) {
+                            background_image_update = 
+                                ((canvas.cvDirtyMask[Canvas3D.RENDERER_DIRTY_IDX] & Canvas3D.BACKGROUND_IMAGE_DIRTY) != 0);
+                        }
 
 			if (VirtualUniverse.mc.doDsiRenderLock) {
 			    canvas.drawingSurfaceObject.unLock();
 			}
 
+                        // Issue 109 : removed copyOfCvCache now that we have
+                        // a separate canvasViewCache for computing view frustum
+                        CanvasViewCache cvCache = canvas.canvasViewCache;
+
 			// Deadlock if we include updateViewCache in
 			// drawingSurfaceObject sync.
-			canvas.updateViewCache(false, copyOfCvCache, null,
+			canvas.updateViewCache(false, null, null,
 					       renderBin.geometryBackground != null);
 
 			if ((VirtualUniverse.mc.doDsiRenderLock) &&
@@ -856,8 +826,8 @@ class Renderer extends J3dThread {
 								
                         // setup viewport
                         canvas.setViewport(canvas.ctx, 0, 0,
-                           copyOfCvCache.getCanvasWidth(),
-                           copyOfCvCache.getCanvasHeight());
+                           cvCache.getCanvasWidth(),
+                           cvCache.getCanvasHeight());
 
 
 
@@ -915,7 +885,7 @@ class Renderer extends J3dThread {
 
 
 		        // stereo setup
-                        boolean useStereo = copyOfCvCache.getUseStereo();
+                        boolean useStereo = cvCache.getUseStereo();
                         if (useStereo) {
                             num_stereo_passes = 2;
                             stereo_mode = Canvas3D.FIELD_LEFT;
@@ -942,7 +912,7 @@ class Renderer extends J3dThread {
 				num_accum_passes = NUM_ACCUMULATION_SAMPLES;
 
 				System.arraycopy(
-						 copyOfCvCache.getLeftProjection().mat,
+						 cvCache.getLeftProjection().mat,
                                 0, accumLeftProjMat, 0, 16);
 
 
@@ -960,7 +930,7 @@ class Renderer extends J3dThread {
 
 				if (useStereo) {
 				    System.arraycopy(
-					copyOfCvCache.getRightProjection().mat,
+					cvCache.getRightProjection().mat,
 					0, accumRightProjMat, 0, 16);
 				    accumRightX = accumRightProjMat[3];
 				    accumRightY = accumRightProjMat[7];
@@ -968,13 +938,13 @@ class Renderer extends J3dThread {
 
 				if (renderBin.geometryBackground != null) {
 				    System.arraycopy(
-					copyOfCvCache.getInfLeftProjection().mat,
+					cvCache.getInfLeftProjection().mat,
 					0, accumInfLeftProjMat, 0, 16);
 				    accumInfLeftX = accumInfLeftProjMat[3];
 				    accumInfLeftY = accumInfLeftProjMat[7];
 				    if (useStereo) {
 					System.arraycopy(
-					    copyOfCvCache.getInfRightProjection().mat,
+					    cvCache.getInfRightProjection().mat,
 					    0, accumInfRightProjMat, 0, 16);
 				        accumInfRightX = accumInfRightProjMat[3];
 				        accumInfRightY = accumInfRightProjMat[7];
@@ -1044,8 +1014,8 @@ class Renderer extends J3dThread {
 			canvas.beginScene();
 
 			// this is if the background image resizes with the canvas
-			int winWidth = copyOfCvCache.getCanvasWidth();
-			int winHeight = copyOfCvCache.getCanvasHeight();
+			int winWidth = cvCache.getCanvasWidth();
+			int winHeight = cvCache.getCanvasHeight();
 
 
 		        // clear background if not full screen antialiasing
@@ -1217,7 +1187,7 @@ class Renderer extends J3dThread {
 				    // setup rendering matrices
 				    if (pass == 0) {
                                         canvas.vpcToEc = 
-					    copyOfCvCache.getInfLeftVpcToEc();
+					    cvCache.getInfLeftVpcToEc();
 	    	                        if (doAccum) {
                                             canvas.setProjectionMatrix(
 						canvas.ctx,
@@ -1225,11 +1195,11 @@ class Renderer extends J3dThread {
 				        } else {
                                             canvas.setProjectionMatrix(
 						canvas.ctx,
-					       	copyOfCvCache.getInfLeftProjection().mat);
+					       	cvCache.getInfLeftProjection().mat);
 				        }
 				    } else {
                                         canvas.vpcToEc = 
-					    copyOfCvCache.getInfRightVpcToEc();
+					    cvCache.getInfRightVpcToEc();
 	    	                        if (doAccum) {
                                             canvas.setProjectionMatrix(
 						canvas.ctx,
@@ -1237,11 +1207,11 @@ class Renderer extends J3dThread {
 				        } else {
                                             canvas.setProjectionMatrix(
 						canvas.ctx,
-					       copyOfCvCache.getInfRightProjection().mat);
+					       cvCache.getInfRightProjection().mat);
 				        }
                                     }
                                     canvas.vworldToEc.mul(canvas.vpcToEc,
-                                        copyOfCvCache.getInfVworldToVpc());
+                                        cvCache.getInfVworldToVpc());
 
 				    // render background geometry
 				    renderBin.renderBackground(canvas);
@@ -1249,33 +1219,33 @@ class Renderer extends J3dThread {
 
 			        // setup rendering matrices
                                 if (pass == 0) {
-                            	    canvas.vpcToEc = copyOfCvCache.getLeftVpcToEc();
+                            	    canvas.vpcToEc = cvCache.getLeftVpcToEc();
 			            if (doAccum) {
                                         canvas.setProjectionMatrix(
 						canvas.ctx, accumLeftProjMat);
                                     } else {
                                         canvas.setProjectionMatrix(canvas.ctx,
-					copyOfCvCache.getLeftProjection().mat);
+					cvCache.getLeftProjection().mat);
 				    }
 			        } else {
-                            	    canvas.vpcToEc = copyOfCvCache.getRightVpcToEc();
+                            	    canvas.vpcToEc = cvCache.getRightVpcToEc();
 			            if (doAccum) {
                                         canvas.setProjectionMatrix(
 						canvas.ctx, accumRightProjMat);
                                     } else {
                                         canvas.setProjectionMatrix(canvas.ctx,
-						copyOfCvCache.getRightProjection().mat);
+						cvCache.getRightProjection().mat);
 				    }
 			        } 
                                 canvas.vworldToEc.mul(canvas.vpcToEc,
-                                        copyOfCvCache.getVworldToVpc());
+                                        cvCache.getVworldToVpc());
 
 
-                                synchronized (copyOfCvCache) {
+                                synchronized (cvCache) {
                                  if (pass == 0) {
-                                     canvas.setFrustumPlanes(copyOfCvCache.getLeftFrustumPlanesInVworld());
+                                     canvas.setFrustumPlanes(cvCache.getLeftFrustumPlanesInVworld());
                                  } else {
-                                     canvas.setFrustumPlanes(copyOfCvCache.getRightFrustumPlanesInVworld());
+                                     canvas.setFrustumPlanes(cvCache.getRightFrustumPlanesInVworld());
                                  }
                                 }
 
@@ -1464,7 +1434,6 @@ class Renderer extends J3dThread {
 	dirtyDlistPerRinfoList.clear();
 	textureIdResourceFreeList.clear();
 	displayListResourceFreeList.clear();
-	copyOfCvCache = new CanvasViewCache(null, null, null);
 	onScreen = null;
 	offScreen = null;
 	m = null;
@@ -1630,12 +1599,15 @@ class Renderer extends J3dThread {
 	    // restore current context
 	    currentCanvas.makeCtxCurrent();
 	}
-	if (texture.equals("2D")){
-	    VirtualUniverse.mc.freeTexture2DId(texId);
-	}
-	else if(texture.equals("3D")){
-	    VirtualUniverse.mc.freeTexture3DId(texId);
-	}
+        // Issue 162: TEMPORARY FIX -- don't free the texture ID, since it will
+        // be freed once per canvas / screen and will subsequently cause the ID
+        // to be used for multiple textures.
+//	if (texture.equals("2D")){
+//	    VirtualUniverse.mc.freeTexture2DId(texId);
+//	}
+//	else if(texture.equals("3D")){
+//	    VirtualUniverse.mc.freeTexture3DId(texId);
+//	}
     }
 
 
diff --git a/src/classes/share/javax/media/j3d/RenderingAttributes.java b/src/classes/share/javax/media/j3d/RenderingAttributes.java
index d3cb2be..d4c444a 100644
--- a/src/classes/share/javax/media/j3d/RenderingAttributes.java
+++ b/src/classes/share/javax/media/j3d/RenderingAttributes.java
@@ -16,38 +16,72 @@ package javax.media.j3d;
  * The RenderingAttributes object defines common rendering attributes
  * for all primitive types. The rendering attributes are:<p>
  * <ul>
- * <li>Alpha test function - used to compare the alpha test value with
- * each per-pixel alpha value. If the test passes, the pixel is
+ * <li>Depth test function - used to compare the incoming (source) depth of
+ * each pixel with depth of the pixel stored in frame buffer. If the test
+ * passes, the pixel is written, otherwise the pixel is not written. The depth test
+ * function is set with the <code>setDepthTestFunction</code>
+ * method. By default, LESS_OR_EQUAL is the function used. The depth test
+ * function is one of the following:</li><p>
+ * <ul>
+ * <li>ALWAYS - pixels are always drawn, irrespective of the depth
+ * value. This effectively disables depth testing.</li><p>
+ *
+ * <li>NEVER - pixels are never drawn, irrespective of the depth
+ * value.</li><p>
+ *
+ * <li>EQUAL - pixels are drawn if the incoming pixel depth is equal
+ * to the stored pixel depth in the frame buffer.</li><p>
+ *
+ * <li>NOT_EQUAL - pixels are drawn if the incoming pixel depth is
+ * not equal to the stored pixel depth in the frame buffer.</li><p>
+ * 
+ * <li>LESS - pixels are drawn if the incoming pixel depth is less
+ * than the stored pixel depth in the frame buffer.</li><p>
+ * 
+ * <li>LESS_OR_EQUAL - pixels are drawn if the incoming pixel depth
+ * is less than or equal to the stored pixel depth in the frame buffer.
+ * This is the default setting.</li><p>
+ * 
+ * <li>GREATER - pixels are drawn if the incoming pixel depth is greater
+ * than the stored pixel depth in the frame buffer.</li><p>
+ * 
+ * <li>GREATER_OR_EQUAL - pixels are drawn if the incoming pixel depth
+ * is greater than or equal to the stored pixel depth in the frame buffer.</li><p>
+ * </ul>
+ *
+ * <li>Alpha test function - used to compare the incoming (source) alpha value
+ * of each pixel with the alpha test value. If the test passes, the pixel is
  * written, otherwise the pixel is not written. The alpha test
  * function is set with the <code>setAlphaTestFunction</code>
  * method. The alpha test
  * function is one of the following:</li><p>
  * <ul>
  * <li>ALWAYS - pixels are always drawn, irrespective of the alpha
- * value. This effectively disables alpha testing. This is
- * the default setting.</li><p>
+ * value. This effectively disables alpha testing.
+ * This is the default setting.</li><p>
  *
  * <li>NEVER - pixels are never drawn, irrespective of the alpha
  * value.</li><p>
  *
- * <li>EQUAL - pixels are drawn if the pixel alpha value is equal
+ * <li>EQUAL - pixels are drawn if the incoming pixel alpha value is equal
  * to the alpha test value.</li><p>
  *
- * <li>NOT_EQUAL - pixels are drawn if the pixel alpha value is
+ * <li>NOT_EQUAL - pixels are drawn if the incoming pixel alpha value is
  * not equal to the alpha test value.</li><p>
  * 
- * <li>LESS - pixels are drawn if the pixel alpha value is less
+ * <li>LESS - pixels are drawn if the incoming pixel alpha value is less
  * than the alpha test value.</li><p>
  * 
- * <li>LESS_OR_EQUAL - pixels are drawn if the pixel alpha value
+ * <li>LESS_OR_EQUAL - pixels are drawn if the incoming pixel alpha value
  * is less than or equal to the alpha test value.</li><p>
  * 
- * <li>GREATER - pixels are drawn if the pixel alpha value is greater
+ * <li>GREATER - pixels are drawn if the incoming pixel alpha value is greater
  * than the alpha test value.</li><p>
  * 
- * <li>GREATER_OR_EQUAL - pixels are drawn if the pixel alpha
+ * <li>GREATER_OR_EQUAL - pixels are drawn if the incoming pixel alpha
  * value is greater than or equal to the alpha test value.</li><p>
  * </ul>
+ *
  * <li>Alpha test value - the test value used by the alpha test function.
  * This value is compared to the alpha value of each rendered pixel.
  * The alpha test value is set with the <code>setAlphaTestValue</code>
@@ -59,8 +93,22 @@ package javax.media.j3d;
  * is enabled or disabled with the <code>setRasterOpEnable</code>
  * method. The raster operation is one of the following:</li><p>
  * <ul>
+ * <li>ROP_CLEAR - DST = 0.</li>
+ * <li>ROP_AND DST = SRC & DST.</li>
+ * <li>ROP_AND_REVERSE DST = SRC & ~DST.</li>
  * <li>ROP_COPY - DST = SRC. This is the default operation.</li>
- * <li>ROP_XOR - DST = SRC ^ DST.</li><p>
+ * <li>ROP_AND_INVERTED - DST = ~SRC & DST.</li>
+ * <li>ROP_NOOP - DST = DST.</li>
+ * <li>ROP_XOR - DST = SRC ^ DST.</li>
+ * <li>ROP_OR - DST = DST | SRC.</li>
+ * <li>ROP_NOR - DST = ~( DST | SRC .)</li>
+ * <li>ROP_EQUIV - DST = ~( DST ^ SRC .)</li>
+ * <li>ROP_INVERT - DST = ~DST.</li>
+ * <li>ROP_OR_REVERSE - DST = src | ~DST.</li>
+ * <li>ROP_COPY_INVERTED - DST = ~SRC.</li>
+ * <li>ROP_OR_INVERTED - DST = ~SRC | DST.</li>
+ * <li>ROP_NAND - DST = ~(SRC & DST.)</li>
+ * <li>ROP_SET - DST = 1.</li><p>
  * </ul>
  * <li>Vertex colors - vertex colors can be ignored for this
  * RenderingAttributes object. This capability is set with the
@@ -93,12 +141,79 @@ package javax.media.j3d;
  * include opaque objects or primitives rendered with
  * SCREEN_DOOR transparency. By default, the depth buffer
  * is enabled and the depth buffer write is enabled.</li><p>
+ *
+ * <li>Stencil buffer - can be enabled or disabled for this RenderingAttributes
+ * component object using the <code>setStencilEnable</code> method. If the
+ * stencil buffer is disabled, the stencil operation and function are ignored.
+ * If a scene graph is rendered on a Canvas3D that does not have a stencil
+ * buffer, the stencil buffer will be implicitly disabled for that
+ * canvas.</li><p>
+ *
+ * <li>Stencil write mask - mask that controls which bits of the stencil
+ * buffer are written when the stencil buffer is enabled. The default value is
+ * <code>~0</code> (all ones).</li><p>
+ *
+ * <li>Stencil operation - a set of three stencil operations performed
+ * when: 1)&nbsp;the stencil test fails; 2)&nbsp;the stencil test passes, but
+ * the depth test fails; or 3)&nbsp;both the stencil test and depth test pass.
+ * The stencil operations are set with the <code>setStencilOp</code>
+ * method. The stencil operation is one of the following:</li><p>
+ * <ul>
+ * <li>STENCIL_KEEP - keeps the current value (no operation performed).
+ * This is the default setting.</li>
+ * <li>STENCIL_ZERO - Sets the stencil buffer value to 0.</li>
+ * <li>STENCIL_REPLACE - Sets the stencil buffer value to
+ * <code>refValue</code>, as specified by <code>setStencilFunction</code>.</li>
+ * <li>STENCIL_INCR - Increments the current stencil buffer value.</li>
+ * <li>STENCIL_DECR - Decrements the current stencil buffer value.</li>
+ * <li>STENCIL_INVERT - Bitwise inverts the current stencil buffer value.</li><p>
  * </ul>
  *
- * @see Appearance
+ * <li>Stencil test function - used to compare the stencil reference value with
+ * the per-pixel stencil value stored in the frame buffer. If the test passes,
+ * the pixel is written, otherwise the pixel is not written. The stencil
+ * test function, reference value, and comparison mask are set with the
+ * <code>setStencilFunction</code> method. The stencil comparison mask is
+ * bitwise-ANDed with both the stencil reference value and the stored stencil
+ * value prior to doing the comparison. The default value for the reference value
+ * is 0. The default value for the comparison mask is <code>~0</code> (all ones).
+ * The stencil test function is one of the following:</li><p>
+ * <ul>
+ * <li>ALWAYS - pixels are always drawn, irrespective of the stencil
+ * value. This effectively disables stencil testing.
+ * This is the default setting.</li><p>
+ *
+ * <li>NEVER - pixels are never drawn, irrespective of the stencil
+ * value.</li><p>
+ *
+ * <li>EQUAL - pixels are drawn if the stencil reference value is equal
+ * to the stored stencil value in the frame buffer.</li><p>
+ *
+ * <li>NOT_EQUAL - pixels are drawn if the stencil reference value is
+ * not equal to the stored stencil value in the frame buffer.</li><p>
+ * 
+ * <li>LESS - pixels are drawn if the stencil reference value is less
+ * than the stored stencil value in the frame buffer.</li><p>
  * 
+ * <li>LESS_OR_EQUAL - pixels are drawn if the stencil reference value
+ * is less than or equal to the stored stencil value in the frame buffer.</li><p>
+ * 
+ * <li>GREATER - pixels are drawn if the stencil reference value is greater
+ * than the stored stencil value in the frame buffer.</li><p>
+ * 
+ * <li>GREATER_OR_EQUAL - pixels are drawn if the stencil reference value
+ * is greater than or equal to the stored stencil value in the frame buffer.</li><p>
+ * </ul>
+ *
+ * </ul>
+ *
+ * <p>Note: the alpha test, depth test, and stencil functions all use
+ * the same enums.</p>
+ *
+ * @see Appearance
  */
 public class RenderingAttributes extends NodeComponent {
+
     /**
      * Specifies that this RenderingAttributes object
      * allows reading its alpha test value component information.
@@ -127,6 +242,24 @@ public class RenderingAttributes extends NodeComponent {
     public static final int
     ALLOW_ALPHA_TEST_FUNCTION_WRITE = CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_ALPHA_TEST_FUNCTION_WRITE;
 
+     /**
+     * Specifies that this RenderingAttributes object
+     * allows reading its depth test function component information.
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int
+    ALLOW_DEPTH_TEST_FUNCTION_READ = CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_DEPTH_TEST_FUNCTION_READ;
+
+    /**
+     * Specifies that this RenderingAttributes object
+     * allows writing its depth test function component information.
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int
+    ALLOW_DEPTH_TEST_FUNCTION_WRITE = CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_DEPTH_TEST_FUNCTION_WRITE;
+
     /**
      * Specifies that this RenderingAttributes object
      * allows reading its depth buffer enable and depth buffer write enable
@@ -200,84 +333,322 @@ public class RenderingAttributes extends NodeComponent {
     CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_RASTER_OP_WRITE;
 
     /**
-     * Indicates pixels are always drawn irrespective of alpha value.
-     * This effectively disables alpha testing.
+     * Specifies that this RenderingAttributes object allows reading
+     * its stencil enable, stencil op, stencil function, and
+     * stencil write mask information.
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int ALLOW_STENCIL_ATTRIBUTES_READ =
+    CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_STENCIL_ATTRIBUTES_READ;
+
+    /**
+     * Specifies that this RenderingAttributes object allows writing
+     * its stencil enable, stencil op, stencil function, and
+     * stencil write mask information.
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int ALLOW_STENCIL_ATTRIBUTES_WRITE =
+    CapabilityBits.RENDERING_ATTRIBUTES_ALLOW_STENCIL_ATTRIBUTES_WRITE;
+
+
+    //
+    // Enums for alpha test, depth test, and stencil test
+    //
+
+    /**
+     * Specifies that pixels are always drawn irrespective of the
+     * values being tested.
+     * Can be used to specify the alpha test function, the depth test function,
+     * or the stencil function.
+     * This setting effectively disables alpha, depth, or stencil testing.
+     *
+     * @see #setAlphaTestFunction
+     * @see #setDepthTestFunction
+     * @see #setStencilFunction(int,int,int)
      */
     public static final int ALWAYS = 0;
 
     /**
-     * Indicates pixels are never drawn irrespective of alpha value.
+     * Specifies that pixels are never drawn irrespective of the
+     * values being tested.
+     * Can be used to specify the alpha test function, the depth test function,
+     * or the stencil function.
+     *
+     * @see #setAlphaTestFunction
+     * @see #setDepthTestFunction
+     * @see #setStencilFunction(int,int,int)
      */
     public static final int NEVER = 1;
 
     /**
-     * Indicates pixels are  drawn if pixel alpha value is equal 
-     * to alpha test value.
+     * Specifies that pixels are drawn if the two values being tested are equal.
+     * Can be used to specify the alpha test function, the depth test function,
+     * or the stencil function.
+     *
+     * @see #setAlphaTestFunction
+     * @see #setDepthTestFunction
+     * @see #setStencilFunction(int,int,int)
      */
     public static final int EQUAL = 2;
 
     /**
-     * Indicates pixels are  drawn if pixel alpha value is not equal
-     * to alpha test value.
+     * Specifies that pixels are drawn if the two values being tested are not equal.
+     * Can be used to specify the alpha test function, the depth test function,
+     * or the stencil function.
+     *
+     * @see #setAlphaTestFunction
+     * @see #setDepthTestFunction
+     * @see #setStencilFunction(int,int,int)
      */
     public static final int NOT_EQUAL = 3;
 
     /**
-     * Indicates pixels are  drawn if pixel alpha value is less 
-     * than alpha test value.
+     * Specifies that pixels are drawn if the source/reference value is less 
+     * than the destination/test value.
+     * Can be used to specify the alpha test function, the depth test function,
+     * or the stencil function.
+     *
+     * @see #setAlphaTestFunction
+     * @see #setDepthTestFunction
+     * @see #setStencilFunction(int,int,int)
      */
     public static final int LESS = 4;
 
     /**
-     * Indicates pixels are  drawn if pixel alpha value is less
-     * than or equal to alpha test value.
+     * Specifies that pixels are drawn if the source/reference value is less 
+     * than or equal to the destination/test value.
+     * Can be used to specify the alpha test function, the depth test function,
+     * or the stencil function.
+     *
+     * @see #setAlphaTestFunction
+     * @see #setDepthTestFunction
+     * @see #setStencilFunction(int,int,int)
      */
     public static final int LESS_OR_EQUAL = 5;
 
     /**
-     * Indicates pixels are  drawn if pixel alpha value is greater
-     * than alpha test value.
+     * Specifies that pixels are drawn if the source/reference value is greater 
+     * than the destination/test value.
+     * Can be used to specify the alpha test function, the depth test function,
+     * or the stencil function.
+     *
+     * @see #setAlphaTestFunction
+     * @see #setDepthTestFunction
+     * @see #setStencilFunction(int,int,int)
      */
     public static final int GREATER = 6;
 
     /**
-     * Indicates pixels are  drawn if pixel alpha value is greater
-     * than or equal to alpha test value.
+     * Specifies that pixels are drawn if the source/reference value is greater 
+     * than or equal to the destination/test value.
+     * Can be used to specify the alpha test function, the depth test function,
+     * or the stencil function.
+     *
+     * @see #setAlphaTestFunction
+     * @see #setDepthTestFunction
+     * @see #setStencilFunction(int,int,int)
      */
     public static final int GREATER_OR_EQUAL = 7;
 
 
-//    public static final int ROP_CLEAR = 0x0;
-//    public static final int ROP_AND = 0x1;
-//    public static final int ROP_AND_REVERSE = 0x2;
+    //
+    // Raster op enums
+    //
+
+    /**
+     * Raster operation: <code>DST = 0</code>.
+     * @see #setRasterOp
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int ROP_CLEAR = 0x0;
+
+    /**
+     * Raster operation: <code>DST = SRC & DST</code>.
+     * @see #setRasterOp
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int ROP_AND = 0x1;
+
+    /**
+     * Raster operation: <code>DST = SRC & ~DST</code>.
+     * @see #setRasterOp
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int ROP_AND_REVERSE = 0x2;
 
     /**
      * Raster operation: <code>DST = SRC</code>.
      * @see #setRasterOp
+     *
      * @since Java 3D 1.2
      */
     public static final int ROP_COPY = 0x3;
 
-//    public static final int ROP_AND_INVERTED = 0x4;
-//    public static final int ROP_NOOP = 0x5;
+    /**
+     * Raster operation: <code>DST = ~SRC & DST</code>.
+     * @see #setRasterOp
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int ROP_AND_INVERTED = 0x4;
+		
+    /**
+     * Raster operation: <code>DST = DST</code>.
+     * @see #setRasterOp
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int ROP_NOOP = 0x5;
 
     /**
      * Raster operation: <code>DST = SRC ^ DST</code>.
      * @see #setRasterOp
+     *
      * @since Java 3D 1.2
      */
     public static final int ROP_XOR = 0x6;
 
-//    public static final int ROP_OR = 0x7;
-//    public static final int ROP_NOR = 0x8;
-//    public static final int ROP_EQUIV = 0x9;
-//    public static final int ROP_INVERT = 0xA;
-//    public static final int ROP_OR_REVERSE = 0xB;
-//    public static final int ROP_COPY_INVERTED = 0xC;
-//    public static final int ROP_OR_INVERTED = 0xD;
-//    public static final int ROP_NAND = 0xE;
-//    public static final int ROP_SET = 0xF;
+    /**
+     * Raster operation: <code>DST = DST | SRC</code>.
+     * @see #setRasterOp
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int ROP_OR = 0x7;
+
+    /**
+     * Raster operation: <code>DST = ~( DST | SRC )</code>.
+     * @see #setRasterOp
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int ROP_NOR = 0x8;
+
+    /**
+     * Raster operation: <code>DST = ~( DST ^ SRC )</code>.
+     * @see #setRasterOp
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int ROP_EQUIV = 0x9;
+		
+    /**
+     * Raster operation: <code>DST = ~DST</code>.
+     * @see #setRasterOp
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int ROP_INVERT = 0xA;
+		
+    /**
+     * Raster operation: <code>DST = src | ~DST</code>.
+     * @see #setRasterOp
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int ROP_OR_REVERSE = 0xB;
 
+    /**
+     * Raster operation: <code>DST = ~SRC</code>.
+     * @see #setRasterOp
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int ROP_COPY_INVERTED = 0xC;
+
+    /**
+     * Raster operation: <code>DST = ~SRC | DST</code>.
+     * @see #setRasterOp
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int ROP_OR_INVERTED = 0xD;
+
+    /**
+     * Raster operation: <code>DST = ~(SRC & DST)</code>.
+     * @see #setRasterOp
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int ROP_NAND = 0xE;
+
+    /**
+     * Raster operation: <code>DST = 1</code>.
+     * @see #setRasterOp
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int ROP_SET = 0xF;
+
+
+    //
+    // Stencil op enums
+    //
+
+    /**
+     * Stencil operation: <code>DST = DST</code>
+     * @see #setStencilOp(int,int,int)
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int STENCIL_KEEP = 1;
+
+    /**
+     * Stencil operation: <code>DST = 0</code>
+     * @see #setStencilOp(int,int,int)
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int STENCIL_ZERO = 2;
+
+    /**
+     * Stencil operation: <code>DST = REF</code>
+     * @see #setStencilOp(int,int,int)
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int STENCIL_REPLACE = 3;
+
+    /**
+     * Stencil operation: <code>DST = DST + 1</code>
+     * @see #setStencilOp(int,int,int)
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int STENCIL_INCR = 4;
+
+    /**
+     * Stencil operation: <code>DST = DST - 1</code>
+     * @see #setStencilOp(int,int,int)
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int STENCIL_DECR = 5;
+
+    /**
+     * Stencil operation: <code>DST = ~DST</code>
+     * @see #setStencilOp(int,int,int)
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int STENCIL_INVERT = 6;
+
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_ALPHA_TEST_FUNCTION_READ,
+        ALLOW_ALPHA_TEST_VALUE_READ,
+        ALLOW_DEPTH_ENABLE_READ,
+        ALLOW_DEPTH_TEST_FUNCTION_READ,
+        ALLOW_IGNORE_VERTEX_COLORS_READ,        
+        ALLOW_RASTER_OP_READ,
+        ALLOW_STENCIL_ATTRIBUTES_READ,
+        ALLOW_VISIBLE_READ
+    };
 
     /**
      * Constructs a RenderingAttributes object with default parameters.
@@ -286,15 +657,26 @@ public class RenderingAttributes extends NodeComponent {
      * depth buffer enable : true<br>
      * depth buffer write enable : true<br>
      * alpha test function : ALWAYS<br>
-     * alpha test value : 0.0<br>
+     * alpha test value : 0.0f<br>
      * visible : true<br>
      * ignore vertex colors : false<br>
      * raster operation enable : false<br>
      * raster operation : ROP_COPY<br>
+     * depth test: LESS_OR_EQUAL<br>
+     * stencil enable : false<br>
+     * stencil write mask : ~0 (all ones)<br>
+     * stencil op - failOp : STENCIL_KEEP<br>
+     * stencil op - zFailOp : STENCIL_KEEP<br>
+     * stencil op - zPassOp : STENCIL_KEEP<br>
+     * stencil function : ALWAYS<br>
+     * stencil reference value : 0<br>
+     * stencil comparison mask : ~0 (all ones)
      * </ul>
      */
     public RenderingAttributes() {
 	// Just use default attributes
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -327,8 +709,10 @@ public class RenderingAttributes extends NodeComponent {
      * @param rasterOpEnable a flag that specifies whether logical
      * raster operations are enabled for this RenderingAttributes object.
      * This disables all alpha blending operations.
-     * @param rasterOp the logical raster operation, one of ROP_COPY or
-     * ROP_XOR.
+     * @param rasterOp the logical raster operation, one of:
+     * ROP_CLEAR, ROP_AND, ROP_AND_REVERSE, ROP_COPY, ROP_AND_INVERTED,
+     * ROP_NOOP, ROP_XOR, ROP_OR, ROP_NOR, ROP_EQUIV, ROP_INVERT,
+     * ROP_OR_REVERSE, ROP_COPY_INVERTED, ROP_OR_INVERTED, ROP_NAND or ROP_SET
      *
      * @since Java 3D 1.2
      */
@@ -340,6 +724,8 @@ public class RenderingAttributes extends NodeComponent {
 			       boolean ignoreVertexColors,
 			       boolean rasterOpEnable,
 			       int rasterOp) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
 	
 	((RenderingAttributesRetained)this.retained).initDepthBufferEnable(depthBufferEnable);
 	((RenderingAttributesRetained)this.retained).initDepthBufferWriteEnable(depthBufferWriteEnable);
@@ -356,9 +742,13 @@ public class RenderingAttributes extends NodeComponent {
     /**
      * Enables or disables depth buffer mode for this RenderingAttributes
      * component object.
+     *
      * @param state true or false to enable or disable depth buffer mode
+     *
      * @exception CapabilityNotSetException if appropriate capability is 
      * not set and this object is part of live or compiled scene graph
+     *
+     * @see GraphicsConfigTemplate3D#setDepthSize
      */
     public void setDepthBufferEnable(boolean state){
 	if (isLiveOrCompiled())
@@ -401,7 +791,6 @@ public class RenderingAttributes extends NodeComponent {
 	if (isLiveOrCompiled())
 	    if (!this.getCapability(ALLOW_DEPTH_ENABLE_WRITE))
 		throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes2"));
-
 	if (isLive())
 	    ((RenderingAttributesRetained)this.retained).setDepthBufferWriteEnable(state);
 	else
@@ -456,9 +845,9 @@ public class RenderingAttributes extends NodeComponent {
     }
 
     /**
-     * Set alpha test function.  This function is used to compare the
-     * alpha test value with each per-pixel alpha value.  If the test
-     * passes, the pixel is written otherwise the pixel is not
+     * Set alpha test function. This function is used to compare
+     * each incoming (source) per-pixel alpha value with the alpha test value.
+     * If the test passes, the pixel is written otherwise the pixel is not
      * written.
      * @param function the new alpha test function.  One of
      * ALWAYS, NEVER, EQUAL, NOT_EQUAL, LESS, LESS_OR_EQUAL, GREATER,
@@ -639,8 +1028,10 @@ public class RenderingAttributes extends NodeComponent {
      * Sets the raster operation function for this RenderingAttributes
      * component object.
      *
-     * @param rasterOp the logical raster operation, one of ROP_COPY or
-     * ROP_XOR
+     * @param rasterOp the logical raster operation, one of:
+     * ROP_CLEAR, ROP_AND, ROP_AND_REVERSE, ROP_COPY, ROP_AND_INVERTED,
+     * ROP_NOOP, ROP_XOR, ROP_OR, ROP_NOR, ROP_EQUIV, ROP_INVERT,
+     * ROP_OR_REVERSE, ROP_COPY_INVERTED, ROP_OR_INVERTED, ROP_NAND or ROP_SET.
      * @exception CapabilityNotSetException if appropriate capability is 
      * not set and this object is part of live or compiled scene graph
      *
@@ -660,7 +1051,10 @@ public class RenderingAttributes extends NodeComponent {
     /**
      * Retrieves the current raster operation for this RenderingAttributes
      * object.
-     * @return one of ROP_COPY or ROP_XOR.
+     * @return one of:
+     * ROP_CLEAR, ROP_AND, ROP_AND_REVERSE, ROP_COPY, ROP_AND_INVERTED,
+     * ROP_NOOP, ROP_XOR, ROP_OR, ROP_NOR, ROP_EQUIV, ROP_INVERT,
+     * ROP_OR_REVERSE, ROP_COPY_INVERTED, ROP_OR_INVERTED, ROP_NAND or ROP_SET
      * @exception CapabilityNotSetException if appropriate capability is 
      * not set and this object is part of live or compiled scene graph
      *
@@ -723,13 +1117,368 @@ public class RenderingAttributes extends NodeComponent {
 
 	rt.initDepthBufferEnable(attr.getDepthBufferEnable());
 	rt.initDepthBufferWriteEnable(attr.getDepthBufferWriteEnable());
+	rt.initDepthTestFunction(attr.getDepthTestFunction());
 	rt.initAlphaTestValue(attr.getAlphaTestValue());
 	rt.initAlphaTestFunction(attr.getAlphaTestFunction());
 	rt.initVisible(attr.getVisible());
 	rt.initIgnoreVertexColors(attr.getIgnoreVertexColors());
 	rt.initRasterOpEnable(attr.getRasterOpEnable());
 	rt.initRasterOp(attr.getRasterOp());
+	rt.initStencilEnable(attr.getStencilEnable());
+	int[] ops = new int[3];
+	attr.getStencilOp(ops);
+	rt.initStencilOp(ops[0], ops[1], ops[2]);
+	attr.getStencilFunction(ops);
+	rt.initStencilFunction(ops[0], ops[1], ops[2]);
+	rt.initStencilWriteMask(attr.getStencilWriteMask());
+
+    }
+
+    /**
+     * Set depth test function.  This function is used to compare each
+     * incoming (source) per-pixel depth test value with the stored per-pixel
+     * depth value in the frame buffer.  If the test
+     * passes, the pixel is written, otherwise the pixel is not
+     * written.
+     * @param function the new depth test function.  One of
+     * ALWAYS, NEVER, EQUAL, NOT_EQUAL, LESS, LESS_OR_EQUAL, GREATER,
+     * or GREATER_OR_EQUAL.
+     * The default value is LESS_OR_EQUAL.
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @since Java 3D 1.4
+     */
+    public void setDepthTestFunction(int function){
+	if (isLiveOrCompiled())
+	    if (!this.getCapability(ALLOW_DEPTH_TEST_FUNCTION_WRITE))
+		throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes14"));
 
+       if (isLive())
+	    ((RenderingAttributesRetained)this.retained).setDepthTestFunction(function);
+	else
+	    ((RenderingAttributesRetained)this.retained).initDepthTestFunction(function);
+    }
+
+    /**
+     * Retrieves current depth test function.
+     * @return the current depth test function
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @since Java 3D 1.4
+     */
+    public int getDepthTestFunction(){
+	if (isLiveOrCompiled())
+	    if (!this.getCapability(ALLOW_DEPTH_TEST_FUNCTION_READ))
+		throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes15"));
+
+	return ((RenderingAttributesRetained)this.retained).getDepthTestFunction();
+    }
+
+    /**
+     * Enables or disables the stencil buffer for this RenderingAttributes
+     * component object. If the stencil buffer is disabled, the
+     * stencil operation and function are ignored.  If a scene graph
+     * is rendered on a Canvas3D that does not have a stencil buffer,
+     * the stencil buffer will be implicitly disabled for that canvas.
+     *
+     * @param state true or false to enable or disable stencil buffer
+     * operations.
+     * If this is set to false, the stencilOp and stencilFunction parameters
+     * are not used.
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @see GraphicsConfigTemplate3D#setStencilSize
+     *
+     * @since Java 3D 1.4
+     */
+    public void setStencilEnable(boolean state) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes16"));
+            }
+        }
+
+	if (isLive())
+	    ((RenderingAttributesRetained)this.retained).setStencilEnable(state);
+	else
+	    ((RenderingAttributesRetained)this.retained).initStencilEnable(state);
+
+    }
+
+    /**
+     * Retrieves the stencil buffer enable flag for this RenderingAttributes
+     * object.
+     *
+     * @return true if stencil buffer operations are enabled; false
+     * if stencil buffer operations are disabled.
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @since Java 3D 1.4
+     */
+    public boolean getStencilEnable() {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes17"));
+            }
+        }
+
+	return ((RenderingAttributesRetained)this.retained).getStencilEnable();
+    }
+
+    /**
+     * Sets the stencil operations for this RenderingAttributes object to the
+     * specified parameters.
+     *
+     * @param failOp operation performed when the stencil test fails, one of:
+     * STENCIL_KEEP, STENCIL_ZERO, STENCIL_REPLACE, STENCIL_INCR, STENCIL_DECR,
+     * or STENCIL_INVERT.
+     *
+     * @param zFailOp operation performed when the stencil test passes and the
+     * depth test fails, one of:
+     * STENCIL_KEEP, STENCIL_ZERO, STENCIL_REPLACE, STENCIL_INCR, STENCIL_DECR,
+     * or STENCIL_INVERT.
+     *
+     * @param zPassOp operation performed when both the stencil test and the
+     * depth test pass, one of:
+     * STENCIL_KEEP, STENCIL_ZERO, STENCIL_REPLACE, STENCIL_INCR, STENCIL_DECR,
+     * or STENCIL_INVERT.
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @since Java 3D 1.4
+     */
+    public void setStencilOp(int failOp, int zFailOp, int zPassOp) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes16"));
+            }
+        }
+
+	if (isLive())
+	    ((RenderingAttributesRetained)this.retained).setStencilOp(failOp, 
+								      zFailOp, 
+								      zPassOp);
+	else
+	    ((RenderingAttributesRetained)this.retained).initStencilOp(failOp, 
+								       zFailOp, 
+								       zPassOp);
+
+    }
+
+    /**
+     * Sets the stencil operations for this RenderingAttributes object to the
+     * specified parameters.
+     *
+     * @param stencilOps an array of three integers that specifies the new
+     * set of stencil operations. Element 0 of the array specifies the
+     * <code>failOp</code> parameter, element 1 specifies the
+     * <code>zFailOp</code> parameter, and element 2 specifies the
+     * <code>zPassOp</code> parameter.
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @see #setStencilOp(int,int,int)
+     *
+     * @since Java 3D 1.4
+     */
+    public void setStencilOp(int[] stencilOps) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes16"));
+            }
+        }
+
+	if (isLive())
+	    ((RenderingAttributesRetained)this.retained).setStencilOp(stencilOps[0], 
+								      stencilOps[1],
+								      stencilOps[2]);
+	else
+	    ((RenderingAttributesRetained)this.retained).initStencilOp(stencilOps[0], 
+								       stencilOps[1],
+								       stencilOps[2]);
+    }
+
+    /**
+     * Retrieves the current set of stencil operations, and copies them
+     * into the specified array. The caller must ensure that this array
+     * has been allocated with enough space to hold the results.
+     *
+     * @param stencilOps array that will receive the current set of
+     * three stencil operations. The <code>failOp</code> parameter is copied
+     * into element 0 of the array, the <code>zFailOp</code> parameter is copied
+     * into element 1, and the <code>zPassOp</code> parameter is copied
+     * into element 2.
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @since Java 3D 1.4
+     */
+    public void getStencilOp(int[] stencilOps) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes17"));
+            }
+        }
+	
+	((RenderingAttributesRetained)this.retained).getStencilOp(stencilOps);
+    }
+
+    /**
+     * Sets the stencil function, reference value, and comparison mask
+     * for this RenderingAttributes object to the specified parameters.
+     *
+     * @param function the stencil test function, used to compare the
+     * stencil reference value with the stored per-pixel
+     * stencil value in the frame buffer.  If the test
+     * passes, the pixel is written, otherwise the pixel is not
+     * written. The stencil function is one of:
+     * ALWAYS, NEVER, EQUAL, NOT_EQUAL, LESS, LESS_OR_EQUAL, GREATER,
+     * or GREATER_OR_EQUAL.
+     *
+     * @param refValue the stencil reference value that is tested against
+     * the stored per-pixel stencil value
+     *
+     * @param compareMask a mask that limits which bits are compared; it is
+     * bitwise-ANDed with both the stencil reference value and the stored
+     * per-pixel stencil value before doing the comparison.
+     *
+     * @exception CapabilityNotSetException if appropriate capability is
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @since Java 3D 1.4
+     */
+    public void setStencilFunction(int function, int refValue, int compareMask) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes16"));
+            }
+        }
+	if (isLive())
+	    ((RenderingAttributesRetained)this.retained).setStencilFunction(function, 
+									    refValue,
+									    compareMask);
+	else
+	    ((RenderingAttributesRetained)this.retained).initStencilFunction(function, 
+									     refValue,
+									     compareMask);
+    }
+
+    /**
+     * Sets the stencil function, reference value, and comparison mask
+     * for this RenderingAttributes object to the specified parameters.
+     *
+     * @param params an array of three integers that specifies the new
+     * stencil function, reference value, and comparison mask.
+     * Element 0 of the array specifies the
+     * stencil function, element 1 specifies the
+     * reference value, and element 2 specifies the
+     * comparison mask.
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @see #setStencilFunction(int,int,int)
+     *
+     * @since Java 3D 1.4
+     */
+    public void setStencilFunction(int[] params) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes16"));
+            }
+        }
+
+	if (isLive())
+	    ((RenderingAttributesRetained)this.retained).setStencilFunction(params[0], 
+									    params[1],
+									    params[2]);
+	else
+	    ((RenderingAttributesRetained)this.retained).initStencilFunction(params[0], 
+									     params[1],
+									     params[2]);
+
+    }
+
+    /**
+     * Retrieves the stencil function, reference value, and comparison mask,
+     * and copies them into the specified array. The caller must ensure
+     * that this array has been allocated with enough space to hold the results.
+     *
+     * @param params array that will receive the current stencil function,
+     * reference value, and comparison mask. The stencil function is copied
+     * into element 0 of the array, the reference value is copied
+     * into element 1, and the comparison mask is copied
+     * into element 2.
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @since Java 3D 1.4
+     */
+    public void getStencilFunction(int[] params) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes17"));
+            }
+        }
+
+	((RenderingAttributesRetained)this.retained).getStencilFunction(params);
+    }
+
+    /**
+     * Sets the stencil write mask for this RenderingAttributes
+     * object. This mask controls which bits of the
+     * stencil buffer are written.
+     * The default value is <code>~0</code> (all ones).
+     *
+     * @param mask the new stencil write mask.
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @since Java 3D 1.4
+     */
+    public void setStencilWriteMask(int mask) {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_WRITE)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes16"));
+            }
+        }
+
+	if (isLive())
+	    ((RenderingAttributesRetained)this.retained).setStencilWriteMask(mask);
+	else
+	    ((RenderingAttributesRetained)this.retained).initStencilWriteMask(mask);
+    }
+
+    /**
+     * Retrieves the current stencil write mask for this RenderingAttributes
+     * object.
+     *
+     * @return the stencil write mask.
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @since Java 3D 1.4
+     */
+    public int getStencilWriteMask() {
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_STENCIL_ATTRIBUTES_READ)) {
+		throw new CapabilityNotSetException(J3dI18N.getString("RenderingAttributes17"));
+            }
+        }
+
+	return ((RenderingAttributesRetained)this.retained).getStencilWriteMask();
     }
 
 }
diff --git a/src/classes/share/javax/media/j3d/RenderingAttributesRetained.java b/src/classes/share/javax/media/j3d/RenderingAttributesRetained.java
index 1847fdf..44afe88 100644
--- a/src/classes/share/javax/media/j3d/RenderingAttributesRetained.java
+++ b/src/classes/share/javax/media/j3d/RenderingAttributesRetained.java
@@ -37,6 +37,15 @@ class RenderingAttributesRetained extends NodeComponentRetained {
 
     static final int RASTER_OP_VALUE		= 0x80;
 
+    static final int DEPTH_TEST_FUNC      	= 0x100;
+
+    static final int STENCIL_ENABLE      	= 0x200;
+
+    static final int STENCIL_OP_VALUES      	= 0x400;
+
+    static final int STENCIL_FUNC      	        = 0x800;
+
+    static final int STENCIL_WRITE_MASK         = 0x1000;
 
     // depth buffer Enable for hidden surface removal
     boolean depthBufferEnable = true;
@@ -47,6 +56,8 @@ class RenderingAttributesRetained extends NodeComponentRetained {
     
     int alphaTestFunction = RenderingAttributes.ALWAYS;
 
+    int depthTestFunction = RenderingAttributes.LESS_OR_EQUAL;
+
     boolean visible  = true;
 
     boolean ignoreVertexColors = false;
@@ -54,8 +65,19 @@ class RenderingAttributesRetained extends NodeComponentRetained {
     // raster operation
     boolean rasterOpEnable = false;
     int rasterOp = RenderingAttributes.ROP_COPY;
-    
+
+    // stencil operation
+    boolean stencilEnable = false;
+    int stencilFailOp = RenderingAttributes.STENCIL_KEEP;
+    int stencilZFailOp = RenderingAttributes.STENCIL_KEEP;
+    int stencilZPassOp = RenderingAttributes.STENCIL_KEEP;
+    int stencilFunction = RenderingAttributes.ALWAYS;
+    int stencilReferenceValue = 0;
+    int stencilCompareMask = ~0;
+    int stencilWriteMask = ~0;
+
     // depth buffer comparison function. Used by multi-texturing only
+    //[PEPE] NOTE: they are both unused. Candidates for removal.
     static final int LESS = 0;
     static final int LEQUAL = 1;
 
@@ -113,16 +135,33 @@ class RenderingAttributesRetained extends NodeComponentRetained {
    	return visible;
     }
 
-    final void initIgnoreVertexColors(boolean state) {
+ 
+    /**
+     * Enables or disables vertex colors for this RenderAttributes
+     * component object.
+     * @param state true or false to enable or disable vertex colors
+     */
+   final void initIgnoreVertexColors(boolean state) {
 	ignoreVertexColors = state;
     }
 
+    /**
+     * Enables or disables vertex colors for this RenderAttributes
+     * component object and sends a 
+     * message notifying the interested structures of the change.
+     * @param state true or false to enable or disable depth vertex colors
+     */
     final void setIgnoreVertexColors(boolean state) {
 	initIgnoreVertexColors(state);
 	sendMessage(IGNORE_VCOLOR,
 		    (state ? Boolean.TRUE: Boolean.FALSE));
     }
 
+    /**
+     * Retrieves the state of vertex color Enable flag
+     * @return true if vertex colors are enabled, false
+     * if vertex colors are disabled
+     */
     final boolean getIgnoreVertexColors() {
 	return ignoreVertexColors;
     }
@@ -260,6 +299,47 @@ class RenderingAttributesRetained extends NodeComponentRetained {
 	return alphaTestFunction;
     }
 
+    /**
+     * Set depth test function.  This function is used to compare the
+     * depth test value with each per-pixel alpha value.  If the test
+     * passes, then the pixel is written otherwise the pixel is not
+     * written.
+     * @param function the new depth test function.  One of:
+     * ALWAYS, NEVER, EQUAL, NOT_EQUAL, LESS, LESS_OR_EQUAL, GREATER,
+     * GREATER_OR_EQUAL.
+     * Default value is LESS_OR_EQUAL
+     * @since Java 3D 1.4
+     */
+    final void initDepthTestFunction(int function){
+	depthTestFunction = function;
+    }
+
+    /**
+     * Set depth test function.  This function is used to compare the
+     * depth test value with each per-pixel depth value.  If the test
+     * passes, the pixel is written otherwise the pixel is not
+     * written.
+     * @param function the new depth test function.  One of
+     * ALWAYS, NEVER, EQUAL, NOT_EQUAL, LESS, LESS_OR_EQUAL, GREATER,
+     * GREATER_OR_EQUAL
+     * Default value is LESS_OR_EQUAL
+     * @since Java 3D 1.4
+     */
+    final void setDepthTestFunction(int function){
+	initDepthTestFunction(function);
+	sendMessage(DEPTH_TEST_FUNC, new Integer(function));
+    }
+
+    /**
+     * Retrieves current depth test function.
+     * @return the current depth test function
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     * @since Java 3D 1.4
+     */
+    final int getDepthTestFunction(){
+        return depthTestFunction;
+    }
 
     /**
      * Initialize the raster op enable flag
@@ -306,30 +386,149 @@ class RenderingAttributesRetained extends NodeComponentRetained {
     }
 
 
+    // Stencil operations 
+    /**
+     * Initialize the stencil enable state
+     */
+    final void initStencilEnable(boolean state) {
+	stencilEnable = state;
+    }
+
+    /**
+     * Set the stencil enable state
+     */
+    final void setStencilEnable(boolean state) {
+	initStencilEnable(state);
+	sendMessage(STENCIL_ENABLE, new Boolean(state));
+    }
+
+    /**
+     * Retrieves the current stencil enable state.
+     */
+    final boolean getStencilEnable() {
+	return stencilEnable;
+    }
+
+    /**
+     * Initialize the stencil op. value
+     */
+    final void initStencilOp(int failOp, int zFailOp, int zPassOp) {
+	stencilFailOp = failOp;
+	stencilZFailOp = zFailOp;
+	stencilZPassOp = zPassOp;
+    }
+
+    /**
+     * Set the stencil op. value
+     */
+    final void setStencilOp(int failOp, int zFailOp, int zPassOp) {
+	initStencilOp(failOp, zFailOp, zPassOp);
+	
+	ArrayList arrList = new ArrayList(3);
+	arrList.add(new Integer(failOp));
+	arrList.add(new Integer(zFailOp));
+	arrList.add(new Integer(zPassOp));
+	sendMessage(STENCIL_OP_VALUES, arrList);
+    }
+
+    /**
+     * Retrieves the current stencil op. value
+     */
+    final void getStencilOp(int[] stencilOps) {
+	stencilOps[0] = stencilFailOp;
+	stencilOps[1] = stencilZFailOp;
+	stencilOps[2] = stencilZPassOp;
+    }
+
+
+    /**
+     * Initialize the stencil function value
+     */
+    final void initStencilFunction(int function, int refValue, int compareMask) {
+	stencilFunction = function;
+	stencilReferenceValue = refValue;
+	stencilCompareMask = compareMask;
+    }
+
+    /**
+     * Set the stencil function value
+     */
+    final void setStencilFunction(int function, int refValue, int compareMask) {
+	initStencilOp(function, refValue, compareMask);
+	
+	ArrayList arrList = new ArrayList(3);
+	arrList.add(new Integer(function));
+	arrList.add(new Integer(refValue));
+	arrList.add(new Integer(compareMask));
+	sendMessage(STENCIL_FUNC, arrList);
+    }
+
+    /**
+     * Retrieves the current stencil op. value
+     */
+    final void getStencilFunction(int[] params) {
+	params[0] = stencilFunction;
+	params[1] = stencilReferenceValue;
+	params[2] = stencilCompareMask;
+    }
+
+
+    /**
+     * Initialize the stencil write mask
+     */
+    final void initStencilWriteMask(int mask) {
+	stencilWriteMask = mask;
+    }
+
+    /**
+     * Set the stencil write mask
+     */
+    final void setStencilWriteMask(int mask) {
+	initStencilWriteMask(mask);	
+	sendMessage(STENCIL_WRITE_MASK, new Integer(mask));
+    }
+
+    /**
+     * Retrieves the current stencil write mask
+     */
+    final int getStencilWriteMask() {
+	return stencilWriteMask;
+    }
+
     
     /**
      * Updates the native context.
      */
+
+    // TODO : Need to handle stencil operation on the native side -- Chien
     native void updateNative(long ctx,
 			     boolean depthBufferWriteEnableOverride,
                              boolean depthBufferEnableOverride,
 			     boolean depthBufferEnable, 
 			     boolean depthBufferWriteEnable,
+                             int depthTestFunction,
 			     float alphaTestValue, int alphaTestFunction,
 			     boolean ignoreVertexColors,
-			     boolean rasterOpEnable, int rasterOp);
+			     boolean rasterOpEnable, int rasterOp,
+			     boolean userStencilAvailable, boolean stencilEnable, 
+			     int stencilFailOp, int stencilZFailOp, int stencilZPassOp,
+			     int stencilFunction, int stencilReferenceValue, 
+			     int stencilCompareMask, int stencilWriteMask );
 
     /**
      * Updates the native context.
      */
-    void updateNative(long ctx,
+    void updateNative(Canvas3D c3d,
 		      boolean depthBufferWriteEnableOverride,
                       boolean depthBufferEnableOverride) {
-	updateNative(ctx, 
+	updateNative(c3d.ctx, 
 		     depthBufferWriteEnableOverride, depthBufferEnableOverride,
-		     depthBufferEnable, depthBufferWriteEnable, alphaTestValue, 
-		     alphaTestFunction, ignoreVertexColors,
-		     rasterOpEnable, rasterOp);
+		     depthBufferEnable, depthBufferWriteEnable,  depthTestFunction,
+                     alphaTestValue, alphaTestFunction, ignoreVertexColors,
+		     rasterOpEnable, rasterOp, c3d.userStencilAvailable, stencilEnable, 
+		     stencilFailOp, stencilZFailOp, stencilZPassOp,
+		     stencilFunction, stencilReferenceValue, stencilCompareMask,
+		     stencilWriteMask  );
     }
 
    /**
@@ -368,14 +567,17 @@ class RenderingAttributesRetained extends NodeComponentRetained {
      *  given "value"
      */
     synchronized void updateMirrorObject(int component, Object value) {
-	RenderingAttributesRetained mirrorRa = (RenderingAttributesRetained)mirror;
-      
+	RenderingAttributesRetained mirrorRa = (RenderingAttributesRetained)mirror;      
+
 	if ((component & DEPTH_ENABLE) != 0) {
 	    mirrorRa.depthBufferEnable = ((Boolean)value).booleanValue();
 	}
 	else if ((component & DEPTH_WRITE_ENABLE) != 0) {
 	    mirrorRa.depthBufferWriteEnable = ((Boolean)value).booleanValue();
 	}
+	else if ((component & DEPTH_TEST_FUNC) != 0) {
+	    mirrorRa.depthTestFunction = ((Integer)value).intValue();
+	}
 	else if ((component & ALPHA_TEST_VALUE) != 0) {
 	    mirrorRa.alphaTestValue = ((Float)value).floatValue();
 	}
@@ -393,7 +595,25 @@ class RenderingAttributesRetained extends NodeComponentRetained {
 	}	
 	else if ((component & RASTER_OP_VALUE) != 0) {
 	    mirrorRa.rasterOp = (((Integer)value).intValue());
-	}	
+	}
+	else if ((component & STENCIL_ENABLE) != 0) {
+	    mirrorRa.stencilEnable = (((Boolean)value).booleanValue());
+	}
+	else if ((component & STENCIL_OP_VALUES) != 0) {
+	    ArrayList arrlist = (ArrayList) value;
+	    mirrorRa.stencilFailOp = (((Integer)arrlist.get(0)).intValue());
+	    mirrorRa.stencilZFailOp = (((Integer)arrlist.get(1)).intValue());
+	    mirrorRa.stencilZPassOp = (((Integer)arrlist.get(2)).intValue());
+	}
+	else if ((component & STENCIL_FUNC) != 0) {
+	    ArrayList arrlist = (ArrayList) value;
+	    mirrorRa.stencilFunction = (((Integer)arrlist.get(0)).intValue());
+	    mirrorRa.stencilReferenceValue = (((Integer)arrlist.get(1)).intValue());
+	    mirrorRa.stencilCompareMask = (((Integer)arrlist.get(2)).intValue());
+	}
+	else if ((component & STENCIL_WRITE_MASK) != 0) {
+	    mirrorRa.stencilWriteMask = (((Integer)value).intValue());
+	}
     }
 
     boolean equivalent(RenderingAttributesRetained rr) {
@@ -406,7 +626,16 @@ class RenderingAttributesRetained extends NodeComponentRetained {
 		(rr.visible == visible) &&
 		(rr.ignoreVertexColors == ignoreVertexColors) &&
 		(rr.rasterOpEnable == rasterOpEnable) &&
-		(rr.rasterOp == rasterOp));
+		(rr.rasterOp == rasterOp) &&
+		(rr.depthTestFunction == depthTestFunction) &&
+		(rr.stencilEnable == stencilEnable) &&
+		(rr.stencilFailOp == stencilFailOp) &&
+		(rr.stencilZFailOp == stencilZFailOp) &&
+		(rr.stencilZPassOp == stencilZPassOp) &&
+		(rr.stencilFunction == stencilFunction) &&
+		(rr.stencilReferenceValue == stencilReferenceValue) &&
+		(rr.stencilCompareMask == stencilCompareMask) &&
+		(rr.stencilWriteMask == stencilWriteMask));
     }
 
     protected void set(RenderingAttributesRetained ra) {
@@ -415,10 +644,20 @@ class RenderingAttributesRetained extends NodeComponentRetained {
 	depthBufferWriteEnable = ra.depthBufferWriteEnable;
 	alphaTestValue = ra.alphaTestValue;
 	alphaTestFunction = ra.alphaTestFunction;
+	depthTestFunction = ra.depthTestFunction;
 	visible = ra.visible;
 	ignoreVertexColors = ra.ignoreVertexColors;
 	rasterOpEnable = ra.rasterOpEnable;
 	rasterOp = ra.rasterOp;
+	stencilEnable = ra.stencilEnable;
+	stencilFailOp = ra.stencilFailOp;
+	stencilZFailOp = ra.stencilZFailOp;
+	stencilZPassOp = ra.stencilZPassOp;
+	stencilFunction = ra.stencilFunction;
+	stencilReferenceValue = ra.stencilReferenceValue;
+	stencilCompareMask = ra.stencilCompareMask;
+	stencilWriteMask = ra.stencilWriteMask;
+
     }
     
     final void sendMessage(int attrMask, Object attr) {
@@ -462,7 +701,7 @@ class RenderingAttributesRetained extends NodeComponentRetained {
 
     }
 
-
+    // TODO : Need to handle stencil operation -- Chien
     void handleFrequencyChange(int bit) {
 	int mask = 0;
 	
@@ -478,6 +717,16 @@ class RenderingAttributesRetained extends NodeComponentRetained {
 	    mask = RASTER_OP_ENABLE;
 	if(bit == RenderingAttributes.ALLOW_DEPTH_ENABLE_WRITE)
 	    mask = DEPTH_WRITE_ENABLE;
+	if( bit == RenderingAttributes.ALLOW_DEPTH_TEST_FUNCTION_WRITE)
+	    mask = DEPTH_TEST_FUNC;
+
+	if( bit == RenderingAttributes.ALLOW_STENCIL_ATTRIBUTES_WRITE)
+	    mask = DEPTH_TEST_FUNC;
+
+	if( bit == RenderingAttributes.ALLOW_DEPTH_TEST_FUNCTION_WRITE)
+	    mask = STENCIL_ENABLE | STENCIL_OP_VALUES | STENCIL_FUNC | 
+		STENCIL_WRITE_MASK;
+
 	if (mask != 0)
 	    setFrequencyChangeMask(bit, mask);
 	//	System.out.println("changedFreqent2 = "+changedFrequent);
diff --git a/src/classes/share/javax/media/j3d/RenderingAttributesStructure.java b/src/classes/share/javax/media/j3d/RenderingAttributesStructure.java
index d81a58a..abe2180 100644
--- a/src/classes/share/javax/media/j3d/RenderingAttributesStructure.java
+++ b/src/classes/share/javax/media/j3d/RenderingAttributesStructure.java
@@ -42,11 +42,13 @@ class RenderingAttributesStructure extends J3dStructure implements ObjectUpdate
 	for (int i=0; i < nMsg; i++) {
 	    m = messages[i];
 	    switch (m.type) {
-		// Apperance is always updated immediately, since rBin needs
+		// Appearance is always updated immediately, since rBin needs
 		// the most up-to-date values for restructuring
 	    case J3dMessage.APPEARANCE_CHANGED:
-	    case J3dMessage.TEXTURE_UNIT_STATE_CHANGED: // TODO: Is this correct?
+	    case J3dMessage.SHADER_APPEARANCE_CHANGED:
+	    case J3dMessage.TEXTURE_UNIT_STATE_CHANGED:
 		{
+		    // System.out.println("1 RAS : J3dMessage type : " + m.type);
 		    int component = ((Integer)m.args[1]).intValue();
 		    NodeComponentRetained nc = (NodeComponentRetained)m.args[0];
 		    nc.mirror.changedFrequent = ((Integer)m.args[3]).intValue();
@@ -69,7 +71,11 @@ class RenderingAttributesStructure extends J3dStructure implements ObjectUpdate
 	    case J3dMessage.TRANSPARENCYATTRIBUTES_CHANGED:
 	    case J3dMessage.MATERIAL_CHANGED:
 	    case J3dMessage.TEXCOORDGENERATION_CHANGED:
+	    case J3dMessage.SHADER_ATTRIBUTE_CHANGED: 
+	    case J3dMessage.SHADER_ATTRIBUTE_SET_CHANGED:
 		{
+		    // System.out.println("2 RAS : J3dMessage type : " + m.type);
+
 		    NodeComponentRetained nc = (NodeComponentRetained)m.args[0];
 		    nc.mirror.changedFrequent = ((Integer)m.args[3]).intValue();
 		    if (nc.mirror.changedFrequent != 0) {
@@ -105,8 +111,6 @@ class RenderingAttributesStructure extends J3dStructure implements ObjectUpdate
 		    NodeComponentRetained nc = (NodeComponentRetained)m.args[0];
 		    nc.mirror.changedFrequent = ((Integer)m.args[4]).intValue();
 
-
-
 		    if (nc.mirror.changedFrequent != 0) {
 			objList.add(m);
 			addMirrorObj = true;
@@ -118,11 +122,9 @@ class RenderingAttributesStructure extends J3dStructure implements ObjectUpdate
 		    }
 		}
 		break;
-
 	    case J3dMessage.TEXTURE_CHANGED: 
 		{
 		    NodeComponentRetained nc = (NodeComponentRetained)m.args[0];
-		    int attrMask = ((Integer)m.args[1]).intValue();
 		    nc.mirror.changedFrequent = ((Integer)m.args[3]).intValue();
 		    
 		    objList.add(m);
@@ -190,6 +192,7 @@ class RenderingAttributesStructure extends J3dStructure implements ObjectUpdate
 		updateTextureAttributes((Object[])m.args);
 	    }
 	    else if (m.type == J3dMessage.APPEARANCE_CHANGED ||
+		     m.type == J3dMessage.SHADER_APPEARANCE_CHANGED ||
 		     m.type == J3dMessage.TEXTURE_UNIT_STATE_CHANGED){
 		NodeComponentRetained nc = (NodeComponentRetained)m.args[0];
 		nc.mirror.compChanged = 0;
@@ -206,12 +209,13 @@ class RenderingAttributesStructure extends J3dStructure implements ObjectUpdate
     }
 
 
-    void updateNodeComponent(Object[] args) {
-      NodeComponentRetained n = (NodeComponentRetained)args[0];
-      n.updateMirrorObject(((Integer)args[1]).intValue(), args[2]);
+    private void updateNodeComponent(Object[] args) {
+	// System.out.println("RAS : updateNodeComponent : " + this);	    
+	NodeComponentRetained n = (NodeComponentRetained)args[0];
+	n.updateMirrorObject(((Integer)args[1]).intValue(), args[2]);
     }
 
-    void updateTextureAttributes(Object[] args) {
+    private void updateTextureAttributes(Object[] args) {
       TextureAttributesRetained n = (TextureAttributesRetained)args[0];
       n.updateMirrorObject(((Integer)args[1]).intValue(), args[2], args[3]);
     }
diff --git a/src/classes/share/javax/media/j3d/SceneGraphObject.java b/src/classes/share/javax/media/j3d/SceneGraphObject.java
index bfae7f3..32c52b8 100644
--- a/src/classes/share/javax/media/j3d/SceneGraphObject.java
+++ b/src/classes/share/javax/media/j3d/SceneGraphObject.java
@@ -15,9 +15,37 @@ package javax.media.j3d;
 import java.util.Hashtable;
 
 /**
- * SceneGraphObject is a common superclass for
- * all scene graph component objects.  This includes Node,
- * Geometry, Appearance, etc.
+ * SceneGraphObject is the common superclass for all scene graph
+ * objects. Scene graph objects are classified into two main types:
+ * nodes and node components. The Node object is the common superclass
+ * of all nodes, which includes TransformGroup, Shape3D, etc.
+ * The NodeComponent object is the common superclass of all node
+ * components, which includes Geometry, Appearance, etc.
+ *
+ * <p>
+ * All scene graph objects have a name, a user data object, a set of
+ * capability bits, and a set of capabilityIsFrequent bits.
+ *
+ * <p>
+ * Capability bits control whether a particular attribute in a node or
+ * node component is readable or writable. For live or compiled scene
+ * graphs, only those attributes whose capabilities are set before the
+ * scene graph is compiled or made live may be read or written. The
+ * default value for all <i>read</i> capability bits is true, meaning
+ * that all attributes may be read by default. The default value for
+ * all <i>write</i> capability bits is false, meaning that no
+ * attributes may be written by default. Read capability bits are
+ * defined as those capability bits of the form <code>ALLOW_*_READ</code>,
+ * plus the <code>ALLOW_INTERSECT</code> capability bit. Write
+ * capability bits are defined as those capability bits of the form
+ * <code>ALLOW_*_WRITE</code>, plus the <code>ALLOW_CHILDREN_EXTEND</code>
+ * and <code>ALLOW_DETACH</code> capability bits.
+ *
+ * <p>
+ * NOTE that the <code>ENABLE_COLLISION_REPORTING</code> and
+ * <code>ENABLE_PICK_REPORTING</code> bits are not really capability bits,
+ * although they are set with the setCapability method. The default value
+ * for each of the <code>ENABLE_*_REPORTING bits</code> is false.
  */
 public abstract class SceneGraphObject extends Object {
    // Any global flags? (e.g., execution cullable, collideable)
@@ -43,6 +71,9 @@ public abstract class SceneGraphObject extends Object {
     // A reference to user data
     private Object userData = null;
 
+    // Optional name for object.
+    private String objectName = null;
+
     // use for cloneTree/cloneNode only, set to null after the operation
     Hashtable nodeHashtable = null;
 
@@ -52,10 +83,13 @@ public abstract class SceneGraphObject extends Object {
      * Constructs a SceneGraphObject with default parameters.  The default
      * values are as follows:
      * <ul>
-     * capability bits : clear (all bits)<br>
+     * all <i>read</i> capability bits : set (true)<br>
+     * all <i>write</i> capability bits : clear (false)<br>
+     * all capabilityIsFrequent bits : set (true)<br>
      * isLive : false<br>
      * isCompiled : false<br>
      * user data : null<br>
+     * name : null<br>
      * </ul>
      */
     public SceneGraphObject() {
@@ -76,7 +110,18 @@ public abstract class SceneGraphObject extends Object {
 	//	this.retained = new <ClassName>Retained();
 	//	this.retained.setSource(this);
     }
-  
+
+    /**
+     * Method to set default read capability bits to true
+     */
+    void setDefaultReadCapabilities(int[] bits) {
+        if (true /*VirtualUniverse.mc.defaultReadCapability*/) {
+            for (int i=0; i < bits.length; i++) {
+                setCapability(bits[i]);
+            }
+        }
+    }
+
     /**
      * Retrieves the specified capability bit.  Note that only one capability
      * bit may be retrieved per method invocation--capability bits cannot
@@ -331,6 +376,28 @@ public abstract class SceneGraphObject extends Object {
     public void updateNodeReferences(NodeReferenceTable referenceTable) {
     }
 
+    /**
+     * Sets the name of this object. Object names are for information
+     * only.
+     *
+     * @param name the new name of this object
+     *
+     * @since Java 3D 1.4
+     */
+    public void setName( String name ) {
+        objectName = name;
+    }
+
+    /**
+     * Returns the name of this object.
+     *
+     * @return the name of this object
+     *
+     * @since Java 3D 1.4
+     */             
+    public String getName() {
+	return objectName;
+    }
 
     /**
      * Copies all SceneGraphObject information from
@@ -353,6 +420,7 @@ public abstract class SceneGraphObject extends Object {
         // Duplicate any class specific data here.
 	capabilityBits = originalNode.capabilityBits;
         userData = originalNode.userData;
+        objectName = originalNode.objectName;
     }
 
 
@@ -402,4 +470,25 @@ public abstract class SceneGraphObject extends Object {
 	    return originalNodeComponent;
 	}
     }
+
+    // Internal method to make a prefix out of the name of this object
+    String getNamePrefix() {
+	String name = getName();
+
+	if (name != null) {
+	    return "[" + name + "] ";
+	}
+
+	return "";
+    }
+
+    /**
+     * Returns a String representation of this SceneGraphObject.
+     * If its name is non-null, then it is concatenated with
+     * super.toString().
+     */
+    public String toString() {
+	return getNamePrefix() + super.toString();
+    }
+
 }
diff --git a/src/classes/share/javax/media/j3d/ScreenViewCache.java b/src/classes/share/javax/media/j3d/ScreenViewCache.java
index a3802d4..397367a 100644
--- a/src/classes/share/javax/media/j3d/ScreenViewCache.java
+++ b/src/classes/share/javax/media/j3d/ScreenViewCache.java
@@ -37,7 +37,12 @@ class ScreenViewCache extends Object {
 
     // Mask that indicates Screen3D view dependence info. has changed,
     // and CanvasViewCache may need to recompute the final view matries. 
-    int scrvcDirtyMask = 0;
+    // Issue 163: Array of dirty bits is used because the Renderer and
+    // RenderBin run asynchronously. Now that they each have a separate
+    // instance of CanvasViewCache (due to the fix for Issue 109), they
+    // need separate dirty bits. Array element 0 is used for the Renderer and
+    // element 1 is used for the RenderBin.
+    int[] scrvcDirtyMask = new int[2];
     
     //
     // Tracker-base coordinate system to image-plate coordinate
@@ -72,17 +77,20 @@ class ScreenViewCache extends Object {
      * Take snapshot of all per-screen API parameters.
      */
     synchronized void snapshot() {
-	
-	// accumulate the dirty bits for offscreen because
-	// the dirty bits will not be processed until renderOffScreen
-	// or triggered by RenderBin at some little time
-	if (screen.offScreen)
-	    scrvcDirtyMask |= screen.scrDirtyMask;
-	else
-	    scrvcDirtyMask = screen.scrDirtyMask;
-
-	screen.scrDirtyMask = 0;
-	physicalScreenWidth = screen.physicalScreenWidth;
+
+        // accumulate the dirty bits for offscreen because
+        // the dirty bits will not be processed until renderOffScreen
+        // or triggered by RenderBin at some little time
+        if (screen.offScreen) {
+            scrvcDirtyMask[0] |= screen.scrDirtyMask;
+            scrvcDirtyMask[1] |= screen.scrDirtyMask;
+        } else {
+            scrvcDirtyMask[0] = screen.scrDirtyMask;
+            scrvcDirtyMask[1] = screen.scrDirtyMask;
+        }
+        screen.scrDirtyMask = 0;
+
+        physicalScreenWidth = screen.physicalScreenWidth;
 	physicalScreenHeight = screen.physicalScreenHeight;
 	screenWidth = screen.screenSize.width;
 	screenHeight = screen.screenSize.height;
diff --git a/src/classes/share/javax/media/j3d/Sensor.java b/src/classes/share/javax/media/j3d/Sensor.java
index ef44ac6..05df43a 100644
--- a/src/classes/share/javax/media/j3d/Sensor.java
+++ b/src/classes/share/javax/media/j3d/Sensor.java
@@ -59,29 +59,35 @@ public class Sensor {
 
     /**
      * Set predictor type to do no prediction; this is the default.
+     *
+     * @deprecated As of Java 3D version 1.4, prediction is not a
+     * supported feature.
      */
     public static final int PREDICT_NONE = 1;
 
     /**
-     * Set predictor type to generate the SensorRead to correspond with 
-     * the next frame time.
+     * @deprecated As of Java 3D version 1.4, prediction is not a
+     * supported feature.
      */
     public static final int PREDICT_NEXT_FRAME_TIME = 2;
 
     /**
      * Use no prediction policy; this is the default.
+     *
+     * @deprecated As of Java 3D version 1.4, prediction is not a
+     * supported feature.
      */
     public static final int NO_PREDICTOR = 16;
 
     /**
-     * Set the predictor policy to assume the sensor is predicting head 
-     * position/orientation.
+     * @deprecated As of Java 3D version 1.4, prediction is not a
+     * supported feature.
      */
     public static final int HEAD_PREDICTOR = 32;
 
     /**
-     * Set the predictor policy to assume the sensor is predicting hand 
-     * position/orientation.
+     * @deprecated As of Java 3D version 1.4, prediction is not a
+     * supported feature.
      */
     public static final int HAND_PREDICTOR = 64;
 
@@ -110,11 +116,11 @@ public class Sensor {
     // size of the sensor read buffer
     int sensorReadCount;
 
-    // Default prediction policy: don't predict
-    int predictionPolicy = NO_PREDICTOR; 
+    // Prediction policy -- unused
+    private int predictionPolicy = NO_PREDICTOR; 
 
-    // Default Predictor none
-    int predictorType = PREDICT_NONE;
+    // Predictor type -- unused
+    private int predictorType = PREDICT_NONE;
 
     // This sensor's associated device
     InputDevice device;
@@ -135,12 +141,6 @@ public class Sensor {
     Matrix3d temp_rot = new Matrix3d(); 
     Matrix3d local_svd = new Matrix3d(); 
 
-    // Prediction workspace -- these may go away when the readings array
-    //  is used.
-    static int MAX_PREDICTION_LENGTH = 20;
-    Transform3D[]  previousReads = new Transform3D[MAX_PREDICTION_LENGTH];
-    long[] times = new long[MAX_PREDICTION_LENGTH];
- 
 
     /**
      * Constructs a Sensor object for the specified input device using
@@ -149,8 +149,8 @@ public class Sensor {
      * sensor read count : 30<br>
      * sensor button count : 0<br>
      * hot spot : (0,0,0)<br>
-     * predictor : PREDICT_NONE<br>
-     * prediction policy : NO_PREDICTOR<br>
+     * predictor : PREDICT_NONE &mdash; <i>this attribute is unused</i><br>
+     * prediction policy : NO_PREDICTOR &mdash; <i>this attribute is unused</i><br>
      * </ul>
      * @param device the Sensor's associated device.
      */
@@ -237,11 +237,6 @@ public class Sensor {
         }
         currentIndex = 0;
         this.hotspot = new Point3d(hotspot);
-
-        // prediction initialization
-        for(int i=0 ; i<MAX_PREDICTION_LENGTH ; i++)  {
-            previousReads[i] = new Transform3D();
-        }
     }
 
     //  argument of 0 is last reading (ie, currentIndex), argument 
@@ -252,11 +247,16 @@ public class Sensor {
     }
 
     /**
-     * This function sets the type of predictor to use with this sensor.
+     * Sets the type of predictor to use with this sensor.
+     * Since prediction is not implemented (and never has been), this
+     * attribute has no effect.
      * @param predictor predictor type one of PREDICT_NONE or
      * PREDICT_NEXT_FRAME_TIME
      * @exception IllegalArgumentException if an invalid predictor type
      *  is specified.
+     *
+     * @deprecated As of Java 3D version 1.4, prediction is not a
+     * supported feature.
      */
     public void setPredictor(int predictor){
        if (predictor != PREDICT_NONE && predictor != PREDICT_NEXT_FRAME_TIME) {
@@ -267,20 +267,27 @@ public class Sensor {
     }
 
     /**
-     * This function returns the type of predictor used by this sensor.
-     * @return returns the predictor type. One of PREDICT_NONE or
-     * PREDICT_NEXT_FRAME_TIME.
+     * Returns the type of predictor used by this sensor.
+     * @return the predictor type.
+     *
+     * @deprecated As of Java 3D version 1.4, prediction is not a
+     * supported feature.
      */ 
     public int getPredictor(){
 	return predictorType;
     }
 
     /**
-     * This function sets the prediction policy use by this sensor.
+     * Sets the prediction policy use by this sensor.
+     * Since prediction is not implemented (and never has been), this
+     * attribute has no effect.
      * @param policy prediction policy one of NO_PREDICTOR, HEAD_PREDICTOR,
      * or HAND_PREDICTOR
      * @exception IllegalArgumentException if an invalid prediction policy
      *  is specified.
+     *
+     * @deprecated As of Java 3D version 1.4, prediction is not a
+     * supported feature.
      */
     public void setPredictionPolicy(int policy){
 	if (policy != NO_PREDICTOR && policy != HEAD_PREDICTOR &&
@@ -291,9 +298,11 @@ public class Sensor {
     }
 
     /**
-     * This function returns the prediction policy used by this sensor.
-     * @return returns the prediction policy. one of NO_PREDICTOR,
-     * HEAD_PREDICTOR, or HAND_PREDICTOR.
+     * Returns the prediction policy used by this sensor.
+     * @return the prediction policy.
+     *
+     * @deprecated As of Java 3D version 1.4, prediction is not a
+     * supported feature.
      */ 
     public int getPredictionPolicy(){
 	return predictionPolicy;
@@ -332,79 +341,30 @@ public class Sensor {
     }
 
     /**
-     * Computes the sensor reading consistent with the prediction policy
-     * and copies that value into the specified argument; calling this method
-     * with a prediction policy of NO_PREDICTOR will return the last sensor 
-     * reading; calling this method with a prediction policy of HAND_PREDICTOR,
-     * or HEAD_PREDICTOR will extrapolate previous sensor readings to the
-     * current time.
-     * @param read The matrix that will receive the predicted sensor reading
+     * Retrieves the last sensor reading and copies that value into
+     * the specified argument.
+     *
+     * @param read the matrix that will receive the sensor reading
      */
-    public void getRead(Transform3D read){
-        long time;
-
+    public void getRead(Transform3D read) {
         if(demand_driven == true)
              device.pollAndProcessInput();
 
-        time =  System.currentTimeMillis();
-
-        // before using prediction, fill in some values
-        if(num_reads_so_far < 40*SENSOR_READ_COUNT_BUFFER) {
-            num_reads_so_far++;
-	    read.set(readings[currentIndex].read);
-            return;
-        }
-
-	switch(predictionPolicy) {
-	case NO_PREDICTOR:
-	    read.set(readings[currentIndex].read);
-	    break;
-        case HAND_PREDICTOR:
-	    read.set(readings[currentIndex].read);
-            //getPredictedRead(read, time, 3, 2);     
-            break;
-        case HEAD_PREDICTOR:
-	    read.set(readings[currentIndex].read);
-            //getPredictedRead(read, time, 3, 2);     
-            break;
-	}
+	read.set(readings[currentIndex].read);
     }
 
     /**
-     * Computes the sensor reading consistent as of time deltaT in the future
-     * and copies that value into the specified argument; the reading is
-     * computed using the current prediction policy; a prediction policy of
-     * NO_PREDICTOR will yield the most recent sensor reading for any
-     * deltaT argument (i.e., this method is the same as getRead for a prediction
-     * policy of NO_PREDICTOR).  The time argument must be >= 0.
-     * @param read the matrix that will receive the predicted sensor reading
-     * @param deltaT the time delta into the future for this read
+     * Retrieves the last sensor reading and copies that value into
+     * the specified argument.
+     *
+     * @param read the matrix that will receive the sensor reading
+     * @param deltaT this parameter is ignored
+     *
+     * @deprecated As of Java 3D version 1.4, prediction is not a
+     * supported feature; use <code>getRead(Transform3D)</code> instead.
      */
     public void getRead(Transform3D read, long deltaT){
-        long current_time;
-
-        if(deltaT < 0L)  {
-             throw new IllegalArgumentException(J3dI18N.getString("Sensor2"));
-        }
-
-        if(demand_driven == true)
-             device.pollAndProcessInput();
-
-        current_time =  System.currentTimeMillis();
-
-        switch(predictionPolicy) {
-        case NO_PREDICTOR:
-            read.set(readings[currentIndex].read);
-            break;
-        case HAND_PREDICTOR:
-            read.set(readings[currentIndex].read);
-            //getPredictedRead(read, current_time + deltaT, 3, 2);
-            break;
-        case HEAD_PREDICTOR:
-            read.set(readings[currentIndex].read);
-            //getPredictedRead(read, current_time + deltaT, 3, 2);
-            break;
-        }
+	getRead(read);
     }
 
     /**
@@ -566,132 +526,4 @@ public class Sensor {
         currentIndex = temp;
    }
 
-    /**
-     * This routine does an nth order fit of the last num_readings, which
-     * can be plotted on a graph of time vs. sensor reading.  There is a
-     * separate fit done for each of the 16 matrix elements, then an SVD
-     * done on the final matrix.  The curve that is produced takes into
-     * account non-constant times between each sample (it is fully general).
-     * This curve can then be used to produce a prediction for any 
-     * time in the future by simply inserting a time value and using the 
-     * solution matrix.
-     */
-     void getPredictedRead(Transform3D transform, long time, int num_readings, 
-                              int order) {
-
-       int index = currentIndex;  // lock in current_index for MT-safety
-       long time_basis = readings[index].time;
-       long tempTime;
-
-       time -= time_basis;
-
-       GMatrix A = new GMatrix(num_readings, order+1);
-
-       for(int i=0 ; i<num_readings ; i++) {
-           A.setElement(i, 0, 1.0);
-           tempTime = lastTimeRelative(num_readings-i-1, index, time_basis);
-           A.setElement(i, 1, (double)tempTime);
-           for(int j=2; j<=order ; j++) {
-               // powerAndDiv(time, n) = times^n/n
-               A.setElement(i, j, powerAndDiv(tempTime, j));
-           }
-        }
-
-       GMatrix A_Transpose = new GMatrix(A);
-       A_Transpose.transpose();
-       GMatrix M = new GMatrix(order+1, order+1);
-       M.mul(A_Transpose, A);
-       try {
-          M.invert();
-       } catch (SingularMatrixException e) {
-          System.out.println("SINGULAR MATRIX EXCEPTION in prediction");
-          System.out.println(M);
-       }
-
-       // TODO: can be class scope
-       double[] transformArray = new double[16];
-       GMatrix solMatrix = new GMatrix(order+1, num_readings);
-       solMatrix.mul(M,A_Transpose);
-
-       GVector P = new GVector(order+1);
-
-       // fill in the time for which we are trying to predict a sensor value
-       GVector predTimeVec = new GVector(order+1);
-       predTimeVec.setElement(0, 1);
-       predTimeVec.setElement(1, time);
-       for(int i=2 ; i<=order ; i++) {
-          predTimeVec.setElement(i, powerAndDiv(time, i));
-       }
-
-       GVector R = new GVector(num_readings);
-
-       for(int transElement=0 ; transElement<16 ; transElement++) {
-
-             for(int i=0 ; i<num_readings ; i++) {
-                R.setElement(i, lastReadRelative(num_readings-i-1, index, 
-                                                           transElement));
-             }       
-
-             P.mul(solMatrix,R);
-             transformArray[transElement] =  P.dot(predTimeVec);
-       }
-
-       //Matrix4d temp = new Matrix4d(transformArray);
-       //localSVD(temp);
-       //transform.set(temp);
-       transform.set(transformArray);
-       transform.normalize();
-     }
-
-    /**
-     * Extracts the kth most recent sensor reading and copies that value into
-     * the specified argument; where 0 is the most recent sensor reading, 1 is
-     * the next most recent sensor reading, etc.  
-     * @param read The matrix that will receive the most recent sensor reading
-     * @param k  The kth previous sensor reading
-     */
-    double lastReadRelative(int kth, int base_index, int mat_element){
-        // kth should be < sensorReadCount
-      return 
-      readings[previousIndexRelative(kth, base_index)].read.mat[mat_element];
-    }
-
-    /**
-     * Returns the time associated with the kth most recent sensor reading;
-     * where 0 is the most recent sensor reading, 1 is the next most recent
-     * sensor reading, etc.  However, unlike the public method, returns
-     * the kth reading relative to the index given, instead of the 
-     * current_index and returns the time relative to timeBasis.
-     * @return the time associated with the kthmost recent sensor reading.
-     */
-    long lastTimeRelative(int k, int base_index, long timeBasis){
-        // kth should be < sensorReadCount
-        long time;
-        time = timeBasis - readings[previousIndexRelative(k, base_index)].time;
-        return time;
-    }
-
-    //  argument of 0 is last reading, argument of 1 means next to last 
-    // index, etc.  , but all of these are relative to *base_index*
-    int previousIndexRelative(int k, int base_index){
-        int temp = base_index - k;
-        return(temp >= 0 ? temp : MaxSensorReadIndex + temp + 1);
-    }
-
-    // this method returns  (value^order)/order
-    double powerAndDiv(double value, int order) {
-
-       if(order == 0)
-           return 1;
-       else if(order == 1)
-           return value;
-
-       double total = 1.0;
-       for(int i=0 ; i< order ; i++)
-           total *= value;
-
-       total = total / (double)order;
-       return total;
-    }
-
 }
diff --git a/src/classes/share/javax/media/j3d/SetLiveState.java b/src/classes/share/javax/media/j3d/SetLiveState.java
index 84ff55a..6c81c4e 100644
--- a/src/classes/share/javax/media/j3d/SetLiveState.java
+++ b/src/classes/share/javax/media/j3d/SetLiveState.java
@@ -243,7 +243,7 @@ class SetLiveState extends Object {
 	localToVworldIndex = null;
 	localToVworldKeys = null;
 	
-        // TODO: optimization for targetThreads computation, require
+        // XXXX: optimization for targetThreads computation, require
         // cleanup in GroupRetained.doSetLive()
 	//transformTargetThreads = 0;
 
diff --git a/src/classes/share/javax/media/j3d/Shader.java b/src/classes/share/javax/media/j3d/Shader.java
new file mode 100644
index 0000000..f5870ee
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/Shader.java
@@ -0,0 +1,131 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The Shader object is the abstract base class for programmable
+ * shader code. Currently, only text-based source code shaders are
+ * supported, so the only subclass of Shader is SourceCodeShader. We
+ * leave open the possibility for binary (object code) shaders in the
+ * future.
+ *
+ * <p>
+ * Each instance of a Shader object allows an application to specify
+ * the source code used in programming the Graphics Pipeline Unit
+ * (GPU) of the graphics accelerator. A Shader object is constructed
+ * with modes that specify the <i>shading language</i> and the
+ * <i>shader type</i>.
+ *
+ * <p>
+ * The shading language specifies the language and runtime environment
+ * used to program the GPU. The currently defined shading languages
+ * are GLSL (also known as the OpenGL 2.0 shading language) and
+ * Cg. Note that not all shading languages are supported on all
+ * platforms. It is up to the application or utility to query whether
+ * a particular shading language is supported before using it. The
+ * value of the <code>shadingLanguage</code> mode is one of:
+ * <code>SHADING_LANGUAGE_GLSL</code> or
+ * <code>SHADING_LANGUAGE_CG</code>.
+ *
+ *<p>
+ * The shader type specifies whether the shader is a <i>vertex
+ * shader</i> or a <i>fragment shader</i>. A vertex shader replaces
+ * the fixed-function graphics pipeline for vertex operations
+ * (transformation and lighting). A fragment shader replaces the
+ * fixed-function graphics pipeline for fragment shading operations
+ * (texture mapping, texture application, coloring, shading, and so
+ * forth). The value of the <code>shaderType</code> mode is one of:
+ * <code>SHADER_TYPE_VERTEX</code> or
+ * <code>SHADER_TYPE_FRAGMENT</code>.
+ *
+ * <p>
+ * Both the shading language and shader type are immutable modes of
+ * the Shader object.
+ *
+ * <p>
+ * NOTE: Applications should <i>not</i> extend this class.
+ *
+ * @see ShaderProgram
+ * @see Canvas3D#isShadingLanguageSupported
+ *
+ * @since Java 3D 1.4
+ */
+
+public abstract class Shader extends NodeComponent {
+
+
+    /**
+     * This constant indicates the GLSL shading language. It is one
+     * of the possible values of the shadingLanguage parameter.
+     */
+    public static final int SHADING_LANGUAGE_GLSL = 1;
+
+    /**
+     * This constant indicates the Cg shading language. It is one
+     * of the possible values of the shadingLanguage parameter.
+     */
+    public static final int SHADING_LANGUAGE_CG = 2;
+
+
+    /**
+     * This constant indicates that the shader type is a vertex
+     * shader.  It is one of the possible values of the shaderType
+     * parameter.
+     */
+    public static final int SHADER_TYPE_VERTEX = 1;
+
+    /**
+     * This constant indicates that the shader type is a fragment
+     * shader.  It is one of the possible values of the shaderType
+     * parameter.
+     */
+    public static final int SHADER_TYPE_FRAGMENT = 2;
+
+
+    /**
+     * Not a public constructor, for internal use
+     */
+    Shader() {
+    }
+
+    /**
+     * Package scope constructor so it can't be subclassed by classes
+     * outside the javax.media.j3d package.
+     */
+    Shader(int shadingLanguage, int shaderType) {
+	((ShaderRetained)this.retained).initializeShader(shadingLanguage, shaderType);
+    }
+
+    /**
+     * Returns the shading language of this shader.
+     *
+     * @return the  shading language of this shader, one of:
+     * <code>SHADING_LANGUAGE_GLSL</code> or
+     * <code>SHADING_LANGUAGE_CG</code>.
+     */
+    public int getShadingLanguage() {
+	return ((ShaderRetained)this.retained).getShadingLanguage();
+    }
+
+    /**
+     * Returns the type of this shader.
+     *
+     * @return the shader type, one of:
+     * <code>SHADER_TYPE_VERTEX</code> or
+     * <code>SHADER_TYPE_FRAGMENT</code>.
+     */
+    public int getShaderType() {
+	return ((ShaderRetained)this.retained).getShaderType();
+    }
+}
+
diff --git a/src/classes/share/javax/media/j3d/ShaderAppearance.java b/src/classes/share/javax/media/j3d/ShaderAppearance.java
new file mode 100644
index 0000000..2b9be8c
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderAppearance.java
@@ -0,0 +1,285 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Hashtable;
+
+/**
+ * <p>The ShaderAppearance object defines programmable shading attributes
+ * that can be set as a component object of a Shape3D node. The
+ * ShaderAppearance rendering state adds the following attributes in
+ * addition to those defined by Appearance:</p>
+ *
+ * <ul>
+ * <li>Shader program - specifies the shader program...</li>
+ *
+ * <p></p>
+ * <li>Shader attribute set - specifies the shader parameters, both as
+ * explicit attributes and as implicit bindings to Java 3D
+ * state...</li>
+ * </ul>
+ *
+ * <p>The ShaderAppearance object modifies the definition of some of the
+ * attributes in Appearance:</p>
+ *
+ * <ul>
+ * <li>Coloring attributes - XXXXX</li>
+ *
+ * <p></p>
+ * <li>Line attributes - XXXXX</li>
+ *
+ * <p></p>
+ * <li>Point attributes - XXXXX</li>
+ *
+ * <p></p>
+ * <li>Polygon attributes - XXXXX</li>
+ *
+ * <p></p>
+ * <li>Rendering attributes - XXXXX</li>
+ *
+ * <p></p>
+ * <li>Transparency attributes - XXXXX</li>
+ *
+ * <p></p>
+ * <li>Material - XXXXX</li>
+ *
+ * <p></p>
+ * <li>Texture - XXXXX</li>
+ *
+ * <p></p>
+ * <li>Texture attributes - XXXXX</li>
+ *
+ * <p></p>
+ * <li>Texture coordinate generation - XXXXX</li>
+ *
+ * <p></p>
+ * <li>Texture unit state - XXXXX</li>
+ * </ul>
+ *
+ * @see ShaderProgram
+ * @see ShaderAttributeSet
+ *
+ * @since Java 3D 1.4
+ */
+public class ShaderAppearance extends Appearance {
+    /**
+     * Specifies that this ShaderAppearance object allows reading its
+     * ShaderProgram component information.
+     */
+    public static final int
+	ALLOW_SHADER_PROGRAM_READ =
+	CapabilityBits.SHADER_APPEARANCE_ALLOW_SHADER_PROGRAM_READ;
+
+    /**
+     * Specifies that this ShaderAppearance object allows writing its
+     * ShaderProgram component information.
+     */
+    public static final int
+	ALLOW_SHADER_PROGRAM_WRITE =
+	CapabilityBits.SHADER_APPEARANCE_ALLOW_SHADER_PROGRAM_WRITE;
+
+    /**
+     * Specifies that this ShaderAppearance object allows reading its
+     * ShaderAttributeSet component information.
+     */
+    public static final int
+	ALLOW_SHADER_ATTRIBUTE_SET_READ =
+	CapabilityBits.SHADER_APPEARANCE_ALLOW_SHADER_ATTRIBUTE_SET_READ;
+
+    /**
+     * Specifies that this ShaderAppearance object allows writing its
+     * ShaderAttributeSet component information.
+     */
+    public static final int
+	ALLOW_SHADER_ATTRIBUTE_SET_WRITE =
+	CapabilityBits.SHADER_APPEARANCE_ALLOW_SHADER_ATTRIBUTE_SET_WRITE;
+
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+	ALLOW_SHADER_PROGRAM_READ,
+	ALLOW_SHADER_ATTRIBUTE_SET_READ
+    };
+
+    /**
+     * Constructs a ShaderAppearance component object using defaults for all
+     * state variables. All component object references are initialized
+     * to null.
+     */
+    public ShaderAppearance() {
+	// Just use default values
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+    }
+
+    /**
+     * Creates the retained mode ShaderAppearanceRetained object that this
+     * ShaderAppearance component object will point to.
+     */
+    void createRetained() {
+	this.retained = new ShaderAppearanceRetained();
+	this.retained.setSource(this);
+    }
+
+    /**
+     * Sets the ShaderProgram object to the specified object.  Setting it to
+     * null causes a default pass-through shader to be used ???
+     *
+     * @param shaderProgram object that specifies the desired shader program
+     * @exception CapabilityNotSetException if appropriate capability is
+     * not set and this object is part of live or compiled scene graph
+     */
+    public void setShaderProgram(ShaderProgram shaderProgram) {
+
+        if (isLiveOrCompiled()) {
+	  if(!this.getCapability(ALLOW_SHADER_PROGRAM_WRITE))
+	    throw new CapabilityNotSetException(J3dI18N.getString("ShaderAppearance0"));
+	}
+
+	((ShaderAppearanceRetained)this.retained).setShaderProgram(shaderProgram);
+	
+    }
+
+
+    /**
+     * Retrieves the current ShaderProgram object.
+     *
+     * @return the ShaderProgram object
+     * @exception CapabilityNotSetException if appropriate capability is
+     * not set and this object is part of live or compiled scene graph
+     */
+    public ShaderProgram getShaderProgram() {
+
+        if (isLiveOrCompiled()) {
+	    if(!this.getCapability(ALLOW_SHADER_PROGRAM_READ))
+		throw new CapabilityNotSetException(J3dI18N.getString("ShaderAppearance1"));
+	}
+	return ((ShaderAppearanceRetained)this.retained).getShaderProgram();
+    }
+
+
+    /**
+     * Sets the ShaderAttributeSet object to the specified object.  Setting it to
+     * null is equivalent to specifying an empty set of attributes.
+     *
+     * @param shaderAttributeSet object that specifies the desired shader attributes
+     * @exception CapabilityNotSetException if appropriate capability is
+     * not set and this object is part of live or compiled scene graph
+     */
+    public void setShaderAttributeSet(ShaderAttributeSet shaderAttributeSet) {
+        if (isLiveOrCompiled()) {
+	    if(!this.getCapability(ALLOW_SHADER_ATTRIBUTE_SET_WRITE))
+		throw new CapabilityNotSetException(J3dI18N.getString("ShaderAppearance2"));
+	}
+
+	((ShaderAppearanceRetained)this.retained).setShaderAttributeSet(shaderAttributeSet);
+    }
+
+
+    /**
+     * Retrieves the current ShaderAttributeSet object.
+     *
+     * @return the ShaderAttributeSet object
+     * @exception CapabilityNotSetException if appropriate capability is
+     * not set and this object is part of live or compiled scene graph
+     */
+    public ShaderAttributeSet getShaderAttributeSet() {
+        if (isLiveOrCompiled()) {
+	    if(!this.getCapability(ALLOW_SHADER_ATTRIBUTE_SET_READ))
+		throw new CapabilityNotSetException(J3dI18N.getString("ShaderAppearance3"));
+	}
+	return ((ShaderAppearanceRetained)this.retained).getShaderAttributeSet();
+    }
+
+
+   /**
+     * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+     */
+    public NodeComponent cloneNodeComponent() {
+        ShaderAppearance a = new ShaderAppearance();
+        a.duplicateNodeComponent(this);
+        return a;
+    }
+
+    /**
+     * NOTE: Applications should <i>not</i> call this method directly.
+     * It should only be called by the cloneNode method.
+     *
+     * @deprecated replaced with duplicateNodeComponent(
+     *  NodeComponent originalNodeComponent, boolean forceDuplicate)
+     */
+    public void duplicateNodeComponent(NodeComponent originalNodeComponent) {
+	checkDuplicateNodeComponent(originalNodeComponent);
+    }
+
+   /**
+     * Copies all ShaderAppearance information from
+     * <code>originalNodeComponent</code> into
+     * the current node.  This method is called from the
+     * <code>cloneNode</code> method which is, in turn, called by the
+     * <code>cloneTree</code> method.<P>
+     *
+     * @param originalNodeComponent the original node to duplicate.
+     * @param forceDuplicate when set to <code>true</code>, causes the
+     *  <code>duplicateOnCloneTree</code> flag to be ignored.  When
+     *  <code>false</code>, the value of each node's
+     *  <code>duplicateOnCloneTree</code> variable determines whether
+     *  NodeComponent data is duplicated or copied.
+     *
+     * @exception RestrictedAccessException if this object is part of a live
+     *  or compiled scenegraph.
+     *
+     * @see Node#cloneTree
+     * @see NodeComponent#setDuplicateOnCloneTree
+     */
+    void duplicateAttributes(NodeComponent originalNodeComponent,
+			     boolean forceDuplicate) {
+	super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+	Hashtable hashtable = originalNodeComponent.nodeHashtable;
+
+	ShaderAppearanceRetained app =
+	    (ShaderAppearanceRetained) originalNodeComponent.retained;
+
+	ShaderAppearanceRetained rt = (ShaderAppearanceRetained) retained;
+
+	rt.setShaderProgram((ShaderProgram) getNodeComponent(app.getShaderProgram(),
+				forceDuplicate,
+				hashtable));
+    }
+
+    /**
+     *  This function is called from getNodeComponent() to see if any of
+     *  the sub-NodeComponents  duplicateOnCloneTree flag is true.
+     *  If it is the case, current NodeComponent needs to
+     *  duplicate also even though current duplicateOnCloneTree flag is false.
+     *  This should be overwrite by NodeComponent which contains sub-NodeComponent.
+     */
+    boolean duplicateChild() {
+	if (super.duplicateChild())
+	    return true;
+
+	if (getDuplicateOnCloneTree())
+	    return true;
+
+	ShaderAppearanceRetained rt = (ShaderAppearanceRetained) retained;
+
+	NodeComponent nc;
+
+	nc = rt.getShaderProgram();
+	if ((nc != null) && nc.getDuplicateOnCloneTree())
+	    return true;
+
+	return false;
+    }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ShaderAppearanceRetained.java b/src/classes/share/javax/media/j3d/ShaderAppearanceRetained.java
new file mode 100644
index 0000000..b8a1936
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderAppearanceRetained.java
@@ -0,0 +1,360 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Vector;
+import java.util.BitSet;
+import java.util.ArrayList;
+
+
+/**
+ * The Appearance object defines all rendering state that can be set
+ * as a component object of a Shape3D node.
+ */
+class ShaderAppearanceRetained extends AppearanceRetained {
+
+    //
+    // State variables: these should all be initialized to approproate
+    // Java 3D defaults.
+    //
+ 
+    protected ShaderProgramRetained shaderProgram = null;
+    protected ShaderAttributeSetRetained shaderAttributeSet = null;
+    protected boolean isMirror = false; // For Debugging.
+
+    static final int SHADER_PROGRAM        = 0x0800;
+    static final int SHADER_ATTRIBUTE_SET  = 0x1000;    
+
+    /**
+     * Set the shader program object to the specified object.
+     * @param shaderProgram object that specifies the desired shader program
+     * and shader program attributes.
+     */
+    void setShaderProgram(ShaderProgram sp) {
+	synchronized(liveStateLock) {
+	    if (source.isLive()) {
+		// System.out.println("**** ShaderAppearceRetained.setShaderProgram()");
+
+		if (this.shaderProgram != null) {
+		    this.shaderProgram.clearLive(refCount);
+		    this.shaderProgram.removeMirrorUsers(this);
+		}
+
+		if (sp != null) {
+		    ((ShaderProgramRetained)sp.retained).setLive(inBackgroundGroup, 
+								 refCount);
+		    ((ShaderProgramRetained)sp.retained).copyMirrorUsers(this);
+	    	}
+		
+		sendMessage(SHADER_PROGRAM,  
+			    (sp != null ? ((ShaderProgramRetained)sp.retained).mirror : null));
+		
+	    }
+
+	    if (sp == null) {
+		this.shaderProgram = null;
+	    } else {
+		this.shaderProgram = (ShaderProgramRetained)sp.retained;
+	    }
+	}
+    }
+
+
+    /**
+     * Retrieves the current shader program object.
+     * @return current shader program object
+     */
+    ShaderProgram getShaderProgram() {
+	return (shaderProgram == null ? null : (ShaderProgram)shaderProgram.source);	
+    }
+
+
+    /**
+     * Sets the ShaderAttributeSet object to the specified object.  Setting it to
+     * null is equivalent to specifying an empty set of attributes.
+     *
+     * @param shaderAttributeSet object that specifies the desired shader attributes
+     */
+    void setShaderAttributeSet(ShaderAttributeSet sas) {
+	synchronized(liveStateLock) {
+	    if (source.isLive()) {
+		// System.out.println("**** ShaderAppearceRetained.setShaderAttributeSet()");
+
+		if (this.shaderAttributeSet != null) {
+		    this.shaderAttributeSet.clearLive(refCount);
+		    this.shaderAttributeSet.removeMirrorUsers(this);
+		}
+
+		if (sas != null) {
+		    ((ShaderAttributeSetRetained)sas.retained).setLive(inBackgroundGroup, 
+								       refCount);
+		    ((ShaderAttributeSetRetained)sas.retained).copyMirrorUsers(this);
+	    	}
+		
+		// System.out.println(" --   testing  needed!");
+		sendMessage(SHADER_ATTRIBUTE_SET,  
+			    (sas != null ? 
+			     ((ShaderAttributeSetRetained)sas.retained).mirror : null));
+		
+	    }
+	    
+	    if (sas == null) {
+		this.shaderAttributeSet = null;
+	    } else {
+		this.shaderAttributeSet = (ShaderAttributeSetRetained)sas.retained;
+	    }
+	}
+    }
+
+
+    /**
+     * Retrieves the current ShaderAttributeSet object.
+     * @return current ShaderAttributeSet object
+     */
+    ShaderAttributeSet getShaderAttributeSet() {
+	return (shaderAttributeSet == null ? null : (ShaderAttributeSet)shaderAttributeSet.source);	
+
+    }
+
+
+    public boolean equals(Object obj) {
+	System.out.println("ShaderAppearanceRetained :  equals() not tested yet!");
+	return ((obj instanceof ShaderAppearanceRetained) &&
+	 	equals((ShaderAppearanceRetained) obj));
+    }
+
+    boolean equals(ShaderAppearanceRetained sApp) {
+	boolean flag;
+	flag = (sApp == this);
+	
+	// If the reference is the same, we can stop check.
+	if(flag)
+	    return flag;
+
+	// Check each member's reference for equal.
+	flag = ((sApp != null) &&
+		(shaderProgram == sApp.shaderProgram)  &&
+		(shaderAttributeSet == sApp.shaderAttributeSet));
+	
+
+	if (!flag)
+	    return flag;
+	
+	return super.equals(sApp);
+
+    }
+
+    
+
+    synchronized void createMirrorObject() {
+	// System.out.println("ShaderAppearanceRetained : createMirrorObject()");
+
+	if (mirror == null) {
+	    // we can't check isStatic() since it sub-NodeComponent
+	    // create a new one, we should create a
+	    // new AppearanceRetained() even though isStatic() = true.
+	    // For simplicity, always create a retained side.
+	    mirror = new ShaderAppearanceRetained();
+	    ((ShaderAppearanceRetained)mirror).isMirror = true; // For Debugging.
+	}
+	initMirrorObject();
+    }
+
+    /**
+     * This routine updates the mirror appearance for this appearance.
+     * It also calls the update method for each node component if it
+     * is not null.
+     */
+    synchronized void initMirrorObject() {
+	// System.out.println("ShaderAppearanceRetained : initMirrorObject()");
+
+	super.initMirrorObject();
+
+	ShaderAppearanceRetained mirrorApp = (ShaderAppearanceRetained)mirror;
+
+	if(shaderProgram != null) {
+	    mirrorApp.shaderProgram = (ShaderProgramRetained)shaderProgram.mirror;
+	}
+	else {
+	    mirrorApp.shaderProgram = null;	    
+	}
+
+	if(shaderAttributeSet != null) {
+	    mirrorApp.shaderAttributeSet = 
+		(ShaderAttributeSetRetained)shaderAttributeSet.mirror;
+	}
+	else {
+	    // System.out.println("shaderAttributeSet is null");
+	    mirrorApp.shaderAttributeSet = null;
+	}
+
+    }
+
+  /**
+   * Update the "component" field of the mirror object with the
+   *  given "value"
+   */
+    synchronized void updateMirrorObject(int component, Object value) {
+
+	// System.out.println("ShaderAppearanceRetained : updateMirrorObject() this " + this);
+	super.updateMirrorObject(component, value);
+ 	ShaderAppearanceRetained mirrorApp = (ShaderAppearanceRetained)mirror;
+	if ((component & SHADER_PROGRAM) != 0) {
+	    mirrorApp.shaderProgram = (ShaderProgramRetained)value;
+	}
+	else if ((component & SHADER_ATTRIBUTE_SET) != 0) {
+	    mirrorApp.shaderAttributeSet = (ShaderAttributeSetRetained)value;
+	}
+	
+    }
+
+    /**
+     * This method calls the setLive method of all appearance bundle
+     * objects.
+     */
+    void doSetLive(boolean backgroundGroup, int refCount) {
+	// System.out.println("ShaderAppearceRetained.doSetLive()");
+
+
+	if (shaderProgram != null) {
+	    shaderProgram.setLive(backgroundGroup, refCount);
+	}
+
+	if (shaderAttributeSet != null) {
+	    shaderAttributeSet.setLive(backgroundGroup, refCount);
+	}
+
+
+	// Increment the reference count and initialize the appearance
+	// mirror object
+        super.doSetLive(backgroundGroup, refCount);
+    }
+
+
+    /**
+     * This clearLive routine first calls the superclass's method, then
+     * it removes itself to the list of lights
+     */
+    void clearLive(int refCount) {
+	super.clearLive(refCount);
+
+	if (shaderProgram != null) {
+	    shaderProgram.clearLive(refCount);
+	}
+
+	if (shaderAttributeSet != null) {
+	    shaderAttributeSet.clearLive(refCount);
+	}
+    }
+
+    synchronized void addAMirrorUser(Shape3DRetained shape) {
+
+	super.addAMirrorUser(shape);
+	if (shaderProgram != null) 
+	    shaderProgram.addAMirrorUser(shape);
+        if (shaderAttributeSet != null) 
+	    shaderAttributeSet.addAMirrorUser(shape);
+    }
+    
+    synchronized void removeAMirrorUser(Shape3DRetained shape) {
+        
+	super.removeAMirrorUser(shape);
+	if (shaderProgram != null) 
+	    shaderProgram.removeAMirrorUser(shape);
+        if (shaderAttributeSet != null) 
+	    shaderAttributeSet.removeAMirrorUser(shape);
+    }
+    
+    
+    final void sendMessage(int attrMask, Object attr) {
+	ArrayList univList = new ArrayList();
+	ArrayList gaList = Shape3DRetained.getGeomAtomsList(mirror.users, univList);  
+	// Send to rendering attribute structure, regardless of
+	// whether there are users or not (alternate appearance case ..)
+	J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+	createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+	createMessage.type = J3dMessage.SHADER_APPEARANCE_CHANGED;
+        createMessage.universe = null;
+	createMessage.args[0] = this;
+	createMessage.args[1]= new Integer(attrMask);
+	createMessage.args[2] = attr;
+	createMessage.args[3] = new Integer(changedFrequent);
+
+	VirtualUniverse.mc.processMessage(createMessage);
+	    
+	//System.out.println("univList.size is " + univList.size());
+	for(int i=0; i<univList.size(); i++) {
+	    createMessage = VirtualUniverse.mc.getMessage();
+	    createMessage.threads = J3dThread.UPDATE_RENDER;
+	    createMessage.type = J3dMessage.SHADER_APPEARANCE_CHANGED;
+		
+	    createMessage.universe = (VirtualUniverse) univList.get(i);
+	    createMessage.args[0] = this;
+	    createMessage.args[1]= new Integer(attrMask);
+	    createMessage.args[2] = attr;
+
+	    ArrayList gL = (ArrayList) gaList.get(i);
+	    GeometryAtom[] gaArr = new GeometryAtom[gL.size()];
+	    gL.toArray(gaArr);
+	    createMessage.args[3] = gaArr;
+
+	    VirtualUniverse.mc.processMessage(createMessage);
+	}
+    }
+
+    
+    boolean isStatic() {
+	if (!super.isStatic()) {
+	    return false;
+	}
+
+	boolean flag =
+	    source.capabilityBitsEmpty() &&
+	    ((shaderProgram == null) ||
+	     shaderProgram.source.capabilityBitsEmpty()) &&
+	    ((shaderAttributeSet == null) ||
+	     shaderAttributeSet.source.capabilityBitsEmpty());
+
+ 	return flag;
+    }
+
+
+    
+    boolean isOpaque(int geoType) {
+	
+	if (!super.isOpaque(geoType)) {
+	    return false;
+	}
+	
+	// TODO: IMPLEMENT THIS
+	// TODO: How do we determine whether a ShaderAppearance is opaque???
+	return true;
+    }
+
+    void handleFrequencyChange(int bit) {
+	// System.out.println("ShaderAppearanceRetained : handleFrequencyChange()");
+	super.handleFrequencyChange(bit);
+
+	int mask = 0;
+	if (bit == ShaderAppearance.ALLOW_SHADER_PROGRAM_WRITE)
+	    mask = SHADER_PROGRAM;
+	else if (bit == ShaderAppearance.ALLOW_SHADER_ATTRIBUTE_SET_WRITE)
+	    mask = SHADER_ATTRIBUTE_SET;
+	
+
+	if (mask != 0)
+	    setFrequencyChangeMask(bit, mask);
+    }
+}
+
+
diff --git a/src/classes/share/javax/media/j3d/ShaderAttribute.java b/src/classes/share/javax/media/j3d/ShaderAttribute.java
new file mode 100644
index 0000000..7618492
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderAttribute.java
@@ -0,0 +1,77 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * The ShaderAttribute object encapsulates a uniform attribute for a
+ * shader programs.  Uniform attributes (variables) are those
+ * attributes whose values are constant during the rendering of a
+ * primitive. Their values may change from primitive to primitive, but
+ * are constant for each vertex (for vertex shaders) or fragment (for
+ * fragment shaders) of a single primitive. Examples of uniform
+ * attributes include a transformation matrix, a texture map, lights,
+ * lookup tables, etc.
+ *
+ * <p>
+ * There are two ways in which values can be specified for uniform
+ * attributes: explicitly, by providing a value; and implicitly, by
+ * defining a binding between a Java 3D system attribute and a uniform
+ * attribute. This functionality is provided by two subclasses of
+ * ShaderAttribute as follows:
+ *
+ * <ul>
+ * <li>ShaderAttributeObject, in which attributes are expressed as
+ * <code>(attrName,&nbsp;value)</code> pairs, is used for explicitly
+ * defined attributes</li>
+ * <li>ShaderAttributeBinding, in which attributes are expressed as
+ * <code>(attrName,&nbsp;j3dAttrName)</code> pairs, is used for
+ * implicitly defined, automatically tracked attributes</li>
+ * </ul>
+ *
+ * @see ShaderAttributeSet
+ * @see ShaderProgram
+ *
+ * @since Java 3D 1.4
+ */
+
+public abstract class ShaderAttribute extends NodeComponent {
+    /**
+     * Name of the shader attribute (immutable)
+     */
+
+    /**
+     * Package scope constructor
+     *
+     */
+    ShaderAttribute(String attrName) {   
+	if (attrName == null) {
+	    throw new NullPointerException();
+	}
+
+	((ShaderAttributeRetained)this.retained).initializeAttrName(attrName);
+    }
+
+    /**
+     * Retrieves the name of this shader attribute.
+     *
+     * @return the name of this shader attribute
+     */
+    public String getAttributeName() {
+
+ 	return ((ShaderAttributeRetained)this.retained).getAttributeName();
+ 
+   }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ShaderAttributeArray.java b/src/classes/share/javax/media/j3d/ShaderAttributeArray.java
new file mode 100644
index 0000000..c5ee3db
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderAttributeArray.java
@@ -0,0 +1,147 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * The ShaderAttributeArray object encapsulates a uniform shader
+ * attribute whose value is specified explicitly. The shader variable
+ * <code>attrName</code> is explicitly set to the specified
+ * <code>value</code> during rendering. <code>attrName</code> must be
+ * the name of a valid uniform attribute in the shader in which it is
+ * used. Otherwise, the attribute name will be ignored and a runtime
+ * error may be generated. The <code>value</code> must be an array
+ * of one of the allowed classes. The allowed classes are:
+ * <code>Integer[]</code>, <code>Float[]</code>,
+ * <code>Tuple{2,3,4}{i,f}[]</code>, <code>Matrix{3,4}f[]</code>. A
+ * ClassCastException will be thrown if a specified <code>value</code>
+ * object is not one of the allowed types. Further, the type and length of the
+ * value is immutable once a ShaderAttributeArray is constructed.
+ * Subsequent setValue operations must be called with an array of the
+ * same type and length as the one that was used to construct the
+ * ShaderAttributeArray. Finally, the type of the <code>value</code>
+ * object must match the type of the corresponding
+ * <code>attrName</code> variable in the shader in which it is
+ * used. Otherwise, the shader will not be able to use the attribute
+ * and a runtime error may be generated.
+ *
+ * @see ShaderAttributeSet
+ * @see ShaderProgram
+ *
+ * @since Java 3D 1.4
+ */
+
+public class ShaderAttributeArray extends ShaderAttributeObject {
+    /**
+     * Constructs a new ShaderAttributeArray object with the specified
+     * <code>(attrName,&nbsp;value)</code> pair. The specified value
+     * must be an array of one of the allowed class types.
+     * A deep copy of the array is stored.
+     *
+     * @param attrName the name of the shader attribute
+     * @param value the value of the shader attribute
+     *
+     * @exception NullPointerException if attrName or value is null
+     *
+     * @exception ClassCastException if value is not an array of
+     * one of the allowed classes
+     */
+    public ShaderAttributeArray(String attrName, Object value) {
+	super(attrName, value);
+    }
+
+    // Implement abstract getValue method
+    public Object getValue() {
+        if (isLiveOrCompiled())
+	    if (!this.getCapability(ALLOW_VALUE_READ))
+		throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeObject0"));
+
+ 	return ((ShaderAttributeArrayRetained)this.retained).getValue();
+    }
+
+    // Implement abstract setValue method
+    public void setValue(Object value) {
+	if (value == null) {
+	    throw new NullPointerException();
+	}
+
+        if (isLiveOrCompiled())
+	    if (!this.getCapability(ALLOW_VALUE_WRITE))
+		throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeObject1"));
+	
+	if (isLive())
+	    ((ShaderAttributeArrayRetained)this.retained).setValue(value);
+	else
+	    ((ShaderAttributeArrayRetained)this.retained).initValue(value);
+
+    }
+
+
+    /**
+     * Sets the specified array element of the value of this shader
+     * attribute to the specified value.
+     * A copy of the object is stored.
+     *
+     * @param value the new value of the shader attribute
+     *
+     * @exception NullPointerException if value is null
+     *
+     * @exception ClassCastException if value is not an instance of
+     * the same base class as the individual elements of the array object
+     * used to construct this shader attribute object.
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     */
+    public void setValue(int index, Object value) {
+	if (value == null) {
+	    throw new NullPointerException();
+	}
+
+        if (isLiveOrCompiled())
+	    if (!this.getCapability(ALLOW_VALUE_WRITE))
+		throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeObject1"));
+
+	if (isLive())
+	    ((ShaderAttributeArrayRetained)this.retained).setValue(index, value);
+	else {
+	    ((ShaderAttributeArrayRetained)this.retained).initValue(index, value);
+	}
+    }
+
+    /**
+     * Returns the number of elements in the value array.
+     *
+     * @return the number of elements in the value array
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     */
+    public int length() {
+        if (isLiveOrCompiled())
+	    if (!this.getCapability(ALLOW_VALUE_READ))
+		throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeObject0"));
+
+        return ((ShaderAttributeArrayRetained)this.retained).length();
+    }
+
+    /**
+     * Creates a retained mode ShaderAttributeArrayRetained object that this
+     * ShaderAttributeArray component object will point to.
+     */
+    void createRetained() {
+	this.retained = new ShaderAttributeArrayRetained();
+	this.retained.setSource(this);
+    }
+}
diff --git a/src/classes/share/javax/media/j3d/ShaderAttributeArrayRetained.java b/src/classes/share/javax/media/j3d/ShaderAttributeArrayRetained.java
new file mode 100644
index 0000000..f4d4a42
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderAttributeArrayRetained.java
@@ -0,0 +1,996 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * The ShaderAttributeArray object encapsulates a uniform shader
+ * attribute whose value is specified explicitly. The shader variable
+ * <code>attrName</code> is explicitly set to the specified
+ * <code>value</code> during rendering. <code>attrName</code> must be
+ * the name of a valid uniform attribute in the shader in which it is
+ * used. Otherwise, the attribute name will be ignored and a runtime
+ * error may be generated. The <code>value</code> must be an array
+ * of one of the allowed classes. The allowed classes are:
+ * <code>Integer[]</code>, <code>Float[]</code>,
+ * <code>Tuple{2,3,4}{i,f}[]</code>, <code>Matrix{3,4}f[]</code>. A
+ * ClassCastException will be thrown if a specified <code>value</code>
+ * object is not one of the allowed types. Further, the type and length of the
+ * value is immutable once a ShaderAttributeArray is constructed.
+ * Subsequent setValue operations must be called with an array of the
+ * same type and length as the one that was used to construct the
+ * ShaderAttributeArray. Finally, the type of the <code>value</code>
+ * object must match the type of the corresponding
+ * <code>attrName</code> variable in the shader in which it is
+ * used. Otherwise, the shader will not be able to use the attribute
+ * and a runtime error may be generated.
+ *
+ * @see ShaderAttributeSet
+ * @see ShaderProgram
+ *
+ * @since Java 3D 1.4
+ */
+
+class ShaderAttributeArrayRetained extends ShaderAttributeObjectRetained {
+
+    ShaderAttributeArrayRetained() {
+    }
+
+    void initValue(int index, Object value) {
+	/*
+	System.err.println("ShaderAttributeObjectRetained : attrName = " + attrName +
+			   ", index = " + index + ", value = " + value +
+			   ", value.class = " + value.getClass());
+	*/
+	((ArrayWrapper)attrWrapper).set(index, value);
+
+    }
+
+    
+    /**
+     * Sets the specified array element of the value of this shader
+     * attribute to the specified value.
+     * A copy of the object is stored.
+     *
+     * @param value the new value of the shader attribute
+     *
+     * @exception NullPointerException if value is null
+     *
+     * @exception ClassCastException if value is not an instance of
+     * the same base class as the individual elements of the array object
+     * used to construct this shader attribute object.
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     */
+    void setValue(int index, Object value) {
+	initValue(index, value);
+	// We should only need to update the array instead of replacing it.
+	// Until this become a really bottleneck, it will just be a convenience 
+	// method for end user. 
+	// An efficient approach is to 
+	// (1) Create a new ShaderAttributeValue object for the "value" object 
+	// and pass it to sendMessage(), (2) Create a new sendMessage that take in
+	// a third arguement, ie. index.
+	setValue(attrWrapper.getRef());
+    }
+
+    /**
+     * Returns the number of elements in the value array.
+     *
+     * @return the number of elements in the value array
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     */
+    int length() {
+	return ((ArrayWrapper)attrWrapper).length();
+
+    }
+
+    // Helper methods ...
+
+    synchronized void createMirrorObject() {
+	// System.out.println("ShaderAttributeArrayRetained : createMirrorObject");
+        // This method should only call by setLive().
+	if (mirror == null) {
+            ShaderAttributeArrayRetained mirrorSAA = new ShaderAttributeArrayRetained();
+	    mirrorSAA.createObjectData(getValue());
+	    mirror = mirrorSAA;
+	    mirror.source = source;
+	    
+	}
+	initMirrorObject();
+    }
+
+
+    /**
+     * Computes the base class from the specified object. A
+     * ClassCastException is thrown if the object is not an array of
+     * one of the allowed classes.
+     */
+    int computeClassType(Object value) {
+	Class objClass = value.getClass();
+	if (!objClass.isArray()) {
+	    throw new ClassCastException(objClass + " -- must be array class");
+	}
+
+	for (int i = 0; i < classTable.length; i++) {
+	    if (classTableArr[i].isInstance(value)) {
+		return i;
+	    }
+	}
+	throw new ClassCastException(objClass + " -- unrecognized class");
+    }
+
+    /**
+     * Returns the base class represented by the specified class type.
+     */
+    Class getBaseClass(int classType) {
+	return classTableArr[classType];
+    }
+
+    /**
+     * Creates an attribute wrapper object of the specified class
+     * type, and stores the specified array of objects.
+     */
+    AttrWrapper createAttrWrapper(Object value, int classType) {
+	ArrayWrapper attrWrapper = null;
+	switch (classType) {
+	case TYPE_INTEGER:
+	    attrWrapper = new IntegerArrayWrapper();
+	    break;
+	case TYPE_FLOAT:
+	    attrWrapper = new FloatArrayWrapper();
+	    break;
+//	case TYPE_DOUBLE:
+//	    attrWrapper = new DoubleArrayWrapper();
+//	    break;
+	case TYPE_TUPLE2I:
+	    attrWrapper = new Tuple2iArrayWrapper();
+	    break;
+	case TYPE_TUPLE2F:
+	    attrWrapper = new Tuple2fArrayWrapper();
+	    break;
+//	case TYPE_TUPLE2D:
+//	    attrWrapper = new Tuple2dArrayWrapper();
+//	    break;
+	case TYPE_TUPLE3I:
+	    attrWrapper = new Tuple3iArrayWrapper();
+	    break;
+	case TYPE_TUPLE3F:
+	    attrWrapper = new Tuple3fArrayWrapper();
+	    break;
+//	case TYPE_TUPLE3D:
+//	    attrWrapper = new Tuple3dArrayWrapper();
+//	    break;
+	case TYPE_TUPLE4I:
+	    attrWrapper = new Tuple4iArrayWrapper();
+	    break;
+	case TYPE_TUPLE4F:
+	    attrWrapper = new Tuple4fArrayWrapper();
+	    break;
+//	case TYPE_TUPLE4D:
+//	    attrWrapper = new Tuple4dArrayWrapper();
+//	    break;
+	case TYPE_MATRIX3F:
+	    attrWrapper = new Matrix3fArrayWrapper();
+	    break;
+//	case TYPE_MATRIX3D:
+//	    attrWrapper = new Matrix3dArrayWrapper();
+//	    break;
+	case TYPE_MATRIX4F:
+	    attrWrapper = new Matrix4fArrayWrapper();
+	    break;
+//	case TYPE_MATRIX4D:
+//	    attrWrapper = new Matrix4dArrayWrapper();
+//	    break;
+	default:
+	    // Should never get here
+	    assert false;
+	    return null;
+	}
+
+	attrWrapper.set(value);
+	return attrWrapper;
+    }
+
+
+    //
+    // The following wrapper classes are used to store a copy of the
+    // user-specified shader attribute value. There is a wrapper class
+    // for each supported base class.
+    //
+
+    // Base wrapper class for array attribute types
+    static abstract class ArrayWrapper extends AttrWrapper {
+	int length = 0;
+
+	/**
+	 * Returns the length of the array
+	 */
+	int length() {
+	    return length;
+	}
+
+	/**
+	 * Sets the specified array element of the value of this
+	 * shader attribute to the specified value.
+	 */
+	abstract void set(int index, Object value);
+    }
+
+    // Wrapper class for Integer
+    static class IntegerArrayWrapper extends ArrayWrapper {
+	private int[] value = new int[0];
+
+	void set(Object value) {
+	    Integer[] arr = (Integer[])value;
+	    if (this.length != arr.length) {
+		this.length = arr.length;
+		this.value = new int[this.length];
+	    }
+	    for (int i = 0; i < this.length; i++) {
+		this.value[i] = arr[i].intValue();
+	    }
+	}
+
+	void set(int index, Object value) {
+	    this.value[index] = ((Integer)value).intValue();
+	}
+
+	Object get() {
+	    Integer[] arr = new Integer[this.length];
+	    for (int i = 0; i < this.length; i++) {
+		arr[i] = new Integer(this.value[i]);
+	    }
+	    return arr;
+	}
+
+	Object getRef() {
+	    return this.value;
+	}
+    }
+
+    // Wrapper class for Float
+    static class FloatArrayWrapper extends ArrayWrapper {
+	private float[] value = new float[0];
+
+	void set(Object value) {
+	    Float[] arr = (Float[])value;
+	    if (this.length != arr.length) {
+		this.length = arr.length;
+		this.value = new float[this.length];
+	    }
+	    for (int i = 0; i < this.length; i++) {
+		this.value[i] = arr[i].floatValue();
+	    }
+	}
+
+	void set(int index, Object value) {
+	    this.value[index] = ((Float)value).floatValue();
+	}
+
+	Object get() {
+	    Float[] arr = new Float[this.length];
+	    for (int i = 0; i < this.length; i++) {
+		arr[i] = new Float(this.value[i]);
+	    }
+	    return arr;
+	}
+
+	Object getRef() {
+	    return this.value;
+	}
+    }
+
+    /*
+    // Wrapper class for Double
+    static class DoubleArrayWrapper extends ArrayWrapper {
+	private double[] value = new double[0];
+
+	void set(Object value) {
+	    Double[] arr = (Double[])value;
+	    if (this.length != arr.length) {
+		this.length = arr.length;
+		this.value = new double[this.length];
+	    }
+	    for (int i = 0; i < this.length; i++) {
+		this.value[i] = arr[i].doubleValue();
+	    }
+	}
+
+	void set(int index, Object value) {
+	    this.value[index] = ((Double)value).doubleValue();
+	}
+
+	Object get() {
+	    Double[] arr = new Double[this.length];
+	    for (int i = 0; i < this.length; i++) {
+		arr[i] = new Double(this.value[i]);
+	    }
+	    return arr;
+	}
+
+	Object getRef() {
+	    return this.value;
+	}
+    }
+    */
+
+    // Wrapper class for Tuple2i
+    static class Tuple2iArrayWrapper extends ArrayWrapper {
+	private int[] value = new int[0];
+
+	void set(Object value) {
+	    Tuple2i[] arr = (Tuple2i[])value;
+	    if (this.length != arr.length) {
+		this.length = arr.length;
+		this.value = new int[this.length*2];
+	    }
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 2;
+		this.value[j+0] = arr[i].x;
+		this.value[j+1] = arr[i].y;
+	    }
+	}
+
+	void set(int index, Object value) {
+	    int j = index * 2;
+	    this.value[j+0] = ((Tuple2i)value).x;
+	    this.value[j+1] = ((Tuple2i)value).y;
+	}
+
+	Object get() {
+	    Tuple2i[] arr = new Tuple2i[this.length];
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 2;
+		arr[i].x = this.value[j+0];
+		arr[i].y = this.value[j+1];
+	    }
+	    return arr;
+	}
+
+	Object getRef() {
+	    return this.value;
+	}
+    }
+
+    // Wrapper class for Tuple2f
+    static class Tuple2fArrayWrapper extends ArrayWrapper {
+	private float[] value = new float[0];
+
+	void set(Object value) {
+	    Tuple2f[] arr = (Tuple2f[])value;
+	    if (this.length != arr.length) {
+		this.length = arr.length;
+		this.value = new float[this.length*2];
+	    }
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 2;
+		this.value[j+0] = arr[i].x;
+		this.value[j+1] = arr[i].y;
+	    }
+	}
+
+	void set(int index, Object value) {
+	    int j = index * 2;
+	    this.value[j+0] = ((Tuple2f)value).x;
+	    this.value[j+1] = ((Tuple2f)value).y;
+	}
+
+	Object get() {
+	    Tuple2f[] arr = new Tuple2f[this.length];
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 2;
+		arr[i].x = this.value[j+0];
+		arr[i].y = this.value[j+1];
+	    }
+	    return arr;
+	}
+
+	Object getRef() {
+	    return this.value;
+	}
+    }
+
+    /*
+    // Wrapper class for Tuple2d
+    static class Tuple2dArrayWrapper extends ArrayWrapper {
+	private double[] value = new double[0];
+
+	void set(Object value) {
+	    Tuple2d[] arr = (Tuple2d[])value;
+	    if (this.length != arr.length) {
+		this.length = arr.length;
+		this.value = new double[this.length*2];
+	    }
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 2;
+		this.value[j+0] = arr[i].x;
+		this.value[j+1] = arr[i].y;
+	    }
+	}
+
+	void set(int index, Object value) {
+	    int j = index * 2;
+	    this.value[j+0] = ((Tuple2d)value).x;
+	    this.value[j+1] = ((Tuple2d)value).y;
+	}
+
+	Object get() {
+	    Tuple2d[] arr = new Tuple2d[this.length];
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 2;
+		arr[i].x = this.value[j+0];
+		arr[i].y = this.value[j+1];
+	    }
+	    return arr;
+	}
+
+	Object getRef() {
+	    return this.value;
+	}
+    }
+    */
+
+    // Wrapper class for Tuple3i
+    static class Tuple3iArrayWrapper extends ArrayWrapper {
+	private int[] value = new int[0];
+
+	void set(Object value) {
+	    Tuple3i[] arr = (Tuple3i[])value;
+	    if (this.length != arr.length) {
+		this.length = arr.length;
+		this.value = new int[this.length*3];
+	    }
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 3;
+		this.value[j+0] = arr[i].x;
+		this.value[j+1] = arr[i].y;
+		this.value[j+2] = arr[i].z;
+	    }
+	}
+
+	void set(int index, Object value) {
+	    int j = index * 3;
+	    this.value[j+0] = ((Tuple3i)value).x;
+	    this.value[j+1] = ((Tuple3i)value).y;
+	    this.value[j+2] = ((Tuple3i)value).z;
+	}
+
+	Object get() {
+	    Tuple3i[] arr = new Tuple3i[this.length];
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 3;
+		arr[i].x = this.value[j+0];
+		arr[i].y = this.value[j+1];
+		arr[i].z = this.value[j+2];
+	    }
+	    return arr;
+	}
+
+	Object getRef() {
+	    return this.value;
+	}
+    }
+
+    // Wrapper class for Tuple3f
+    static class Tuple3fArrayWrapper extends ArrayWrapper {
+	private float[] value = new float[0];
+
+	void set(Object value) {
+	    Tuple3f[] arr = (Tuple3f[])value;
+	    if (this.length != arr.length) {
+		this.length = arr.length;
+		this.value = new float[this.length*3];
+	    }
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 3;
+		this.value[j+0] = arr[i].x;
+		this.value[j+1] = arr[i].y;
+		this.value[j+2] = arr[i].z;
+	    }
+	}
+
+	void set(int index, Object value) {
+	    int j = index * 3;
+	    this.value[j+0] = ((Tuple3f)value).x;
+	    this.value[j+1] = ((Tuple3f)value).y;
+	    this.value[j+2] = ((Tuple3f)value).z;
+	}
+
+	Object get() {
+	    Tuple3f[] arr = new Tuple3f[this.length];
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 3;
+		arr[i].x = this.value[j+0];
+		arr[i].y = this.value[j+1];
+		arr[i].z = this.value[j+2];
+	    }
+	    return arr;
+	}
+
+	Object getRef() {
+	    return this.value;
+	}
+    }
+
+    /*
+    // Wrapper class for Tuple3d
+    static class Tuple3dArrayWrapper extends ArrayWrapper {
+	private double[] value = new double[0];
+
+	void set(Object value) {
+	    Tuple3d[] arr = (Tuple3d[])value;
+	    if (this.length != arr.length) {
+		this.length = arr.length;
+		this.value = new double[this.length*3];
+	    }
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 3;
+		this.value[j+0] = arr[i].x;
+		this.value[j+1] = arr[i].y;
+		this.value[j+2] = arr[i].z;
+	    }
+	}
+
+	void set(int index, Object value) {
+	    int j = index * 3;
+	    this.value[j+0] = ((Tuple3d)value).x;
+	    this.value[j+1] = ((Tuple3d)value).y;
+	    this.value[j+2] = ((Tuple3d)value).z;
+	}
+
+	Object get() {
+	    Tuple3d[] arr = new Tuple3d[this.length];
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 3;
+		arr[i].x = this.value[j+0];
+		arr[i].y = this.value[j+1];
+		arr[i].z = this.value[j+2];
+	    }
+	    return arr;
+	}
+
+	Object getRef() {
+	    return this.value;
+	}
+    }
+    */
+
+    // Wrapper class for Tuple4i
+    static class Tuple4iArrayWrapper extends ArrayWrapper {
+	private int[] value = new int[0];
+
+	void set(Object value) {
+	    Tuple4i[] arr = (Tuple4i[])value;
+	    if (this.length != arr.length) {
+		this.length = arr.length;
+		this.value = new int[this.length*4];
+	    }
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 4;
+		this.value[j+0] = arr[i].x;
+		this.value[j+1] = arr[i].y;
+		this.value[j+2] = arr[i].z;
+		this.value[j+3] = arr[i].w;
+	    }
+	}
+
+	void set(int index, Object value) {
+	    int j = index * 4;
+	    this.value[j+0] = ((Tuple4i)value).x;
+	    this.value[j+1] = ((Tuple4i)value).y;
+	    this.value[j+2] = ((Tuple4i)value).z;
+	    this.value[j+3] = ((Tuple4i)value).w;
+	}
+
+	Object get() {
+	    Tuple4i[] arr = new Tuple4i[this.length];
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 4;
+		arr[i].x = this.value[j+0];
+		arr[i].y = this.value[j+1];
+		arr[i].z = this.value[j+2];
+		arr[i].w = this.value[j+3];
+	    }
+	    return arr;
+	}
+
+	Object getRef() {
+	    return this.value;
+	}
+    }
+
+    // Wrapper class for Tuple4f
+    static class Tuple4fArrayWrapper extends ArrayWrapper {
+	private float[] value = new float[0];
+
+	void set(Object value) {
+	    Tuple4f[] arr = (Tuple4f[])value;
+	    if (this.length != arr.length) {
+		this.length = arr.length;
+		this.value = new float[this.length*4];
+	    }
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 4;
+		this.value[j+0] = arr[i].x;
+		this.value[j+1] = arr[i].y;
+		this.value[j+2] = arr[i].z;
+		this.value[j+3] = arr[i].w;
+	    }
+	}
+
+	void set(int index, Object value) {
+	    int j = index * 4;
+	    this.value[j+0] = ((Tuple4f)value).x;
+	    this.value[j+1] = ((Tuple4f)value).y;
+	    this.value[j+2] = ((Tuple4f)value).z;
+	    this.value[j+3] = ((Tuple4f)value).w;
+	}
+
+	Object get() {
+	    Tuple4f[] arr = new Tuple4f[this.length];
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 4;
+		arr[i].x = this.value[j+0];
+		arr[i].y = this.value[j+1];
+		arr[i].z = this.value[j+2];
+		arr[i].w = this.value[j+3];
+	    }
+	    return arr;
+	}
+
+	Object getRef() {
+	    return this.value;
+	}
+    }
+
+    /*
+    // Wrapper class for Tuple4d
+    static class Tuple4dArrayWrapper extends ArrayWrapper {
+	private double[] value = new double[0];
+
+	void set(Object value) {
+	    Tuple4d[] arr = (Tuple4d[])value;
+	    if (this.length != arr.length) {
+		this.length = arr.length;
+		this.value = new double[this.length*4];
+	    }
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 4;
+		this.value[j+0] = arr[i].x;
+		this.value[j+1] = arr[i].y;
+		this.value[j+2] = arr[i].z;
+		this.value[j+3] = arr[i].w;
+	    }
+	}
+
+	void set(int index, Object value) {
+	    int j = index * 4;
+	    this.value[j+0] = ((Tuple4d)value).x;
+	    this.value[j+1] = ((Tuple4d)value).y;
+	    this.value[j+2] = ((Tuple4d)value).z;
+	    this.value[j+3] = ((Tuple4d)value).w;
+	}
+
+	Object get() {
+	    Tuple4d[] arr = new Tuple4d[this.length];
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 4;
+		arr[i].x = this.value[j+0];
+		arr[i].y = this.value[j+1];
+		arr[i].z = this.value[j+2];
+		arr[i].w = this.value[j+3];
+	    }
+	    return arr;
+	}
+
+	Object getRef() {
+	    return this.value;
+	}
+    }
+    */
+
+    // Wrapper class for Matrix3f
+    static class Matrix3fArrayWrapper extends ArrayWrapper {
+	private float[] value = new float[0];
+
+	void set(Object value) {
+	    Matrix3f[] arr = (Matrix3f[])value;
+	    if (this.length != arr.length) {
+		this.length = arr.length;
+		this.value = new float[this.length * 9];
+	    }
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 9;
+		this.value[j+0] = arr[i].m00;
+		this.value[j+1] = arr[i].m01;
+		this.value[j+2] = arr[i].m02;
+		this.value[j+3] = arr[i].m10;
+		this.value[j+4] = arr[i].m11;
+		this.value[j+5] = arr[i].m12;
+		this.value[j+6] = arr[i].m20;
+		this.value[j+7] = arr[i].m21;
+		this.value[j+8] = arr[i].m22;
+	    }
+	}
+
+	void set(int index, Object value) {
+	    int j = index * 9;
+	    Matrix3f m = (Matrix3f)value;
+
+	    this.value[j+0] = m.m00;
+	    this.value[j+1] = m.m01;
+	    this.value[j+2] = m.m02;
+	    this.value[j+3] = m.m10;
+	    this.value[j+4] = m.m11;
+	    this.value[j+5] = m.m12;
+	    this.value[j+6] = m.m20;
+	    this.value[j+7] = m.m21;
+	    this.value[j+8] = m.m22;
+	}
+
+	Object get() {
+	    Matrix3f[] arr = new Matrix3f[this.length];
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 9;
+		arr[i].m00 = this.value[j+0];
+		arr[i].m01 = this.value[j+1];
+		arr[i].m02 = this.value[j+2];
+		arr[i].m10 = this.value[j+3];
+		arr[i].m11 = this.value[j+4];
+		arr[i].m12 = this.value[j+5];
+		arr[i].m20 = this.value[j+6];
+		arr[i].m21 = this.value[j+7];
+		arr[i].m22 = this.value[j+8];
+	    }
+	    return arr;
+	}
+
+	Object getRef() {
+	    return this.value;
+	}
+    }
+
+    /*
+    // Wrapper class for Matrix3d
+    static class Matrix3dArrayWrapper extends ArrayWrapper {
+	private double[] value = new double[0];
+
+	void set(Object value) {
+	    Matrix3d[] arr = (Matrix3d[])value;
+	    if (this.length != arr.length) {
+		this.length = arr.length;
+		this.value = new double[this.length * 9];
+	    }
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 9;
+		this.value[j+0] = arr[i].m00;
+		this.value[j+1] = arr[i].m01;
+		this.value[j+2] = arr[i].m02;
+		this.value[j+3] = arr[i].m10;
+		this.value[j+4] = arr[i].m11;
+		this.value[j+5] = arr[i].m12;
+		this.value[j+6] = arr[i].m20;
+		this.value[j+7] = arr[i].m21;
+		this.value[j+8] = arr[i].m22;
+	    }
+	}
+
+	void set(int index, Object value) {
+	    int j = index * 9;
+	    Matrix3d m = (Matrix3d)value;
+
+	    this.value[j+0] = m.m00;
+	    this.value[j+1] = m.m01;
+	    this.value[j+2] = m.m02;
+	    this.value[j+3] = m.m10;
+	    this.value[j+4] = m.m11;
+	    this.value[j+5] = m.m12;
+	    this.value[j+6] = m.m20;
+	    this.value[j+7] = m.m21;
+	    this.value[j+8] = m.m22;
+	}
+
+	Object get() {
+	    Matrix3d[] arr = new Matrix3d[this.length];
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 9;
+		arr[i].m00 = this.value[j+0];
+		arr[i].m01 = this.value[j+1];
+		arr[i].m02 = this.value[j+2];
+		arr[i].m10 = this.value[j+3];
+		arr[i].m11 = this.value[j+4];
+		arr[i].m12 = this.value[j+5];
+		arr[i].m20 = this.value[j+6];
+		arr[i].m21 = this.value[j+7];
+		arr[i].m22 = this.value[j+8];
+	    }
+	    return arr;
+	}
+
+	Object getRef() {
+	    return this.value;
+	}
+    }
+    */
+
+    // Wrapper class for Matrix4f
+    static class Matrix4fArrayWrapper extends ArrayWrapper {
+	private float[] value = new float[0];
+
+	void set(Object value) {
+	    Matrix4f[] arr = (Matrix4f[])value;
+	    if (this.length != arr.length) {
+		this.length = arr.length;
+		this.value = new float[this.length * 16];
+	    }
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 16;
+		this.value[j+0]  = arr[i].m00;
+		this.value[j+1]  = arr[i].m01;
+		this.value[j+2]  = arr[i].m02;
+		this.value[j+3]  = arr[i].m03;
+		this.value[j+4]  = arr[i].m10;
+		this.value[j+5]  = arr[i].m11;
+		this.value[j+6]  = arr[i].m12;
+		this.value[j+7]  = arr[i].m13;
+		this.value[j+8]  = arr[i].m20;
+		this.value[j+9]  = arr[i].m21;
+		this.value[j+10] = arr[i].m22;
+		this.value[j+11] = arr[i].m23;
+		this.value[j+12] = arr[i].m30;
+		this.value[j+13] = arr[i].m31;
+		this.value[j+14] = arr[i].m32;
+		this.value[j+15] = arr[i].m33;
+	    }
+	}
+
+	void set(int index, Object value) {
+	    int j = index * 16;
+	    Matrix4f m = (Matrix4f)value;
+
+	    this.value[j+0]  = m.m00;
+	    this.value[j+1]  = m.m01;
+	    this.value[j+2]  = m.m02;
+	    this.value[j+3]  = m.m03;
+	    this.value[j+4]  = m.m10;
+	    this.value[j+5]  = m.m11;
+	    this.value[j+6]  = m.m12;
+	    this.value[j+7]  = m.m13;
+	    this.value[j+8]  = m.m20;
+	    this.value[j+9]  = m.m21;
+	    this.value[j+10] = m.m22;
+	    this.value[j+11] = m.m23;
+	    this.value[j+12] = m.m30;
+	    this.value[j+13] = m.m31;
+	    this.value[j+14] = m.m32;
+	    this.value[j+15] = m.m33;
+	}
+
+	Object get() {
+	    Matrix4f[] arr = new Matrix4f[this.length];
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 16;
+		arr[i].m00 = this.value[j+0];
+		arr[i].m01 = this.value[j+1];
+		arr[i].m02 = this.value[j+2];
+		arr[i].m03 = this.value[j+3];
+		arr[i].m10 = this.value[j+4];
+		arr[i].m11 = this.value[j+5];
+		arr[i].m12 = this.value[j+6];
+		arr[i].m13 = this.value[j+7];
+		arr[i].m20 = this.value[j+8];
+		arr[i].m21 = this.value[j+9];
+		arr[i].m22 = this.value[j+10];
+		arr[i].m23 = this.value[j+11];
+		arr[i].m30 = this.value[j+12];
+		arr[i].m31 = this.value[j+13];
+		arr[i].m32 = this.value[j+14];
+		arr[i].m33 = this.value[j+15];
+	    }
+	    return arr;
+	}
+
+	Object getRef() {
+	    return this.value;
+	}
+    }
+
+    /*
+    // Wrapper class for Matrix4d
+    static class Matrix4dArrayWrapper extends ArrayWrapper {
+	private double[] value = new double[0];
+
+	void set(Object value) {
+	    Matrix4d[] arr = (Matrix4d[])value;
+	    if (this.length != arr.length) {
+		this.length = arr.length;
+		this.value = new double[this.length * 16];
+	    }
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 16;
+		this.value[j+0]  = arr[i].m00;
+		this.value[j+1]  = arr[i].m01;
+		this.value[j+2]  = arr[i].m02;
+		this.value[j+3]  = arr[i].m03;
+		this.value[j+4]  = arr[i].m10;
+		this.value[j+5]  = arr[i].m11;
+		this.value[j+6]  = arr[i].m12;
+		this.value[j+7]  = arr[i].m13;
+		this.value[j+8]  = arr[i].m20;
+		this.value[j+9]  = arr[i].m21;
+		this.value[j+10] = arr[i].m22;
+		this.value[j+11] = arr[i].m23;
+		this.value[j+12] = arr[i].m30;
+		this.value[j+13] = arr[i].m31;
+		this.value[j+14] = arr[i].m32;
+		this.value[j+15] = arr[i].m33;
+	    }
+	}
+
+	void set(int index, Object value) {
+	    int j = index * 16;
+	    Matrix4d m = (Matrix4d)value;
+
+	    this.value[j+0]  = m.m00;
+	    this.value[j+1]  = m.m01;
+	    this.value[j+2]  = m.m02;
+	    this.value[j+3]  = m.m03;
+	    this.value[j+4]  = m.m10;
+	    this.value[j+5]  = m.m11;
+	    this.value[j+6]  = m.m12;
+	    this.value[j+7]  = m.m13;
+	    this.value[j+8]  = m.m20;
+	    this.value[j+9]  = m.m21;
+	    this.value[j+10] = m.m22;
+	    this.value[j+11] = m.m23;
+	    this.value[j+12] = m.m30;
+	    this.value[j+13] = m.m31;
+	    this.value[j+14] = m.m32;
+	    this.value[j+15] = m.m33;
+	}
+
+	Object get() {
+	    Matrix4d[] arr = new Matrix4d[this.length];
+	    for (int i = 0; i < this.length; i++) {
+		int j = i * 16;
+		arr[i].m00 = this.value[j+0];
+		arr[i].m01 = this.value[j+1];
+		arr[i].m02 = this.value[j+2];
+		arr[i].m03 = this.value[j+3];
+		arr[i].m10 = this.value[j+4];
+		arr[i].m11 = this.value[j+5];
+		arr[i].m12 = this.value[j+6];
+		arr[i].m13 = this.value[j+7];
+		arr[i].m20 = this.value[j+8];
+		arr[i].m21 = this.value[j+9];
+		arr[i].m22 = this.value[j+10];
+		arr[i].m23 = this.value[j+11];
+		arr[i].m30 = this.value[j+12];
+		arr[i].m31 = this.value[j+13];
+		arr[i].m32 = this.value[j+14];
+		arr[i].m33 = this.value[j+15];
+	    }
+	    return arr;
+	}
+
+	Object getRef() {
+	    return this.value;
+	}
+    }
+    */
+}
diff --git a/src/classes/share/javax/media/j3d/ShaderAttributeBinding.java b/src/classes/share/javax/media/j3d/ShaderAttributeBinding.java
new file mode 100644
index 0000000..608fbfc
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderAttributeBinding.java
@@ -0,0 +1,128 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * The ShaderAttributeBinding object encapsulates a uniform attribute
+ * whose value is bound to a Java&nbsp;3D system attribute. The
+ * shader variable <code>attrName</code> is implicitly set to the
+ * value of the corresponding Java&nbsp;3D system attribute
+ * <code>j3dAttrName</code> during rendering. <code>attrName</code>
+ * must be the name of a valid uniform attribute in the shader in
+ * which it is used. Otherwise, the attribute name will be ignored and
+ * a runtime error may be generated. <code>j3dAttrName</code> must be
+ * the name of a predefined Java&nbsp;3D system attribute. An
+ * IllegalArgumentException will be thrown if the specified
+ * <code>j3dAttrName</code> is not one of the predefined system
+ * attributes. Further, the type of the <code>j3dAttrName</code>
+ * attribute must match the type of the corresponding
+ * <code>attrName</code> variable in the shader in which it is
+ * used. Otherwise, the shader will not be able to use the attribute
+ * and a runtime error may be generated.
+ *
+ * <p>
+ * Following is the list of predefined Java&nbsp;3D system attributes:<br>
+ *
+ * <ul>
+ * <font color="#ff0000"><i>TODO: replace the following with
+ * the real system attributes table</i></font><br>
+ * <table BORDER=1 CELLSPACING=2 CELLPADDING=2>
+ * <tr>
+ * <td><b>Name</b></td>
+ * <td><b>Type</b></td>
+ * <td><b>Description</b></td>
+ * </tr>
+ * <tr>
+ * <td><code>something</code></td>
+ * <td>Float</td>
+ * <td>This is something (of course)</td>
+ * </tr>
+ * <tr>
+ * <td><code>somethingElse</code></td>
+ * <td>Tuple3f</td>
+ * <td>This is something else</td>
+ * </tr>
+ * </table>
+ * </ul>
+ *
+ * <p>
+ * Depending on the shading language (and profile) being used, several
+ * Java 3D state attributes are automatically made available to the
+ * shader program as pre-defined uniform attributes. The application
+ * doesn't need to do anything to pass these attributes in to the
+ * shader program. The implementation of each shader language (e.g.,
+ * Cg, GLSL) defines its own mapping from Java 3D attribute to uniform
+ * variable name.
+ *
+ * <p>
+ * A list of these attributes for each shader language can be found in
+ * the concrete subclass of ShaderProgram for that shader language.
+ *
+ * <p>
+ * <font color="#ff0000"><i>NOTE: This class is not yet
+ * implemented.</i></font><br>
+ *
+ * @see ShaderAttributeSet
+ * @see ShaderProgram
+ *
+ * @since Java 3D 1.4
+ */
+
+public class ShaderAttributeBinding extends ShaderAttribute {
+
+    /**
+     * Constructs a new ShaderAttributeBinding from the specified
+     * <code>(attrName,&nbsp;j3dAttrName)</code> pair.
+     *
+     * @param attrName the name of the shader attribute to be added
+     * @param j3dAttrName the name of the Java&nbsp;3D attribute
+     * to bind to the shader attribute
+     *
+     * @exception UnsupportedOperationException this class is not
+     * yet implemented
+     *
+     * @exception NullPointerException if attrName or j3dAttrName is null
+     *
+     * @exception IllegalArgumentException if j3dAttrName is not the name
+     * of a valid predefined Java&nbsp;3D system attribute
+     */
+    public ShaderAttributeBinding(String attrName, String j3dAttrName) {
+	super(attrName);
+	((ShaderAttributeBindingRetained)this.retained).initJ3dAttrName(j3dAttrName);
+	// TODO: implement this class
+	throw new UnsupportedOperationException(J3dI18N.getString("ShaderAttributeBinding0"));
+    }
+
+    /**
+     * Retrieves the name of the Java 3D system attribute that is bound to this
+     * shader attribute.
+     *
+     * @return the name of the Java 3D system attribute that is bound to this
+     * shader attribute
+     */
+    public String getJ3DAttributeName() {
+ 	return ((ShaderAttributeBindingRetained)this.retained).getJ3DAttributeName();
+    }
+
+    /**
+     * Creates a retained mode ShaderAttributeBindingRetained object that this
+     * ShaderAttributeBinding component object will point to.
+     */
+    void createRetained() {
+	this.retained = new ShaderAttributeBindingRetained();
+	this.retained.setSource(this);
+    }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ShaderAttributeBindingRetained.java b/src/classes/share/javax/media/j3d/ShaderAttributeBindingRetained.java
new file mode 100644
index 0000000..b580406
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderAttributeBindingRetained.java
@@ -0,0 +1,57 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * The ShaderAttributeBinding object encapsulates a uniform attribute
+ * whose value is bound to a Java&nbsp;3D system attribute. The
+ * shader variable <code>attrName</code> is implicitly set to the
+ * value of the corresponding Java&nbsp;3D system attribute
+ * <code>j3dAttrName</code> during rendering. <code>attrName</code>
+ * must be the name of a valid uniform attribute in the shader in
+ * which it is used. Otherwise, the attribute name will be ignored and
+ * a runtime error may be generated. <code>j3dAttrName</code> must be
+ * the name of a predefined Java&nbsp;3D system attribute. An
+ * IllegalArgumentException will be thrown if the specified
+ * <code>j3dAttrName</code> is not one of the predefined system
+ * attributes. Further, the type of the <code>j3dAttrName</code>
+ * attribute must match the type of the corresponding
+ * <code>attrName</code> variable in the shader in which it is
+ * used. Otherwise, the shader will not be able to use the attribute
+ * and a runtime error may be generated.
+ */
+
+class ShaderAttributeBindingRetained extends ShaderAttributeRetained {
+    String j3dAttrName;
+
+    ShaderAttributeBindingRetained() {
+    }
+
+    void initJ3dAttrName(String j3dAttrName) {
+	this.j3dAttrName = j3dAttrName;
+    }
+
+    /**
+     * Retrieves the name of the Java 3D system attribute that is bound to this
+     * shader attribute.
+     *
+     * @return the name of the Java 3D system attribute that is bound to this
+     * shader attribute
+     */
+    String getJ3DAttributeName() {
+	return j3dAttrName;
+    }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ShaderAttributeObject.java b/src/classes/share/javax/media/j3d/ShaderAttributeObject.java
new file mode 100644
index 0000000..b3a275d
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderAttributeObject.java
@@ -0,0 +1,130 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * The ShaderAttributeObject class is an abstract class that
+ * encapsulates a uniform shader attribute whose value is specified
+ * explicitly. This class has concrete subclasses for single-value
+ * attributes (ShaderAttributeValue) and array attributes
+ * (ShaderAttributeArray). The shader variable <code>attrName</code>
+ * is explicitly set to the specified <code>value</code> during
+ * rendering. <code>attrName</code> must be the name of a valid
+ * uniform attribute in the shader in which it is used. Otherwise, the
+ * attribute name will be ignored and a runtime error may be
+ * generated. The <code>value</code> must be an instance of one of the
+ * allowed classes or an array of one the allowed classes. The allowed
+ * classes are: <code>Integer</code>, <code>Float</code>,
+ * <code>Tuple{2,3,4}{i,f}</code>,
+ * <code>Matrix{3,4}f</code>. A ClassCastException will be thrown
+ * if a specified <code>value</code> object is not one of the allowed
+ * types. Further, the type of the value is immutable once a
+ * ShaderAttributeObject is constructed.  Subsequent setValue
+ * operations must be called with an object of the same type as the
+ * one that was used to construct the ShaderAttributeObject. Finally,
+ * the type of the <code>value</code> object must match the type of
+ * the corresponding <code>attrName</code> variable in the shader in
+ * which it is used. Otherwise, the shader will not be able to use the
+ * attribute and a runtime error may be generated.
+ *
+ * @see ShaderAttributeSet
+ * @see ShaderProgram
+ *
+ * @since Java 3D 1.4
+ */
+
+public abstract class ShaderAttributeObject extends ShaderAttribute {
+
+    /**
+     * Specifies that this ShaderAttributeObject allows reading its value.
+     */
+    public static final int
+	ALLOW_VALUE_READ =
+	CapabilityBits.SHADER_ATTRIBUTE_OBJECT_ALLOW_VALUE_READ;
+
+    /**
+     * Specifies that this ShaderAttributeObject allows writing its value.
+     */
+    public static final int
+	ALLOW_VALUE_WRITE =
+	CapabilityBits.SHADER_ATTRIBUTE_OBJECT_ALLOW_VALUE_WRITE;
+
+
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+	ALLOW_VALUE_READ
+    };
+
+
+    /**
+     * Package scope constructor
+     */
+    ShaderAttributeObject(String attrName, Object value) {
+	super(attrName);	
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+	((ShaderAttributeObjectRetained)this.retained).createObjectData(value);
+    }
+
+
+    /**
+     * Retrieves the value of this shader attribute.
+     * A copy of the object is returned.
+     *
+     * @return a copy of the value of this shader attribute
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     */
+    public abstract Object getValue();
+
+    /**
+     * Sets the value of this shader attribute to the specified value.
+     * A copy of the object is stored.
+     *
+     * @param value the new value of the shader attribute
+     *
+     * @exception NullPointerException if value is null
+     *
+     * @exception ClassCastException if value is not an instance of
+     * the same base class as the object used to construct this shader
+     * attribute object.
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     */
+    public abstract void setValue(Object value);
+
+    /**
+     * Retrieves the base class of the value of this shader attribute.
+     * This class will always be one of the allowable classes, even if
+     * a subclass was used to construct this shader attribute object.
+     * For example, if this shader attribute object was constructed
+     * with an instance of <code>javax.vecmath.Point3f</code>, the
+     * returned class would be <code>javax.vecmath.Tuple3f</code>.
+     *
+     * @return the base class of the value of this shader attribute
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     */
+    public Class getValueClass() {
+
+ 	return ((ShaderAttributeObjectRetained)this.retained).getValueClass();
+    }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ShaderAttributeObjectRetained.java b/src/classes/share/javax/media/j3d/ShaderAttributeObjectRetained.java
new file mode 100644
index 0000000..a66335c
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderAttributeObjectRetained.java
@@ -0,0 +1,311 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.ArrayList;
+import javax.vecmath.*;
+
+/**
+ * The ShaderAttributeObjectRetained class is an abstract class that
+ * encapsulates a uniform shader attribute whose value is specified
+ * explicitly. This class has concrete subclasses for single-value
+ * attributes (ShaderAttributeValueRetained) and array attributes
+ * (ShaderAttributeArrayRetained). The shader variable <code>attrName</code>
+ * is explicitly set to the specified <code>value</code> during
+ * rendering. <code>attrName</code> must be the name of a valid
+ * uniform attribute in the shader in which it is used. Otherwise, the
+ * attribute name will be ignored and a runtime error may be
+ * generated. The <code>value</code> must be an instance of one of the
+ * allowed classes or an array of one the allowed classes. The allowed
+ * classes are: <code>Integer</code>, <code>Float</code>,
+ * <code>Tuple{2,3,4}{i,f}</code>,
+ * <code>Matrix{3,4}f</code>. A ClassCastException will be thrown
+ * if a specified <code>value</code> object is not one of the allowed
+ * types. Further, the type of the value is immutable once a
+ * ShaderAttributeObjectRetained is constructed.  Subsequent setValue
+ * operations must be called with an object of the same type as the
+ * one that was used to construct the ShaderAttributeObjectRetained. 
+ * Finally, the type of the <code>value</code> object must match the type 
+ * of the corresponding <code>attrName</code> variable in the shader in
+ * which it is used. Otherwise, the shader will not be able to use the
+ * attribute and a runtime error may be generated.
+ *
+ * @see ShaderAttributeSetRetained
+ * @see ShaderProgramRetained
+ *
+ * @since Java 3D 1.4
+ */
+
+abstract class ShaderAttributeObjectRetained extends ShaderAttributeRetained {
+
+    private int classType;
+    private Class baseClass;
+    AttrWrapper attrWrapper;
+
+    /**
+     * Package scope constructor
+     */
+    ShaderAttributeObjectRetained() {
+    }
+
+    void createObjectData(Object value) {
+        
+  	classType = computeClassType(value);
+	baseClass = getBaseClass(classType);
+	attrWrapper = createAttrWrapper(value, classType);      
+ 	/*
+	System.err.println("    classType = " + classType +
+			   ", baseClass = " + baseClass +
+			   ", attrWrapper.get() = " + attrWrapper.get());
+	*/   
+    }
+    
+    
+    void initValue(Object value) {
+	/*
+	System.err.println("ShaderAttributeObjectRetained : attrName = " + attrName +
+			   ", value = " + value +
+			   ", value.class = " + value.getClass());
+	*/
+	attrWrapper.set(value);
+
+    }
+
+    /**
+     * Retrieves the value of this shader attribute.
+     * A copy of the object is returned.
+     */
+    Object getValue() {
+	return attrWrapper.get();
+    }
+
+    /**
+     * Sets the value of this shader attribute to the specified value.
+     * A copy of the object is stored.
+     *
+     * @param value the new value of the shader attribute
+     *
+     * @exception NullPointerException if value is null
+     *
+     * @exception ClassCastException if value is not an instance of
+     * the same base class as the object used to construct this shader
+     * attribute object.
+     *
+     */
+    void setValue(Object value) {
+        initValue(value);
+	AttrWrapper valueWrapper = createAttrWrapper(value, this.classType);	
+	sendMessage(SHADER_ATTRIBUTE_VALUE_UPDATE, valueWrapper);
+    }
+
+    /**
+     * Retrieves the base class of the value of this shader attribute.
+     * This class will always be one of the allowable classes, even if
+     * a subclass was used to construct this shader attribute object.
+     * For example, if this shader attribute object was constructed
+     * with an instance of <code>javax.vecmath.Point3f</code>, the
+     * returned class would be <code>javax.vecmath.Tuple3f</code>.
+     *
+     * @return the base class of the value of this shader attribute
+     */
+    Class getValueClass() {
+	return baseClass;
+    }
+    
+   /**
+     * Initializes a mirror object.
+     */
+    synchronized void initMirrorObject() {
+	super.initMirrorObject();        
+	((ShaderAttributeObjectRetained)mirror).initValue(getValue());
+    }
+    
+     /**
+     * Update the "component" field of the mirror object with the  given "value"
+     */
+    synchronized void updateMirrorObject(int component, Object value) {
+
+	//System.out.println("ShaderAttributeObjectRetained : updateMirrorObject");
+	ShaderAttributeObjectRetained mirrorSAV = (ShaderAttributeObjectRetained)mirror;
+        if ((component & SHADER_ATTRIBUTE_VALUE_UPDATE) != 0) {
+	    //System.out.println("     -- SHADER_ATTRIBUTE_VALUE_UPDATE");
+	    mirrorSAV.attrWrapper = (AttrWrapper) value;
+	}
+    }
+    
+    final void sendMessage(int attrMask, Object attr) {
+	
+	ArrayList univList = new ArrayList();
+	ArrayList gaList = Shape3DRetained.getGeomAtomsList(mirror.users, univList);  
+
+	// Send to rendering attribute structure, regardless of
+	// whether there are users or not (alternate appearance case ..)
+	J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+	createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+	createMessage.type = J3dMessage.SHADER_ATTRIBUTE_CHANGED;
+	createMessage.universe = null;
+	createMessage.args[0] = this;
+	createMessage.args[1]= new Integer(attrMask);
+	createMessage.args[2] = attr;
+	//	System.out.println("changedFreqent1 = "+changedFrequent);
+	createMessage.args[3] = new Integer(changedFrequent);
+	VirtualUniverse.mc.processMessage(createMessage);
+
+	// System.out.println("univList.size is " + univList.size());
+	for(int i=0; i<univList.size(); i++) {
+	    createMessage = VirtualUniverse.mc.getMessage();
+	    createMessage.threads = J3dThread.UPDATE_RENDER;
+	    createMessage.type = J3dMessage.SHADER_ATTRIBUTE_CHANGED;
+		
+	    createMessage.universe = (VirtualUniverse) univList.get(i);
+	    createMessage.args[0] = this;
+	    createMessage.args[1]= new Integer(attrMask);
+	    createMessage.args[2] = attr;
+
+	    ArrayList gL = (ArrayList)gaList.get(i);
+	    GeometryAtom[] gaArr = new GeometryAtom[gL.size()];
+	    gL.toArray(gaArr);
+	    createMessage.args[3] = gaArr;  
+	    
+	    VirtualUniverse.mc.processMessage(createMessage);
+	}
+
+    }
+        
+        
+    // Enumerated types representing allowed classes for shader
+    // attributes.
+    //
+    // NOTE that the values for these enums are used as an index into
+    // the tables of classes, so the values must start at 0 and
+    // increment by 1. Also, the order must be the same as the order
+    // of the entries in each of the two class tables.
+    static final int TYPE_INTEGER  =  0;
+    static final int TYPE_FLOAT    =  1;
+    static final int TYPE_TUPLE2I  =  2;
+    static final int TYPE_TUPLE2F  =  3;
+    static final int TYPE_TUPLE3I  =  4;
+    static final int TYPE_TUPLE3F  =  5;
+    static final int TYPE_TUPLE4I  =  6;
+    static final int TYPE_TUPLE4F  =  7;
+    static final int TYPE_MATRIX3F =  8;
+    static final int TYPE_MATRIX4F =  9;
+
+    // Double-precision is not supported in the current version. Uncomment the
+    // following if future support is done.
+//    static final int TYPE_DOUBLE   = 10;
+//    static final int TYPE_TUPLE2D  = 11;
+//    static final int TYPE_TUPLE3D  = 12;
+//    static final int TYPE_TUPLE4D  = 13;
+//    static final int TYPE_MATRIX3D = 14;
+//    static final int TYPE_MATRIX4D = 15;
+
+    static final Class classTable[] = {
+	Integer.class,
+	Float.class,
+	Tuple2i.class,
+	Tuple2f.class,
+	Tuple3i.class,
+	Tuple3f.class,
+	Tuple4i.class,
+	Tuple4f.class,
+	Matrix3f.class,
+	Matrix4f.class,
+
+        // Double-precision is not supported in the current version. Uncomment the
+        // following if future support is done.
+//	Double.class,
+//	Tuple2d.class,
+//	Tuple3d.class,
+//	Tuple4d.class,
+//	Matrix3d.class,
+//	Matrix4d.class,
+    };
+
+    static final Class classTableArr[] = {
+	Integer[].class,
+	Float[].class,
+	Tuple2i[].class,
+	Tuple2f[].class,
+	Tuple3i[].class,
+	Tuple3f[].class,
+	Tuple4i[].class,
+	Tuple4f[].class,
+	Matrix3f[].class,
+	Matrix4f[].class,
+
+        // Double-precision is not supported in the current version. Uncomment the
+        // following if future support is done.
+//	Double[].class,
+//	Tuple2d[].class,
+//	Tuple3d[].class,
+//	Tuple4d[].class,
+//	Matrix3d[].class,
+//	Matrix4d[].class,
+    };
+
+
+    /**
+     * Computes the base class from the specified object. A
+     * ClassCastException is thrown if the object is not an instance
+     * or array of one of the allowed classes.
+     */
+    abstract int computeClassType(Object value);
+
+    /**
+     * Returns the base class represented by the specified class type.
+     */
+    abstract Class getBaseClass(int classType);
+
+    /**
+     * Creates an attribute wrapper object of the specified class
+     * type, and stores the specified object.
+     */
+    abstract AttrWrapper createAttrWrapper(Object value, int classType);
+
+
+    /**
+     * Base wrapper class for subclasses that are used to store a copy
+     * of the user-specified shader attribute value. There is a
+     * wrapper class for each supported base class in ShaderAttributeValue
+     * and ShaderAttributeArray. The value is stored in a Java primitive array.
+     */
+    static abstract class AttrWrapper {
+	/**
+	 * Stores a copy of the specified object in the wrapper object
+	 */
+	abstract void set(Object value);
+
+	/**
+	 * Returns a copy of the wrapped object
+	 */
+	abstract Object get();
+
+	/**
+	 * Returns a reference to the internal primitive array used to
+	 * wrap the object; note that the caller of this method must
+	 * treat the data as read-only. It is intended only as a means
+	 * to pass data down to native methods.
+	 */
+	abstract Object getRef();
+    }
+
+    int getClassType() {
+        return classType;
+    }
+
+    void setClassType(int classType) {
+        this.classType = classType;
+    }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ShaderAttributeRetained.java b/src/classes/share/javax/media/j3d/ShaderAttributeRetained.java
new file mode 100644
index 0000000..64cbdc0
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderAttributeRetained.java
@@ -0,0 +1,82 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * The ShaderAttributeRetained object encapsulates a uniform attribute for a
+ * shader programs.  Uniform attributes (variables) are those
+ * attributes whose values are constant during the rendering of a
+ * primitive. Their values may change from primitive to primitive, but
+ * are constant for each vertex (for vertex shaders) or fragment (for
+ * fragment shaders) of a single primitive. Examples of uniform
+ * attributes include a transformation matrix, a texture map, lights,
+ * lookup tables, etc.
+ *
+ * <p>
+ * There are two ways in which values can be specified for uniform
+ * attributes: explicitly, by providing a value; and implicitly, by
+ * defining a binding between a Java 3D system attribute and a uniform
+ * attribute. This functionality is provided by two subclasses of
+ * ShaderAttributeRetained as follows:
+ *
+ * <ul>
+ * <li>ShaderAttributeObjectRetained, in which attributes are expressed as
+ * <code>(attrName,&nbsp;value)</code> pairs, is used for explicitly
+ * defined attributes</li>
+ * <li>ShaderAttributeBindingRetained, in which attributes are expressed as
+ * <code>(attrName,&nbsp;j3dAttrName)</code> pairs, is used for
+ * implicitly defined, automatically tracked attributes</li>
+ * </ul>
+ *
+ * @see ShaderAttributeSetRetained
+ * @see ShaderProgramRetained
+ *
+ * @since Java 3D 1.4
+ */
+
+abstract class ShaderAttributeRetained extends NodeComponentRetained {
+    // A list of pre-defined bits to indicate which component
+    // in this ShaderAttribute object changed.
+    static final int SHADER_ATTRIBUTE_VALUE_UPDATE            = 0x001;
+    
+    /**
+     * Name of the shader attribute (immutable)
+     */
+    String attrName;
+
+    /**
+     * Package scope constructor
+     */
+    ShaderAttributeRetained() {
+    }
+
+    void initializeAttrName(String attrName) {
+	this.attrName = attrName;
+    }
+
+    /**
+     * Retrieves the name of this shader attribute.
+     *
+     * @return the name of this shader attribute
+     */
+    String getAttributeName() {
+	return attrName;
+    }
+
+    void initMirrorObject() {
+	((ShaderAttributeObjectRetained)mirror).initializeAttrName(this.attrName);
+    }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ShaderAttributeSet.java b/src/classes/share/javax/media/j3d/ShaderAttributeSet.java
new file mode 100644
index 0000000..ad75b11
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderAttributeSet.java
@@ -0,0 +1,263 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import javax.vecmath.*;
+
+/**
+ * The ShaderAttributeSet object provides uniform attributes to shader
+ * programs. Uniform attributes (variables) are those attributes whose
+ * values are constant during the rendering of a primitive. Their
+ * values may change from primitive to primitive, but are constant for
+ * each vertex (for vertex shaders) or fragment (for fragment shaders)
+ * of a single primitive. Examples of uniform attributes include a
+ * transformation matrix, a texture map, lights, lookup tables, etc.
+ * The ShaderAttributeSet object contains a set of ShaderAttribute
+ * objects. Each ShaderAttribute object defines the value of a single
+ * uniform shader variable. The set of attributes is unique with respect
+ * to attribute names: no two attributes in the set will have the same
+ * name.
+ *
+ * <p>
+ * There are two ways in which values can be specified for uniform
+ * attributes: explicitly, by providing a value; and implicitly, by
+ * defining a binding between a Java 3D system attribute and a uniform
+ * attribute. This functionality is provided by two subclasses of
+ * ShaderAttribute: ShaderAttributeObject, which is used to specify
+ * explicitly defined attributes; and ShaderAttributeBinding, which is
+ * used to specify implicitly defined, automatically tracked attributes.
+ *
+ * <p>
+ * Depending on the shading language (and profile) being used, several
+ * Java 3D state attributes are automatically made available to the
+ * shader program as pre-defined uniform attributes. The application
+ * doesn't need to do anything to pass these attributes in to the
+ * shader program. The implementation of each shader language (e.g.,
+ * Cg, GLSL) defines its own bindings from Java 3D attribute to uniform
+ * variable name. A list of these attributes for each shader language
+ * can be found in the concrete subclass of ShaderProgram for that
+ * shader language.
+ *
+ * @see ShaderAttribute
+ * @see ShaderProgram
+ * @see ShaderAppearance#setShaderAttributeSet
+ *
+ * @since Java 3D 1.4
+ */
+
+public class ShaderAttributeSet extends NodeComponent {
+
+    /**
+     * Specifies that this ShaderAttributeSet object allows reading
+     * its attributes.
+     */
+    public static final int
+	ALLOW_ATTRIBUTES_READ =
+	CapabilityBits.SHADER_ATTRIBUTE_SET_ALLOW_ATTRIBUTES_READ;
+
+    /**
+     * Specifies that this ShaderAttributeSet object allows writing
+     * its attributes.
+     */
+    public static final int
+	ALLOW_ATTRIBUTES_WRITE =
+	CapabilityBits.SHADER_ATTRIBUTE_SET_ALLOW_ATTRIBUTES_WRITE;
+
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_ATTRIBUTES_READ
+    };
+    
+    /**
+     * Constructs an empty ShaderAttributeSet object. The attributes set
+     * is initially empty.
+     */
+    public ShaderAttributeSet() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+    }
+
+    //
+    // Methods for dealing with the (name, value) pairs for explicit
+    // attributes
+    //
+
+    /**
+     * Adds the specified shader attribute to the attributes set.
+     * The newly specified attribute replaces an attribute with the
+     * same name, if one already exists in the attributes set.
+     *
+     * @param attr the shader attribute to be added to the set
+     *
+     * @exception NullPointerException if attr is null
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     */
+    public void put(ShaderAttribute attr) {
+	if (attr == null) {
+	    throw new NullPointerException();
+	}
+
+        if (isLiveOrCompiled())
+	    if (!this.getCapability(ALLOW_ATTRIBUTES_WRITE))
+		throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeSet1"));
+	
+        ((ShaderAttributeSetRetained)this.retained).put(attr);
+
+    }
+
+    /**
+     * Retrieves the shader attribute with the specified
+     * <code>attrName</code> from the attributes set. If attrName does
+     * not exist in the attributes set, null is returned.
+     *
+     * @param attrName the name of the shader attribute to be retrieved
+     *
+     * @exception NullPointerException if attrName is null
+     *
+     * @return a the shader attribute associated with the specified
+     * attribute name, or null if the name is not in the attributes
+     * set
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     */
+    public ShaderAttribute get(String attrName) {
+
+ 	if (attrName == null) {
+	    throw new NullPointerException();
+	}
+
+        if (isLiveOrCompiled())
+	    if (!this.getCapability(ALLOW_ATTRIBUTES_READ))
+		throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeSet0"));
+
+	return ((ShaderAttributeSetRetained)this.retained).get(attrName);
+    }
+
+    /**
+     * Removes the shader attribute with the specified
+     * <code>attrName</code> from the attributes set. If attrName does
+     * not exist in the attributes set then nothing happens.
+     *
+     * @param attrName the name of the shader attribute to be removed
+     *
+     * @exception NullPointerException if attrName is null
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     */
+    public void remove(String attrName) {
+	if (attrName == null) {
+	    throw new NullPointerException();
+	}
+	
+        if (isLiveOrCompiled())
+	    if (!this.getCapability(ALLOW_ATTRIBUTES_WRITE))
+		throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeSet1"));
+	
+	((ShaderAttributeSetRetained)this.retained).remove(attrName);
+    }
+
+    /**
+     * Removes the specified shader attribute from the attributes
+     * set. If the attribute does not exist in the attributes set then
+     * nothing happens. Note that this method will <i>not</i> remove a
+     * shader object other than the one specified, even if it has the
+     * same name as the specified attribute. Applications that wish to
+     * remove an attribute by name should use
+     * <code>removeAttribute(String)</code>.
+     *
+     * @param attr the shader attribute to be removed
+     *
+     * @exception NullPointerException if attr is null
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     */
+    public void remove(ShaderAttribute attr) {
+	if (attr == null) {
+	    throw new NullPointerException();
+	}
+
+        if (isLiveOrCompiled())
+	    if (!this.getCapability(ALLOW_ATTRIBUTES_WRITE))
+		throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeSet1"));
+
+	((ShaderAttributeSetRetained)this.retained).remove(attr);
+    }
+
+    /**
+     * Removes all shader attributes from the attributes set. The
+     * attributes set will be empty following this call.
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     */
+    public void clear() {
+
+        if (isLiveOrCompiled())
+	    if (!this.getCapability(ALLOW_ATTRIBUTES_WRITE))
+		throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeSet1"));
+
+	((ShaderAttributeSetRetained)this.retained).clear();
+    }
+
+    /**
+     * Returns a shallow copy of the attributes set.
+     *
+     * @return a shallow copy of the attributes set
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     */
+    public ShaderAttribute[] getAll() {
+
+        if (isLiveOrCompiled())
+	    if (!this.getCapability(ALLOW_ATTRIBUTES_READ))
+		throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeSet0"));
+
+	return ((ShaderAttributeSetRetained)this.retained).getAll();
+    }
+
+    /**
+     * Returns the number of elements in the attributes set.
+     *
+     * @return the number of elements in the attributes set
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     */
+    public int size() {
+
+        if (isLiveOrCompiled())
+	    if (!this.getCapability(ALLOW_ATTRIBUTES_READ))
+		throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeSet0"));
+	
+	return ((ShaderAttributeSetRetained)this.retained).size();
+    }
+
+    /**
+     * Creates a retained mode ShaderAttributeSetRetained object that this
+     * ShaderAttributeSet component object will point to.
+     */
+    void createRetained() {
+	// System.out.println("ShaderAttributeSet : createRetained() ...");
+	this.retained = new ShaderAttributeSetRetained();
+	this.retained.setSource(this);
+    }
+}
diff --git a/src/classes/share/javax/media/j3d/ShaderAttributeSetRetained.java b/src/classes/share/javax/media/j3d/ShaderAttributeSetRetained.java
new file mode 100644
index 0000000..965387b
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderAttributeSetRetained.java
@@ -0,0 +1,390 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ArrayList;
+import javax.vecmath.*;
+
+/**
+ * The ShaderAttributeSet object provides uniform attributes to shader
+ * programs. Uniform attributes (variables) are those attributes whose
+ * values are constant during the rendering of a primitive. Their
+ * values may change from primitive to primitive, but are constant for
+ * each vertex (for vertex shaders) or fragment (for fragment shaders)
+ * of a single primitive. Examples of uniform attributes include a
+ * transformation matrix, a texture map, lights, lookup tables, etc.
+ * The ShaderAttributeSet object contains a set of ShaderAttribute
+ * objects. Each ShaderAttribute object defines the value of a single
+ * uniform shader variable. The set of attributes is unique with respect
+ * to attribute names: no two attributes in the set will have the same
+ * name.
+ *
+ * <p>
+ * There are two ways in which values can be specified for uniform
+ * attributes: explicitly, by providing a value; and implicitly, by
+ * defining a binding between a Java 3D system attribute and a uniform
+ * attribute. This functionality is provided by two subclasses of
+ * ShaderAttribute: ShaderAttributeObject, which is used to specify
+ * explicitly defined attributes; and ShaderAttributeBinding, which is
+ * used to specify implicitly defined, automatically tracked attributes.
+ *
+ * <p>
+ * Depending on the shading language (and profile) being used, several
+ * Java 3D state attributes are automatically made available to the
+ * shader program as pre-defined uniform attributes. The application
+ * doesn't need to do anything to pass these attributes in to the
+ * shader program. The implementation of each shader language (e.g.,
+ * Cg, GLSL) defines its own bindings from Java 3D attribute to uniform
+ * variable name. A list of these attributes for each shader language
+ * can be found in the concrete subclass of ShaderProgram for that
+ * shader language.
+ *
+ * @see ShaderAttribute
+ * @see ShaderProgram
+ * @see ShaderAppearance#setShaderAttributeSet
+ *
+ * @since Java 3D 1.4
+ */
+
+class ShaderAttributeSetRetained extends NodeComponentRetained {
+    // A list of pre-defined bits to indicate which attribute
+    // operation in this ShaderAttributeSet object is needed.
+    static final int ATTRIBUTE_SET_PUT              = 0x01;
+
+    static final int ATTRIBUTE_SET_REMOVE           = 0x02;
+
+    static final int ATTRIBUTE_SET_CLEAR            = 0x04;
+
+    private Map attrs = new HashMap();
+
+    // Lock used for synchronization of live state
+    Object liveStateLock = new Object();
+
+    /**
+     * Constructs an empty ShaderAttributeSetretained object. The attributes set
+     * is initially empty.
+     */
+    ShaderAttributeSetRetained() {
+    }
+
+    //
+    // Methods for dealing with the (name, value) pairs for explicit
+    // attributes
+    //
+
+    /**
+     * Adds the specified shader attribute to the attributes set.
+     * The newly specified attribute replaces an attribute with the
+     * same name, if one already exists in the attributes set.
+     *
+     * @param attr the shader attribute to be added to the set
+     *
+     */
+    void put(ShaderAttribute attr) {
+	synchronized(liveStateLock) {
+	    // System.out.println("ShaderAttributeSetRetained : put()");
+	    ShaderAttributeRetained sAttr = (ShaderAttributeRetained)attr.retained;
+	    // System.out.println("attr is " + attr );
+	    // System.out.println("attrName is " + sAttr.attrName + " attr.Retained is "+ sAttr );
+	    assert(sAttr != null);
+	    attrs.put(sAttr.attrName, sAttr);
+	    
+	    if (source.isLive()) {
+		sAttr.setLive(inBackgroundGroup, refCount);
+		sAttr.copyMirrorUsers(this);
+                
+		sendMessage(ATTRIBUTE_SET_PUT, sAttr.mirror);
+	    }	    
+	}
+    }
+
+    /**
+     * Retrieves the shader attribute with the specified
+     * <code>attrName</code> from the attributes set. If attrName does
+     * not exist in the attributes set, null is returned.
+     *
+     * @param attrName the name of the shader attribute to be retrieved
+     *
+     * @exception CapabilityNotSetException if appropriate capability is 
+     * not set and this object is part of live or compiled scene graph
+     */
+    ShaderAttribute get(String attrName) {
+	return (ShaderAttribute)((ShaderAttributeRetained)attrs.get(attrName)).source;
+    }
+
+    /**
+     * Removes the shader attribute with the specified
+     * <code>attrName</code> from the attributes set. If attrName does
+     * not exist in the attributes set then nothing happens.
+     *
+     * @param attrName the name of the shader attribute to be removed
+     */
+    void remove(String attrName) {
+	synchronized(liveStateLock) {
+	    ShaderAttributeRetained sAttr = (ShaderAttributeRetained)attrs.get(attrName);
+	    attrs.remove(attrName);
+	    if (source.isLive()) {
+		sAttr.clearLive(refCount);
+		sAttr.removeMirrorUsers(this);
+		
+		sendMessage(ATTRIBUTE_SET_REMOVE, attrName);
+	    }
+	}
+    }
+
+    /**
+     * Removes the specified shader attribute from the attributes
+     * set. If the attribute does not exist in the attributes set then
+     * nothing happens. Note that this method will <i>not</i> remove a
+     * shader object other than the one specified, even if it has the
+     * same name as the specified attribute. Applications that wish to
+     * remove an attribute by name should use
+     * <code>removeAttribute(String)</code>.
+     *
+     * @param attr the shader attribute to be removed
+     */
+    void remove(ShaderAttribute attr) {
+	synchronized(liveStateLock) {   
+	    String attrName = attr.getAttributeName();
+	    if (attrs.get(attrName) == attr) {
+		attrs.remove(attrName);
+		if (source.isLive()) {
+		    ((ShaderAttributeRetained)attr.retained).clearLive(refCount);
+		    ((ShaderAttributeRetained)attr.retained).removeMirrorUsers(this);
+		    
+		    sendMessage(ATTRIBUTE_SET_REMOVE, attrName);
+		}
+	    }
+	}
+    }
+
+    /**
+     * Removes all shader attributes from the attributes set. The
+     * attributes set will be empty following this call.
+     *
+     */
+    void clear() {
+	synchronized(liveStateLock) {
+	    attrs.clear();
+	    if(source.isLive()) {
+		ShaderAttributeRetained[] sAttrs = new ShaderAttributeRetained[attrs.size()];
+		sAttrs = (ShaderAttributeRetained[])attrs.values().toArray(sAttrs);
+		for (int i = 0; i < sAttrs.length; i++) {
+		    sAttrs[i].clearLive(refCount);
+		    sAttrs[i].removeMirrorUsers(this);
+		}
+		sendMessage(ATTRIBUTE_SET_CLEAR, null);
+	    }
+	}
+    }
+
+    /**
+     * Returns a shallow copy of the attributes set.
+     *
+     * @return a shallow copy of the attributes set
+     *
+     */
+    ShaderAttribute[] getAll() {
+
+	ShaderAttributeRetained[] sAttrsRetained = new ShaderAttributeRetained[attrs.size()];
+	ShaderAttribute[] sAttrs = new ShaderAttribute[sAttrsRetained.length];
+	sAttrsRetained = (ShaderAttributeRetained[])attrs.values().toArray(sAttrsRetained);
+	for(int i=0; i < sAttrsRetained.length; i++) {
+	    sAttrs[i] = (ShaderAttribute) sAttrsRetained[i].source;
+	}
+	
+	return sAttrs;
+    }
+
+    /**
+     * Returns the number of elements in the attributes set.
+     *
+     * @return the number of elements in the attributes set
+     *
+     */
+    int size() {
+	return attrs.size();
+    }
+
+
+    void updateNative(Canvas3D cv, ShaderProgramRetained shaderProgram) {        
+        shaderProgram.setShaderAttributes(cv, this);
+    }
+
+    Map getAttrs() {
+        return attrs;
+    }
+    
+
+    void setLive(boolean backgroundGroup, int refCount) {
+	
+	// System.out.println("ShaderAttributeSetRetained.setLive()");
+	ShaderAttributeRetained[] sAttrsRetained = new ShaderAttributeRetained[attrs.size()];
+	sAttrsRetained = (ShaderAttributeRetained[])attrs.values().toArray(sAttrsRetained);
+	for(int i=0; i < sAttrsRetained.length; i++) {
+	    sAttrsRetained[i].setLive(backgroundGroup, refCount);
+	}
+	
+	super.doSetLive(backgroundGroup, refCount);
+        super.markAsLive();
+    }
+
+    synchronized void addAMirrorUser(Shape3DRetained shape) {
+
+	super.addAMirrorUser(shape);
+
+	ShaderAttributeRetained[] sAttrsRetained = new ShaderAttributeRetained[attrs.size()];
+	sAttrsRetained = (ShaderAttributeRetained[])attrs.values().toArray(sAttrsRetained);
+	for(int i=0; i < sAttrsRetained.length; i++) {
+	    sAttrsRetained[i].addAMirrorUser(shape); 
+	}
+    }
+
+    synchronized void removeAMirrorUser(Shape3DRetained shape) {
+	super.removeAMirrorUser(shape);
+
+	ShaderAttributeRetained[] sAttrsRetained = new ShaderAttributeRetained[attrs.size()];
+	sAttrsRetained = (ShaderAttributeRetained[])attrs.values().toArray(sAttrsRetained);
+	for(int i=0; i < sAttrsRetained.length; i++) {
+	    sAttrsRetained[i].removeAMirrorUser(shape);
+	}
+    }
+
+
+    synchronized void removeMirrorUsers(NodeComponentRetained node) {
+	super.removeMirrorUsers(node);
+
+	ShaderAttributeRetained[] sAttrsRetained = new ShaderAttributeRetained[attrs.size()];
+	sAttrsRetained = (ShaderAttributeRetained[])attrs.values().toArray(sAttrsRetained);
+	for(int i=0; i < sAttrsRetained.length; i++) {
+	    sAttrsRetained[i].removeMirrorUsers(node);
+	}
+    }
+
+    synchronized void copyMirrorUsers(NodeComponentRetained node) {
+	super.copyMirrorUsers(node);
+
+	ShaderAttributeRetained[] sAttrsRetained = new ShaderAttributeRetained[attrs.size()];
+	sAttrsRetained = (ShaderAttributeRetained[])attrs.values().toArray(sAttrsRetained);
+	for(int i=0; i < sAttrsRetained.length; i++) {
+	    sAttrsRetained[i].copyMirrorUsers(node); 
+	}
+    }
+
+    void clearLive(int refCount) {
+	// System.out.println("ShaderAttributeSetRetained.clearLive()");
+	
+	super.clearLive(refCount);
+	
+	ShaderAttributeRetained[] sAttrsRetained = new ShaderAttributeRetained[attrs.size()];
+	sAttrsRetained = (ShaderAttributeRetained[])attrs.values().toArray(sAttrsRetained);
+	for(int i=0; i < sAttrsRetained.length; i++) {
+	    sAttrsRetained[i].clearLive(refCount);
+	}
+    }
+
+    synchronized void createMirrorObject() {
+	// System.out.println("ShaderAttributeSetRetained : createMirrorObject");
+        // This method should only call by setLive().
+	if (mirror == null) {
+            ShaderAttributeSetRetained mirrorSAS = new ShaderAttributeSetRetained();
+	    mirror = mirrorSAS;
+	    mirror.source = source;
+
+	}
+	initMirrorObject();
+    }
+    
+    void initMirrorObject() {
+
+	ShaderAttributeRetained[] sAttrs = new ShaderAttributeRetained[attrs.size()];
+	sAttrs = (ShaderAttributeRetained[])attrs.values().toArray(sAttrs);
+	// Need to copy the mirror attrs 
+	for (int i = 0; i < sAttrs.length; i++) {
+	    ShaderAttributeRetained mirrorSA = (ShaderAttributeRetained) sAttrs[i].mirror;
+	    assert(mirrorSA != null);
+	    ((ShaderAttributeSetRetained)mirror).attrs.put(mirrorSA.attrName, mirrorSA);
+	}
+    }
+    
+     /**
+     * Update the "component" field of the mirror object with the  given "value"
+     */
+    synchronized void updateMirrorObject(int component, Object value) {
+
+	// System.out.println("ShaderAttributeSetRetained : updateMirrorObject");
+        
+	ShaderAttributeSetRetained mirrorSAS = (ShaderAttributeSetRetained)mirror;
+	
+	if ((component & ATTRIBUTE_SET_PUT) != 0) {
+	    // System.out.println("     -- ATTRIBUTE_SET_PUT");
+	    ShaderAttributeRetained mirrorSA = (ShaderAttributeRetained)value;
+ 	    assert(mirrorSA != null);
+	    ((ShaderAttributeSetRetained)mirror).attrs.put(mirrorSA.attrName, mirrorSA);
+	}
+	else if((component & ATTRIBUTE_SET_REMOVE) != 0) {
+	    // System.out.println("     -- ATTRIBUTE_SET_REMOVE");
+	    ((ShaderAttributeSetRetained)mirror).attrs.remove((String)value);
+	}
+	else if((component & ATTRIBUTE_SET_CLEAR) != 0) {
+	    // System.out.println("     -- ATTRIBUTE_SET_CLEAR");
+	    ((ShaderAttributeSetRetained)mirror).attrs.clear();
+	}
+	else {
+	    assert(false);
+	}
+    }   
+
+    final void sendMessage(int attrMask, Object attr) {
+
+	ArrayList univList = new ArrayList();
+	ArrayList gaList = Shape3DRetained.getGeomAtomsList(mirror.users, univList);  
+
+	// Send to rendering attribute structure, regardless of
+	// whether there are users or not (alternate appearance case ..)
+	J3dMessage createMessage = VirtualUniverse.mc.getMessage();
+	createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES;
+	createMessage.type = J3dMessage.SHADER_ATTRIBUTE_SET_CHANGED;
+	createMessage.universe = null;
+	createMessage.args[0] = this;
+	createMessage.args[1]= new Integer(attrMask);
+	createMessage.args[2] = attr;
+	//	System.out.println("changedFreqent1 = "+changedFrequent);
+	createMessage.args[3] = new Integer(changedFrequent);
+	VirtualUniverse.mc.processMessage(createMessage);
+
+	// System.out.println("univList.size is " + univList.size());
+	for(int i=0; i<univList.size(); i++) {
+	    createMessage = VirtualUniverse.mc.getMessage();
+	    createMessage.threads = J3dThread.UPDATE_RENDER;
+	    createMessage.type = J3dMessage.SHADER_ATTRIBUTE_SET_CHANGED;
+		
+	    createMessage.universe = (VirtualUniverse) univList.get(i);
+	    createMessage.args[0] = this;
+	    createMessage.args[1]= new Integer(attrMask);
+	    createMessage.args[2] = attr;
+
+	    ArrayList gL = (ArrayList)gaList.get(i);
+	    GeometryAtom[] gaArr = new GeometryAtom[gL.size()];
+	    gL.toArray(gaArr);
+	    createMessage.args[3] = gaArr;  
+	    
+	    VirtualUniverse.mc.processMessage(createMessage);
+	}
+
+    }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ShaderAttributeValue.java b/src/classes/share/javax/media/j3d/ShaderAttributeValue.java
new file mode 100644
index 0000000..1f9dc0a
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderAttributeValue.java
@@ -0,0 +1,100 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * The ShaderAttributeValue object encapsulates a uniform shader
+ * attribute whose value is specified explicitly. The shader variable
+ * <code>attrName</code> is explicitly set to the specified
+ * <code>value</code> during rendering. <code>attrName</code> must be
+ * the name of a valid uniform attribute in the shader in which it is
+ * used. Otherwise, the attribute name will be ignored and a runtime
+ * error may be generated. The <code>value</code> must be an instance
+ * of one of the allowed classes. The allowed classes are:
+ * <code>Integer</code>, <code>Float</code>,
+ * <code>Tuple{2,3,4}{i,f}</code>, <code>Matrix{3,4}f</code>. A
+ * ClassCastException will be thrown if a specified <code>value</code>
+ * object is not one of the allowed types. Further, the type of the
+ * value is immutable once a ShaderAttributeValue is constructed.
+ * Subsequent setValue operations must be called with an object of the
+ * same type as the one that was used to construct the
+ * ShaderAttributeValue. Finally, the type of the <code>value</code>
+ * object must match the type of the corresponding
+ * <code>attrName</code> variable in the shader in which it is
+ * used. Otherwise, the shader will not be able to use the attribute
+ * and a runtime error may be generated.
+ *
+ * @see ShaderAttributeSet
+ * @see ShaderProgram
+ *
+ * @since Java 3D 1.4
+ */
+
+public class ShaderAttributeValue extends ShaderAttributeObject {
+    /**
+     * Constructs a new ShaderAttributeValue object with the specified
+     * <code>(attrName,&nbsp;value)</code> pair.
+     * A copy of the object is stored.
+     *
+     * @param attrName the name of the shader attribute
+     * @param value the value of the shader attribute
+     *
+     * @exception NullPointerException if attrName or value is null
+     *
+     * @exception ClassCastException if value is not an instance of
+     * one of the allowed classes
+     */
+    public ShaderAttributeValue(String attrName, Object value) {
+	super(attrName, value);
+    }
+    
+    // Implement abstract getValue method
+    public Object getValue() {
+        
+        if (isLiveOrCompiled())
+	    if (!this.getCapability(ALLOW_VALUE_READ))
+		throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeObject0"));
+
+ 	return ((ShaderAttributeValueRetained)this.retained).getValue();
+    }
+
+    // Implement abstract setValue method
+    public void setValue(Object value) {
+
+        if (value == null) {
+	    throw new NullPointerException();
+	}
+        
+        if (isLiveOrCompiled())
+	    if (!this.getCapability(ALLOW_VALUE_WRITE))
+		throw new CapabilityNotSetException(J3dI18N.getString("ShaderAttributeObject1"));
+
+	if (isLive())
+	    ((ShaderAttributeValueRetained)this.retained).setValue(value);
+	else
+	    ((ShaderAttributeValueRetained)this.retained).initValue(value);
+
+    }
+
+    /**
+     * Creates a retained mode ShaderAttributeValueRetained object that this
+     * ShaderAttributeValue component object will point to.
+     */
+    void createRetained() {
+	this.retained = new ShaderAttributeValueRetained();
+	this.retained.setSource(this);
+    }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ShaderAttributeValueRetained.java b/src/classes/share/javax/media/j3d/ShaderAttributeValueRetained.java
new file mode 100644
index 0000000..758672c
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderAttributeValueRetained.java
@@ -0,0 +1,499 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+
+/**
+ * The ShaderAttributeValueRetained object encapsulates a uniform shader
+ * attribute whose value is specified explicitly. The shader variable
+ * <code>attrName</code> is explicitly set to the specified
+ * <code>value</code> during rendering. <code>attrName</code> must be
+ * the name of a valid uniform attribute in the shader in which it is
+ * used. Otherwise, the attribute name will be ignored and a runtime
+ * error may be generated. The <code>value</code> must be an instance
+ * of one of the allowed classes. The allowed classes are:
+ * <code>Integer</code>, <code>Float</code>,
+ * <code>Tuple{2,3,4}{i,f}</code>, <code>Matrix{3,4}f</code>. A
+ * ClassCastException will be thrown if a specified <code>value</code>
+ * object is not one of the allowed types. Further, the type of the
+ * value is immutable once a ShaderAttributeValue is constructed.
+ * Subsequent setValue operations must be called with an object of the
+ * same type as the one that was used to construct the
+ * ShaderAttributeValue. Finally, the type of the <code>value</code>
+ * object must match the type of the corresponding
+ * <code>attrName</code> variable in the shader in which it is
+ * used. Otherwise, the shader will not be able to use the attribute
+ * and a runtime error may be generated.
+ *
+ * @see ShaderAttributeSetRetained
+ * @see ShaderProgramRetained
+ *
+ * @since Java 3D 1.4
+ */
+
+class ShaderAttributeValueRetained extends ShaderAttributeObjectRetained {
+
+    ShaderAttributeValueRetained() {
+    }
+    
+    synchronized void createMirrorObject() {
+	// System.out.println("ShaderAttributeValueRetained : createMirrorObject");
+        // This method should only call by setLive().
+	if (mirror == null) {
+            ShaderAttributeValueRetained mirrorSAV = new ShaderAttributeValueRetained();
+            mirrorSAV.createObjectData(getValue());
+	    mirror = mirrorSAV;
+	    mirror.source = source;
+
+	}
+	initMirrorObject();
+    }
+
+    /**
+     * Computes the base class from the specified object. A
+     * ClassCastException is thrown if the object is not an instance
+     * of one of the allowed classes.
+     */
+    int computeClassType(Object value) {
+	Class objClass = value.getClass();
+	if (objClass.isArray()) {
+	    throw new ClassCastException(objClass + " -- array class not allowed");
+	}
+
+	for (int i = 0; i < classTable.length; i++) {
+	    if (classTable[i].isInstance(value)) {
+		return i;
+	    }
+	}
+	throw new ClassCastException(objClass + " -- unrecognized class");
+    }
+
+    /**
+     * Returns the base class represented by the specified class type.
+     */
+    Class getBaseClass(int classType) {
+	return classTable[classType];
+    }
+
+    /**
+     * Creates an attribute wrapper object of the specified class
+     * type, and stores the specified object.
+     */
+    AttrWrapper createAttrWrapper(Object value, int classType) {
+	ValueWrapper attrWrapper = null;
+	switch (classType) {
+	case TYPE_INTEGER:
+	    attrWrapper = new IntegerWrapper();
+	    break;
+	case TYPE_FLOAT:
+	    attrWrapper = new FloatWrapper();
+	    break;
+//	case TYPE_DOUBLE:
+//	    attrWrapper = new DoubleWrapper();
+//	    break;
+	case TYPE_TUPLE2I:
+	    attrWrapper = new Tuple2iWrapper();
+	    break;
+	case TYPE_TUPLE2F:
+	    attrWrapper = new Tuple2fWrapper();
+	    break;
+//	case TYPE_TUPLE2D:
+//	    attrWrapper = new Tuple2dWrapper();
+//	    break;
+	case TYPE_TUPLE3I:
+	    attrWrapper = new Tuple3iWrapper();
+	    break;
+	case TYPE_TUPLE3F:
+	    attrWrapper = new Tuple3fWrapper();
+	    break;
+//	case TYPE_TUPLE3D:
+//	    attrWrapper = new Tuple3dWrapper();
+//	    break;
+	case TYPE_TUPLE4I:
+	    attrWrapper = new Tuple4iWrapper();
+	    break;
+	case TYPE_TUPLE4F:
+	    attrWrapper = new Tuple4fWrapper();
+	    break;
+//	case TYPE_TUPLE4D:
+//	    attrWrapper = new Tuple4dWrapper();
+//	    break;
+	case TYPE_MATRIX3F:
+	    attrWrapper = new Matrix3fWrapper();
+	    break;
+//	case TYPE_MATRIX3D:
+//	    attrWrapper = new Matrix3dWrapper();
+//	    break;
+	case TYPE_MATRIX4F:
+	    attrWrapper = new Matrix4fWrapper();
+	    break;
+//	case TYPE_MATRIX4D:
+//	    attrWrapper = new Matrix4dWrapper();
+//	    break;
+	default:
+	    // Should never get here
+	    assert false;
+	    return null;
+	}
+
+	attrWrapper.set(value);
+	return attrWrapper;
+    }
+
+    //
+    // The following wrapper classes are used to store a copy of the
+    // user-specified shader attribute value. There is a wrapper class
+    // for each supported base class.
+    //
+
+    // Base wrapper class for non-array attribute types
+    static abstract class ValueWrapper extends AttrWrapper {
+	// No additional fields or methods are defined in this class
+    }
+
+    // Wrapper class for Integer
+    static class IntegerWrapper extends ValueWrapper {
+	private int[] value = new int[1];
+
+	void set(Object value) {
+	    this.value[0] = ((Integer)value).intValue();
+	}
+
+	Object get() {
+	    return new Integer(this.value[0]);
+	}
+
+	Object getRef() {
+	    return this.value;
+	}
+    }
+
+    // Wrapper class for Float
+    static class FloatWrapper extends ValueWrapper {
+	private float[] value = new float[1];
+
+	void set(Object value) {
+	    this.value[0] = ((Float)value).floatValue();
+	}
+
+	Object get() {
+	    return new Float(this.value[0]);
+	}
+
+	Object getRef() {
+	    return this.value;
+	}
+    }
+
+    /*
+    // Wrapper class for Double
+    static class DoubleWrapper extends ValueWrapper {
+	private double[] value = new double[1];
+
+	void set(Object value) {
+	    this.value[0] = ((Double)value).doubleValue();
+	}
+
+	Object get() {
+	    return new Double(value[0]);
+	}
+
+	Object getRef() {
+	    return value;
+	}
+    }
+    */
+
+    // Wrapper class for Tuple2i
+    static class Tuple2iWrapper extends ValueWrapper {
+	private int[] value = new int[2];
+
+	void set(Object value) {
+	    ((Tuple2i)value).get(this.value);
+	}
+
+	Object get() {
+	    return new Point2i(value);
+	}
+
+	Object getRef() {
+	    return value;
+	}
+    }
+
+    // Wrapper class for Tuple2f
+    static class Tuple2fWrapper extends ValueWrapper {
+	private float[] value = new float[2];
+
+	void set(Object value) {
+	    ((Tuple2f)value).get(this.value);
+	}
+
+	Object get() {
+	    return new Point2f(value);
+	}
+
+	Object getRef() {
+	    return value;
+	}
+    }
+
+    /*
+    // Wrapper class for Tuple2d
+    static class Tuple2dWrapper extends ValueWrapper {
+	private double[] value = new double[2];
+
+	void set(Object value) {
+	    ((Tuple2d)value).get(this.value);
+	}
+
+	Object get() {
+	    return new Point2d(value);
+	}
+
+	Object getRef() {
+	    return value;
+	}
+    }
+    */
+
+    // Wrapper class for Tuple3i
+    static class Tuple3iWrapper extends ValueWrapper {
+	private int[] value = new int[3];
+
+	void set(Object value) {
+	    ((Tuple3i)value).get(this.value);
+	}
+
+	Object get() {
+	    return new Point3i(value);
+	}
+
+	Object getRef() {
+	    return value;
+	}
+    }
+
+    // Wrapper class for Tuple3f
+    static class Tuple3fWrapper extends ValueWrapper {
+	private float[] value = new float[3];
+
+	void set(Object value) {
+	    ((Tuple3f)value).get(this.value);
+	}
+
+	Object get() {
+	    return new Point3f(value);
+	}
+
+	Object getRef() {
+	    return value;
+	}
+    }
+
+    /*
+    // Wrapper class for Tuple3d
+    static class Tuple3dWrapper extends ValueWrapper {
+	private double[] value = new double[3];
+
+	void set(Object value) {
+	    ((Tuple3d)value).get(this.value);
+	}
+
+	Object get() {
+	    return new Point3d(value);
+	}
+
+	Object getRef() {
+	    return value;
+	}
+    }
+    */
+
+    // Wrapper class for Tuple4i
+    static class Tuple4iWrapper extends ValueWrapper {
+	private int[] value = new int[4];
+
+	void set(Object value) {
+	    ((Tuple4i)value).get(this.value);
+	}
+
+	Object get() {
+	    return new Point4i(value);
+	}
+
+	Object getRef() {
+	    return value;
+	}
+    }
+
+    // Wrapper class for Tuple4f
+    static class Tuple4fWrapper extends ValueWrapper {
+	private float[] value = new float[4];
+
+	void set(Object value) {
+	    ((Tuple4f)value).get(this.value);
+	}
+
+	Object get() {
+	    return new Point4f(value);
+	}
+
+	Object getRef() {
+	    return value;
+	}
+    }
+
+    /*
+    // Wrapper class for Tuple4d
+    static class Tuple4dWrapper extends ValueWrapper {
+	private double[] value = new double[4];
+
+	void set(Object value) {
+	    ((Tuple4d)value).get(this.value);
+	}
+
+	Object get() {
+	    return new Point4d(value);
+	}
+
+	Object getRef() {
+	    return value;
+	}
+    }
+    */
+
+    // Wrapper class for Matrix3f
+    static class Matrix3fWrapper extends ValueWrapper {
+	private float[] value = new float[9];
+
+	void set(Object value) {
+	    Matrix3f m = (Matrix3f)value;
+	    this.value[0] = m.m00;
+	    this.value[1] = m.m01;
+	    this.value[2] = m.m02;
+	    this.value[3] = m.m10;
+	    this.value[4] = m.m11;
+	    this.value[5] = m.m12;
+	    this.value[6] = m.m20;
+	    this.value[7] = m.m21;
+	    this.value[8] = m.m22;
+	}
+
+	Object get() {
+	    return new Matrix3f(value);
+	}
+
+	Object getRef() {
+	    return value;
+	}
+    }
+
+    /*
+    // Wrapper class for Matrix3d
+    static class Matrix3dWrapper extends ValueWrapper {
+	private double[] value = new double[9];
+
+	void set(Object value) {
+	    Matrix3d m = (Matrix3d)value;
+	    this.value[0] = m.m00;
+	    this.value[1] = m.m01;
+	    this.value[2] = m.m02;
+	    this.value[3] = m.m10;
+	    this.value[4] = m.m11;
+	    this.value[5] = m.m12;
+	    this.value[6] = m.m20;
+	    this.value[7] = m.m21;
+	    this.value[8] = m.m22;
+	}
+
+	Object get() {
+	    return new Matrix3d(value);
+	}
+
+	Object getRef() {
+	    return value;
+	}
+    }
+    */
+
+    // Wrapper class for Matrix4f
+    static class Matrix4fWrapper extends ValueWrapper {
+	private float[] value = new float[16];
+
+	void set(Object value) {
+	    Matrix4f m = (Matrix4f)value;
+	    this.value[0]  = m.m00;
+	    this.value[1]  = m.m01;
+	    this.value[2]  = m.m02;
+	    this.value[3]  = m.m03;
+	    this.value[4]  = m.m10;
+	    this.value[5]  = m.m11;
+	    this.value[6]  = m.m12;
+	    this.value[7]  = m.m13;
+	    this.value[8]  = m.m20;
+	    this.value[9]  = m.m21;
+	    this.value[10] = m.m22;
+	    this.value[11] = m.m23;
+	    this.value[12] = m.m30;
+	    this.value[13] = m.m31;
+	    this.value[14] = m.m32;
+	    this.value[15] = m.m33;
+	}
+
+	Object get() {
+	    return new Matrix4f(value);
+	}
+
+	Object getRef() {
+	    return value;
+	}
+    }
+
+    /*
+    // Wrapper class for Matrix4d
+    static class Matrix4dWrapper extends ValueWrapper {
+	private double[] value = new double[16];
+
+	void set(Object value) {
+	    Matrix4d m = (Matrix4d)value;
+	    this.value[0]  = m.m00;
+	    this.value[1]  = m.m01;
+	    this.value[2]  = m.m02;
+	    this.value[3]  = m.m03;
+	    this.value[4]  = m.m10;
+	    this.value[5]  = m.m11;
+	    this.value[6]  = m.m12;
+	    this.value[7]  = m.m13;
+	    this.value[8]  = m.m20;
+	    this.value[9]  = m.m21;
+	    this.value[10] = m.m22;
+	    this.value[11] = m.m23;
+	    this.value[12] = m.m30;
+	    this.value[13] = m.m31;
+	    this.value[14] = m.m32;
+	    this.value[15] = m.m33;
+	}
+
+	Object get() {
+	    return new Matrix4d(value);
+	}
+
+	Object getRef() {
+	    return value;
+	}
+    }
+    */
+
+}
diff --git a/src/classes/share/javax/media/j3d/ShaderBin.java b/src/classes/share/javax/media/j3d/ShaderBin.java
new file mode 100644
index 0000000..0e4ad74
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderBin.java
@@ -0,0 +1,363 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import javax.vecmath.*;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ArrayList;
+
+
+// XXXX : We should have a common Bin object that all other Bins extend from. 
+
+
+//class ShaderBin extends Object implements ObjectUpdate, NodeComponentUpdate {
+class ShaderBin implements ObjectUpdate {
+
+    /**
+     * Node component dirty mask.
+     */
+    static final int SHADER_PROGRAM_DIRTY               = 0x1;
+    static final int SHADER_ATTRIBUTE_SET_DIRTY         = 0x2;
+
+
+    /**
+     * The RenderBin for this object
+     */
+    RenderBin renderBin = null;
+
+    /**
+     * The AttributeBin that this ShaderBin resides
+     */
+    AttributeBin attributeBin = null;
+
+    /**
+     * The references to the next and previous ShaderBins in the
+     * list.
+     */
+    ShaderBin next = null;
+    ShaderBin prev = null;
+
+    /**
+     * The list of TextureBins in this ShaderBin
+     */
+    TextureBin textureBinList = null;
+
+    /**
+     * The list of TextureBins to be added for the next frame
+     */
+    ArrayList addTextureBins = new ArrayList();
+
+    boolean onUpdateList = false;
+
+    int numEditingTextureBins = 0;
+
+    int componentDirty = 0;
+    ShaderAppearanceRetained shaderAppearance = null;
+    ShaderProgramRetained shaderProgram = null;
+    ShaderAttributeSetRetained shaderAttributeSet = new ShaderAttributeSetRetained();
+    
+    ShaderBin(ShaderAppearanceRetained sApp,  RenderBin rBin) {
+	reset(sApp, rBin);
+    }
+    
+    void reset(ShaderAppearanceRetained sApp, RenderBin rBin) {
+	prev = null;
+	next = null;
+        renderBin = rBin;
+	attributeBin = null;
+	textureBinList = null;
+	onUpdateList = false;
+	numEditingTextureBins = 0;
+	addTextureBins.clear();
+	if(sApp != null) {
+	    shaderProgram = sApp.shaderProgram;
+	    shaderAttributeSet = sApp.shaderAttributeSet; 
+	}
+	else {
+	    shaderProgram = null;
+	    shaderAttributeSet = null;
+	}
+	shaderAppearance = sApp;
+    }
+    
+    void clear() {
+	reset(null, null);
+    }
+    
+    /**
+     * This tests if the qiven ra.shaderProgram  match this shaderProgram
+     */
+    boolean equals(ShaderAppearanceRetained sApp) {
+	
+	ShaderProgramRetained sp;
+	ShaderAttributeSetRetained ss;
+	 
+	if (sApp == null) {
+	    sp = null;
+	    ss = null;
+	} else {
+	    sp = sApp.shaderProgram;
+	    ss = sApp.shaderAttributeSet;
+	}
+	
+	if((shaderProgram != sp) || (shaderAttributeSet != ss)) {
+	    return false;
+	}
+	
+	return true;
+	
+    }
+
+    public void updateObject() {
+	TextureBin t;
+	int i;
+	
+	if (addTextureBins.size() > 0) {
+	    t = (TextureBin)addTextureBins.get(0);
+	    if (textureBinList == null) {
+		textureBinList = t;
+
+	    }
+	    else {
+		// Look for a TextureBin that has the same texture
+		insertTextureBin(t);	
+	    }	    
+	    for (i = 1; i < addTextureBins.size() ; i++) {
+		t = (TextureBin)addTextureBins.get(i);
+		// Look for a TextureBin that has the same texture
+		insertTextureBin(t);
+
+	    }
+	}
+	addTextureBins.clear();
+	onUpdateList = false;
+
+    }
+    
+    void insertTextureBin(TextureBin t) {
+	TextureBin tb;
+	int i;
+	TextureRetained texture = null;
+
+	if (t.texUnitState != null && t.texUnitState.length > 0) {
+	    if (t.texUnitState[0] != null) {
+	        texture = t.texUnitState[0].texture;
+	    }
+	}
+
+	// use the texture in the first texture unit as the sorting criteria
+	if (texture != null) {
+	    tb = textureBinList; 
+	    while (tb != null) { 
+		if (tb.texUnitState == null || tb.texUnitState[0] == null ||
+			tb.texUnitState[0].texture != texture) {
+		    tb = tb.next;
+		} else {
+		    // put it here  
+		    t.next = tb; 
+		    t.prev = tb.prev; 
+		    if (tb.prev == null) { 
+		        textureBinList = t; 
+		    } 
+		    else { 
+		        tb.prev.next = t; 
+		    } 
+		    tb.prev = t; 
+		    return; 
+	        } 
+	    }
+	} 
+	// Just put it up front
+	t.prev = null;
+	t.next = textureBinList;
+	textureBinList.prev = t;
+	textureBinList = t;
+
+	t.tbFlag &= ~TextureBin.RESORT;
+    }
+
+
+    /**
+     * reInsert textureBin if the first texture is different from
+     * the previous bin and different from the next bin
+     */
+    void reInsertTextureBin(TextureBin tb) {
+
+        TextureRetained texture = null,
+                        prevTexture = null,
+                        nextTexture = null;
+
+        if (tb.texUnitState != null && tb.texUnitState[0] != null) {
+            texture = tb.texUnitState[0].texture;
+        }
+
+        if (tb.prev != null && tb.prev.texUnitState != null) {
+            prevTexture = tb.prev.texUnitState[0].texture;
+        }
+
+        if (texture != prevTexture) {
+            if (tb.next != null && tb.next.texUnitState != null) {
+                nextTexture = tb.next.texUnitState[0].texture;
+            }
+            if (texture != nextTexture) {
+                if (tb.prev != null && tb.next != null) {
+                    tb.prev.next = tb.next;
+		    tb.next.prev = tb.prev;
+                    insertTextureBin(tb);
+                }
+            }
+        }
+    }
+
+
+
+    /**
+     * Adds the given TextureBin to this AttributeBin.
+     */
+    void addTextureBin(TextureBin t, RenderBin rb, RenderAtom ra) {
+	 
+	t.environmentSet = this.attributeBin.environmentSet;
+	t.attributeBin = this.attributeBin;
+	t.shaderBin = this;
+
+	attributeBin.updateFromShaderBin(ra);
+	addTextureBins.add(t);
+
+	if (!onUpdateList) {
+	    rb.objUpdateList.add(this);
+	    onUpdateList = true;
+	}
+    }
+
+    /**
+     * Removes the given TextureBin from this ShaderBin.
+     */
+    void removeTextureBin(TextureBin t) {
+	
+	// If the TextureBin being remove is contained in addTextureBins, 
+	// then remove the TextureBin from the addList
+	if (addTextureBins.contains(t)) {
+	    addTextureBins.remove(addTextureBins.indexOf(t));
+	}
+	else {
+	    if (t.prev == null) { // At the head of the list
+		textureBinList = t.next;
+		if (t.next != null) {
+		    t.next.prev = null;
+		}
+	    } else { // In the middle or at the end.
+		t.prev.next = t.next;
+		if (t.next != null) {
+		    t.next.prev = t.prev;
+		}
+	    }
+	}
+
+	t.shaderBin = null;
+	t.prev = null;
+	t.next = null;
+
+	t.clear();
+
+	renderBin.textureBinFreelist.add(t);
+
+	if (textureBinList == null && addTextureBins.size() == 0 ) {
+	    // Note: Removal of this shaderBin as a user of the rendering
+	    // atttrs is done during removeRenderAtom() in RenderMolecule.java
+	    attributeBin.removeShaderBin(this);
+	}
+    }
+
+    /**
+     * Renders this ShaderBin
+     */
+    void render(Canvas3D cv) {
+
+	TextureBin tb;
+        
+	// System.out.println("ShaderBin.render() shaderProgram = " + shaderProgram);
+
+        // include this ShaderBin to the to-be-updated list in canvas
+        cv.setStateToUpdate(Canvas3D.SHADERBIN_BIT, this);
+
+	tb = textureBinList;
+	while (tb != null) {
+	    tb.render(cv);
+	    tb = tb.next;
+	}
+    }
+
+    void updateTransparentAttributes(Canvas3D cv) {
+   
+        // include this ShaderBin to the to-be-updated state set in canvas
+        cv.setStateToUpdate(Canvas3D.SHADERBIN_BIT, this);              
+    }
+    
+    void updateAttributes(Canvas3D cv) {
+
+        // System.out.println("ShaderBin.updateAttributes() shaderProgram is " + shaderProgram);
+	if (shaderProgram != null) {
+            // Compile, link, and enable shader program
+	    shaderProgram.updateNative(cv, true);
+
+	    if (shaderAttributeSet != null) {
+		shaderAttributeSet.updateNative(cv, shaderProgram);
+	    }
+
+	}
+	else {
+	    if (cv.shaderProgram != null) {
+                // Disable shader program
+		cv.shaderProgram.updateNative(cv, false);
+	    }
+	}
+
+        cv.shaderBin = this;
+	cv.shaderProgram = shaderProgram;
+    }
+
+    void updateNodeComponent() {
+	// System.out.println("ShaderBin.updateNodeComponent() ...");
+	
+	// We don't need to clone shaderProgram. 	
+	// ShaderProgram object can't be modified once it is live, 
+	// so each update should be a new reference.
+	if ((componentDirty & SHADER_PROGRAM_DIRTY) != 0) {
+	    // System.out.println("  - SHADER_PROGRAM_DIRTY");
+
+	    shaderProgram = shaderAppearance.shaderProgram;
+	}
+
+	// We need to clone the shaderAttributeSet.
+	if ((componentDirty & SHADER_ATTRIBUTE_SET_DIRTY) != 0) {
+	    // System.out.println("  - SHADER_ATTRIBUTE_SET_DIRTY");
+
+	    HashMap attrs = (HashMap)shaderAttributeSet.getAttrs();
+	    attrs.clear();
+            if(shaderAppearance.shaderAttributeSet != null) {
+                attrs.putAll(shaderAppearance.shaderAttributeSet.getAttrs());
+            }
+	}
+	
+	componentDirty = 0;
+   }
+
+    void incrActiveTextureBin() {
+	numEditingTextureBins++;
+    }
+
+    void decrActiveTextureBin() {
+	numEditingTextureBins--;
+    }
+}
diff --git a/src/classes/share/javax/media/j3d/ShaderError.java b/src/classes/share/javax/media/j3d/ShaderError.java
new file mode 100644
index 0000000..52c60f7
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderError.java
@@ -0,0 +1,410 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.io.PrintStream;
+
+/**
+ * ShaderError is a container object that holds the details of
+ * a runtime error that occurs while compiling or executing a
+ * programmable shader.
+ *
+ * @since Java 3D 1.4
+ */
+public class ShaderError extends Object {
+    private int errorCode = NO_ERROR;
+    private String errorMessage = null;
+    private String detailMessage = null;
+    private Canvas3D canvas = null;
+    private Shape3D shape = null;
+    private Geometry geometry = null;
+    private ShaderAppearance shaderApp = null;
+    private ShaderProgram shaderProgram = null;
+    private Shader shader = null;
+    private ShaderAttributeSet shaderAttributeSet = null;
+    private ShaderAttribute shaderAttribute = null;
+
+    /**
+     * Indicates that no error occurred.
+     */
+    public static final int NO_ERROR = 0;
+
+    /**
+     * Indicates that an error occurred while compiling a shader.
+     */
+    public static final int COMPILE_ERROR = 1;
+
+    /**
+     * Indicates that an error occurred while linking a shader.
+     */
+    public static final int LINK_ERROR = 2;
+
+    /**
+     * Indicates a error in looking up a vertex attribute
+     * name within a given shader program.
+     */
+    public static final int VERTEX_ATTRIBUTE_LOOKUP_ERROR = 3;
+
+    /**
+     * Indicates a error in looking up the location of a uniform
+     * shader attribute name within a given shader program.
+     */
+    public static final int SHADER_ATTRIBUTE_LOOKUP_ERROR = 4;
+
+    /**
+     * Indicates a error caused by a ShaderAttribute whose name does not
+     * appear in the list of shader attribute names in the corresponding
+     * ShaderProgram object.
+     */
+    public static final int SHADER_ATTRIBUTE_NAME_NOT_SET_ERROR = 5;
+
+    /**
+     * Indicates a error in the type of the attribute versus what the shader
+     * program was expecting.
+     */
+    public static final int SHADER_ATTRIBUTE_TYPE_ERROR = 6;
+
+    /**
+     * Indicates that the specified shading language is not supported
+     * on the screen display device.
+     */
+    public static final int UNSUPPORTED_LANGUAGE_ERROR = 7;
+
+
+    /**
+     * Constructs a new ShaderError object indicating no error. The
+     * error code is set to <code>NO_ERROR</code>.  All other fields
+     * are initialized to null, including the error message.
+     */
+    public ShaderError() {
+    }
+
+    /**
+     * Constructs a new ShaderError object with the given error code
+     * and message.  All other fields are initialized to null.
+     *
+     * @param errorCode the error code for this shader error.
+     *
+     * @param errorMessage a short error message describing this
+     * shader error.
+     */
+    public ShaderError(int errorCode, String errorMessage) {
+	this.errorCode = errorCode;
+	this.errorMessage = errorMessage;
+    }
+
+    /**
+     * Prints a verbose error report to System.err. This verbose
+     * output includes the error code, error message, detail message,
+     * and all relevant Java 3D objects.
+     */
+    public void printVerbose() {
+	printVerbose(System.err);
+    }
+
+    /**
+     * Prints a verbose error report to the specified PrintStream.
+     * This verbose output includes the error code, error message,
+     * detail message, and all relevant Java 3D objects.
+     *
+     * @param printStream the print stream on which to print the error
+     * report.
+     */
+    public void printVerbose(PrintStream printStream) {
+	printStream.println(this);
+        if (canvas != null) {
+            printStream.println("canvas = " + canvas);
+        }
+        if (shape != null) {
+            printStream.println("shape = " + shape);
+        }
+	if (geometry != null) {
+	    printStream.println("geometry = " + geometry);
+	}
+	if (shaderApp != null) {
+	    printStream.println("shaderApp = " + shaderApp);
+	}
+	if (shaderProgram != null) {
+	    printStream.println("shaderProgram = " + shaderProgram);
+	}
+	if (shader != null) {
+	    printStream.println("shader = " + shader);
+	}
+	if (shaderAttributeSet != null) {
+	    printStream.println("shaderAttributeSet = " + shaderAttributeSet);
+	}
+	if (shaderAttribute != null) {
+	    printStream.println("shaderAttribute = " + shaderAttribute);
+	}
+
+        if (detailMessage != null) {
+	    printStream.println();
+	    printStream.println("Detail Message");
+	    printStream.println("--------------");
+	    printStream.println(detailMessage);
+	}
+    }
+
+    /**
+     * Sets the error code for this shader error. This represents the
+     * type of error that occurred.
+     *
+     * @param errorCode the error code for this shader error.
+     */
+    public void setErrorCode(int errorCode) {
+	this.errorCode = errorCode;
+    }
+
+    /**
+     * Returns the error code for this shader error.
+     *
+     * @return the error code.
+     */
+    public int getErrorCode() {
+	return errorCode;
+    }
+
+    /**
+     * Sets the error message for this shader error. This is a short
+     * message describing the error, and is included as part of
+     * toString().
+     *
+     * @param errorMessage a short error message describing this
+     * shader error.
+     */
+    public void setErrorMessage(String errorMessage) {
+	this.errorMessage = errorMessage;
+    }
+
+    /**
+     * Returns the error message for this shader error.
+     *
+     * @return a short error message describing this shader error.
+     */
+    public String getErrorMessage() {
+	return errorMessage;
+    }
+
+    /**
+     * Sets the detail message for this shader error. This is a
+     * detailed error message, typically produced by the shader
+     * compiler, and is not included as part of toString().
+     *
+     * @param detailMessage a detailed message describing this shader
+     * error in more detail.
+     */
+    public void setDetailMessage(String detailMessage) {
+	this.detailMessage = detailMessage;
+    }
+
+    /**
+     * Returns the detail message for this shader error.
+     *
+     * @return the detail message for this shader error.
+     */
+    public String getDetailMessage() {
+	return detailMessage;
+    }
+
+    /**
+     * Sets the canvas associated with this shader error.
+     *
+     * @param canvas the canvas associated with this shader error.
+     */
+    public void setCanvas3D(Canvas3D canvas) {
+	this.canvas = canvas;
+    }
+
+    /**
+     * Returns the canvas associated with this shader error.
+     *
+     * @return the canvas associated with this shader error.
+     */
+    public Canvas3D getCanvas3D() {
+	return this.canvas;
+    }
+
+    /**
+     * Sets the shape node associated with this shader error.
+     *
+     * @param shape the shape node associated with this shader error.
+     */
+    public void setShape3D(Shape3D shape) {
+	this.shape = shape;
+    }
+
+    /**
+     * Returns the shape node associated with this shader error.
+     *
+     * @return the shape node associated with this shader error.
+     */
+    public Shape3D getShape3D() {
+	return this.shape;
+    }
+
+    /**
+     * Sets the geometry associated with this shader error.
+     *
+     * @param geometry the geometry associated with this shader error.
+     */
+    public void setGeometry(Geometry geometry) {
+	this.geometry = geometry;
+    }
+
+    /**
+     * Returns the geometry associated with this shader error.
+     *
+     * @return the geometry associated with this shader error.
+     */
+    public Geometry getGeometry() {
+	return this.geometry;
+    }
+
+    /**
+     * Sets the shader appearance associated with this shader error.
+     *
+     * @param shaderApp the shader appearance associated with this shader error.
+     */
+    public void setShaderAppearance(ShaderAppearance shaderApp) {
+	this.shaderApp = shaderApp;
+    }
+
+    /**
+     * Returns the shader appearance associated with this shader error.
+     *
+     * @return the shader appearance associated with this shader error.
+     */
+    public ShaderAppearance getShaderAppearance() {
+	return this.shaderApp;
+    }
+
+    /**
+     * Sets the shader program associated with this shader error.
+     *
+     * @param shaderProgram the shader program associated with this shader error.
+     */
+    public void setShaderProgram(ShaderProgram shaderProgram) {
+	this.shaderProgram = shaderProgram;
+    }
+
+    /**
+     * Returns the shader program associated with this shader error.
+     *
+     * @return the shader program associated with this shader error.
+     */
+    public ShaderProgram getShaderProgram() {
+	return this.shaderProgram;
+    }
+
+    /**
+     * Sets the shader object associated with this shader error.
+     *
+     * @param shader the shader object associated with this shader error.
+     */
+    public void setShader(Shader shader) {
+	this.shader = shader;
+    }
+
+    /**
+     * Returns the shader object associated with this shader error.
+     *
+     * @return the shader object associated with this shader error.
+     */
+    public Shader getShader() {
+	return this.shader;
+    }
+
+    /**
+     * Sets the shader attribute set associated with this shader error.
+     *
+     * @param shaderAttributeSet the shader attribute set associated with this shader error.
+     */
+    public void setShaderAttributeSet(ShaderAttributeSet shaderAttributeSet) {
+	this.shaderAttributeSet = shaderAttributeSet;
+    }
+
+    /**
+     * Returns the shader attribute set associated with this shader error.
+     *
+     * @return the shader attribute set associated with this shader error.
+     */
+    public ShaderAttributeSet getShaderAttributeSet() {
+	return this.shaderAttributeSet;
+    }
+
+    /**
+     * Sets the shader attribute associated with this shader error.
+     *
+     * @param shaderAttribute the shader attribute associated with this shader error.
+     */
+    public void setShaderAttribute(ShaderAttribute shaderAttribute) {
+	this.shaderAttribute = shaderAttribute;
+    }
+
+    /**
+     * Returns the shader attribute associated with this shader error.
+     *
+     * @return the shader attribute associated with this shader error.
+     */
+    public ShaderAttribute getShaderAttribute() {
+	return this.shaderAttribute;
+    }
+
+
+    /**
+     * Returns a short string that describes this shader error. The
+     * string is composed of the textual description of the errorCode,
+     * a ": ", and the errorMessage field.  If the errorMessage is
+     * null then the ": " and the errorMessage are omitted.
+     *
+     * @return a string representation of this shader error.
+     */
+    public String toString() {
+	// Concatenate string representation of error code with error message
+	String errorCodeStr;
+	switch (errorCode) {
+	case NO_ERROR:
+	    errorCodeStr = "NO_ERROR";
+	    break;
+	case COMPILE_ERROR:
+	    errorCodeStr = "COMPILE_ERROR";
+	    break;
+	case LINK_ERROR:
+	    errorCodeStr = "LINK_ERROR";
+	    break;
+	case VERTEX_ATTRIBUTE_LOOKUP_ERROR:
+	    errorCodeStr = "VERTEX_ATTRIBUTE_LOOKUP_ERROR";
+	    break;
+	case SHADER_ATTRIBUTE_LOOKUP_ERROR:
+	    errorCodeStr = "SHADER_ATTRIBUTE_LOOKUP_ERROR";
+	    break;
+        case SHADER_ATTRIBUTE_NAME_NOT_SET_ERROR:
+	    errorCodeStr = "SHADER_ATTRIBUTE_NAME_NOT_SET_ERROR";
+	    break;
+	case SHADER_ATTRIBUTE_TYPE_ERROR:
+	    errorCodeStr = "SHADER_ATTRIBUTE_TYPE_ERROR";
+	    break;
+	case UNSUPPORTED_LANGUAGE_ERROR:
+	    errorCodeStr = "UNSUPPORTED_LANGUAGE_ERROR";
+	    break;
+	default:
+	    errorCodeStr = "UNKNOWN ERROR CODE (" + errorCode + ")";
+	}
+
+	if (errorMessage == null) {
+	    return errorCodeStr;
+	}
+
+	return errorCodeStr + ": " + errorMessage;
+    }
+}
diff --git a/src/classes/share/javax/media/j3d/ShaderErrorListener.java b/src/classes/share/javax/media/j3d/ShaderErrorListener.java
new file mode 100644
index 0000000..978a251
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderErrorListener.java
@@ -0,0 +1,33 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * Listener interface for monitoring errors in Shader Programs.
+ * Compile and link errors are reported by the shader compiler, as are
+ * runtime errors, such as those resulting from shader attributes that
+ * aren't found or are of the wrong type.
+ *
+ * @see VirtualUniverse#addShaderErrorListener
+ *
+ * @since Java 3D 1.4
+ */
+public interface ShaderErrorListener {
+    /**
+     * Invoked when an error occurs while compiling, linking or
+     * executing a programmable shader.
+     *
+     * @param error object that contains the details of the error.
+     */
+    public void errorOccurred(ShaderError error);
+}
diff --git a/src/classes/share/javax/media/j3d/ShaderProgram.java b/src/classes/share/javax/media/j3d/ShaderProgram.java
new file mode 100644
index 0000000..2d4fc72
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderProgram.java
@@ -0,0 +1,194 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The ShaderProgram node component object is the abstract base class
+ * for programmable shader programs. Each concrete instance of a
+ * ShaderProgram is a container for a set of Shader objects. The set
+ * of Shaders contained in the ShaderProgram is a complete program for
+ * the Graphics Pipeline Unit (GPU) of the graphics accelerator. It is
+ * specified using the shader language defined by the
+ * ShaderProgram. The currently defined shader languages are: Cg and
+ * GLSL.
+ *
+ * <p>
+ * NOTE: Applications should <i>not</i> extend this class.
+ *
+ * @see Shader
+ * @see ShaderAppearance#setShaderProgram
+ *
+ * @since Java 3D 1.4
+ */
+
+public abstract class ShaderProgram extends NodeComponent {
+
+    /**
+     * Specifies that this ShaderProgram object allows reading
+     * its shaders.
+     */
+    public static final int ALLOW_SHADERS_READ =
+	CapabilityBits.SHADER_PROGRAM_ALLOW_SHADERS_READ;
+
+    /**
+     * Specifies that this ShaderProgram object allows reading
+     * its shader or vertex attribute names.
+     */
+    public static final int ALLOW_NAMES_READ =
+	CapabilityBits.SHADER_PROGRAM_ALLOW_NAMES_READ;
+
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_SHADERS_READ,
+        ALLOW_NAMES_READ        
+    };
+    
+    /*
+     * Default values (copied from GeometryArray.java):
+     *
+     * vertexAttrNames : null<br>
+     */
+
+    /**
+     * Package scope constructor so it can't be subclassed by classes
+     * outside the javax.media.j3d package.
+     */
+    ShaderProgram() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+    }
+
+    /**
+     * Sets the vertex attribute names array for this ShaderProgram
+     * object. Each element in the array specifies the shader
+     * attribute name that is bound to the corresponding numbered
+     * vertex attribute within a GeometryArray object that uses this
+     * shader program. Array element 0 specifies the name of
+     * GeometryArray vertex attribute 0, array element 1 specifies the
+     * name of GeometryArray vertex attribute 1, and so forth.
+     * The array of names may be null or empty (0 length), but the
+     * elements of the array must be non-null.
+     *
+     * @param vertexAttrNames array of vertex attribute names for this
+     * shader program. A copy of this array is made.
+     *
+     * @exception RestrictedAccessException if the method is called
+     * when this object is part of live or compiled scene graph.
+     *
+     * @exception NullPointerException if any element in the
+     * vertexAttrNames array is null.
+     */
+    public abstract void setVertexAttrNames(String[] vertexAttrNames);
+
+    /**
+     * Retrieves the vertex attribute names array from this
+     * ShaderProgram object.
+     *
+     * @exception CapabilityNotSetException if appropriate capability is
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @return a copy of this ShaderProgram's array of vertex attribute names.
+     */
+    public abstract String[] getVertexAttrNames();
+
+
+    /**
+     * Sets the shader attribute names array for this ShaderProgram
+     * object. Each element in the array specifies a shader
+     * attribute name that may be set via a ShaderAttribute object.
+     * Only those attributes whose names that appear in the shader
+     * attribute names array can be set for a given shader program.
+     * The array of names may be null or empty (0 length), but the
+     * elements of the array must be non-null.
+     *
+     * <p>
+     * TODO: finish this.
+     *
+     * @param shaderAttrNames array of shader attribute names for this
+     * shader program. A copy of this array is made.
+     *
+     * @exception RestrictedAccessException if the method is called
+     * when this object is part of live or compiled scene graph.
+     *
+     * @exception NullPointerException if any element in the
+     * shaderAttrNames array is null.
+     */
+    public abstract void setShaderAttrNames(String[] shaderAttrNames);
+
+    /**
+     * Retrieves the shader attribute names array from this
+     * ShaderProgram object.
+     *
+     * @exception CapabilityNotSetException if appropriate capability is
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @return a copy of this ShaderProgram's array of shader attribute names.
+     */
+    public abstract String[] getShaderAttrNames();
+
+
+    /**
+     * Copies the specified array of shaders into this shader
+     * program. This method makes a shallow copy of the array. The
+     * array of shaders may be null or empty (0 length), but the
+     * elements of the array must be non-null. The shading
+     * language of each shader in the array must match the
+     * subclass. Subclasses may impose additional restrictions.
+     *
+     * @param shaders array of Shader objects to be copied into this
+     * ShaderProgram
+     *
+     * @exception RestrictedAccessException if the method is called
+     * when this object is part of live or compiled scene graph.
+     *
+     * @exception IllegalArgumentException if the shading language of
+     * any shader in the shaders array doesn't match the type of the
+     * subclass.
+     *
+     * @exception NullPointerException if any element in the
+     * shaders array is null.
+     */
+    public abstract void setShaders(Shader[] shaders);
+    
+    /**
+     * Retrieves the array of shaders from this shader program. A
+     * shallow copy of the array is returned. The return value may
+     * be null.
+     *
+     * @return a copy of this ShaderProgram's array of Shader objects
+     *
+     * @exception CapabilityNotSetException if appropriate capability is
+     * not set and this object is part of live or compiled scene graph
+     */
+    public abstract Shader[] getShaders();
+
+
+    // Default shader error listener class
+    private static ShaderErrorListener defaultErrorListener = null;
+
+    synchronized static ShaderErrorListener getDefaultErrorListener() {
+	if (defaultErrorListener == null) {
+	    defaultErrorListener = new DefaultErrorListener();
+	}
+
+	return defaultErrorListener;
+    }
+
+    static class DefaultErrorListener implements ShaderErrorListener {
+	public void errorOccurred(ShaderError error) {
+            System.err.println();
+	    System.err.println("DefaultShaderErrorListener.errorOccurred:");
+	    error.printVerbose();
+	}
+    }
+}
diff --git a/src/classes/share/javax/media/j3d/ShaderProgramRetained.java b/src/classes/share/javax/media/j3d/ShaderProgramRetained.java
new file mode 100644
index 0000000..fadcda0
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderProgramRetained.java
@@ -0,0 +1,1196 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.*;
+import javax.vecmath.*;
+
+/**
+ * The ShaderProgramRetained object is a component object of an AppearanceRetained 
+ * object that defines the shader properties used when programmable shader is
+ * enabled. ShaderProgramRetained object is an abstract class. All shader program 
+ * objects must be created as either a GLSLShaderProgramRetained object or a
+ * CgShaderProgramRetained object.
+ */
+abstract class ShaderProgramRetained extends NodeComponentRetained {
+
+    // Each element in the array corresponds to a unique renderer if shared
+    // context or a unique canvas otherwise.
+    protected ShaderProgramData shaderProgramData[];
+
+    // Flag indicating whether an UNSUPPORTED_LANGUAGE_ERROR has
+    // already been reported for this shader program object.  It is
+    // set in verifyShaderProgram and cleared in setLive or clearLive.
+    // TODO KCR: Add code to clear this in setLive or clearLive
+    private boolean unsupportedErrorReported = false;
+
+    // Flag indicating whether a LINK_ERROR has occurred for this shader program
+    // object.  It is set in updateNative to indicate that the linkShaderProgram
+    // operation failed. It is cleared in setLive or clearLive.
+    // TODO KCR: Add code to clear this in setLive or clearLive
+    private boolean linkErrorOccurred = false;
+
+    // an array of shaders used by this shader program
+    protected ShaderRetained[] shaders;
+
+    // an array of vertex attribute names
+    protected String[] vertexAttrNames;
+
+    // an array of (uniform) shader attribute names
+    protected String[] shaderAttrNames;
+    
+    // Set of ShaderAttribute objects for which we have already reported an error
+    private HashSet shaderAttrErrorSet = null;
+
+    // need to synchronize access from multiple rendering threads 
+    Object resourceLock = new Object();
+
+    /**
+     * Sets the vertex attribute names array for this ShaderProgram
+     * object. Each element in the array specifies the shader
+     * attribute name that is bound to the corresponding numbered
+     * vertex attribute within a GeometryArray object that uses this
+     * shader program. Array element 0 specifies the name of
+     * GeometryArray vertex attribute 0, array element 1 specifies the
+     * name of GeometryArray vertex attribute 1, and so forth.
+     * The array of names may be null or empty (0 length), but the
+     * elements of the array must be non-null.
+     *
+     * @param vertexAttrNames array of vertex attribute names for this
+     * shader program. A copy of this array is made.
+     */
+    void setVertexAttrNames(String[] vertexAttrNames) {
+        if (vertexAttrNames == null) {
+            this.vertexAttrNames = null;
+        }
+        else {
+            this.vertexAttrNames = (String[])vertexAttrNames.clone();
+        }
+    }
+
+
+    /**
+     * Retrieves the vertex attribute names array from this
+     * ShaderProgram object.
+     *
+     * @return a copy of this ShaderProgram's array of vertex attribute names.
+     */
+    String[] getVertexAttrNames() {
+
+        if (vertexAttrNames == null) {
+	    return null;
+	} 
+	
+	return (String[])vertexAttrNames.clone();
+
+    }
+
+
+    /**
+     * Sets the shader attribute names array for this ShaderProgram
+     * object. Each element in the array specifies a shader
+     * attribute name that may be set via a ShaderAttribute object.
+     * Only those attributes whose names that appear in the shader
+     * attribute names array can be set for a given shader program.
+     * The array of names may be null or empty (0 length), but the
+     * elements of the array must be non-null.
+     *
+     * @param shaderAttrNames array of shader attribute names for this
+     * shader program. A copy of this array is made.
+     */
+    void setShaderAttrNames(String[] shaderAttrNames) {
+        if (shaderAttrNames == null) {
+            this.shaderAttrNames = null;
+        }
+        else {
+            this.shaderAttrNames = (String[])shaderAttrNames.clone();
+        }
+    }
+
+
+    /**
+     * Retrieves the shader attribute names array from this
+     * ShaderProgram object.
+     *
+     * @return a copy of this ShaderProgram's array of shader attribute names.
+     */
+
+    String[] getShaderAttrNames() {
+
+        if (shaderAttrNames == null) {
+	    return null;
+	} 
+	
+	return (String[])shaderAttrNames.clone();
+
+    }
+
+
+
+    /**
+     * Copies the specified array of shaders into this shader
+     * program. This method makes a shallow copy of the array. The
+     * array of shaders may be null or empty (0 length), but the
+     * elements of the array must be non-null. The shading
+     * language of each shader in the array must match the
+     * subclass. Subclasses may impose additional restrictions.
+     *
+     * @param shaders array of Shader objects to be copied into this
+     * ShaderProgram
+     *
+     * @exception CapabilityNotSetException if appropriate capability is
+     * not set and this object is part of live or compiled scene graph
+     *
+     * @exception IllegalArgumentException if the shading language of
+     * any shader in the shaders array doesn't match the type of the
+     * subclass.
+     */
+    void setShaders(Shader[] shaders) {
+
+	if (shaders == null) {
+	    this.shaders = null;
+	    return;
+	}
+	
+	this.shaders = new ShaderRetained[shaders.length];
+
+	// Copy vertex and fragment shader
+	for (int i = 0; i < shaders.length; i++) {
+	    this.shaders[i] = (ShaderRetained)shaders[i].retained;
+	}
+
+    }
+
+    /**
+     * Retrieves the array of shaders from this shader program. A
+     * shallow copy of the array is returned. The return value may
+     * be null.
+     *
+     * @return a copy of this ShaderProgram's array of Shader objects
+     *
+     */
+    Shader[] getShaders() {
+
+	if (shaders == null) {
+	    return null;
+	} else {
+	    Shader shads[] = 
+		new Shader[shaders.length];
+	    for (int i = 0; i < shaders.length; i++) {
+		if (shaders[i] != null) {
+		    shads[i] = (Shader) shaders[i].source;
+		} else {
+		    shads[i] = null;
+		}
+	    }
+	    return shads;
+	}
+    }    
+    
+    /**
+     * Method to create the native shader.
+     */
+    abstract ShaderError createShader(long ctx, ShaderRetained shader, long[] shaderIdArr); 
+
+    /**
+     * Method to destroy the native shader.
+     */
+    abstract ShaderError destroyShader(long ctx, long shaderId);
+
+    /**
+     * Method to compile the native shader.
+     */
+    abstract ShaderError compileShader(long ctx, long shaderId, String source);
+
+    /**
+     * Method to create the native shader program.
+     */
+    abstract ShaderError createShaderProgram(long ctx, long[] shaderProgramIdArr);
+
+    /**
+     * Method to destroy the native shader program.
+     */
+    abstract ShaderError destroyShaderProgram(long ctx, long shaderProgramId);
+
+    /**
+     * Method to link the native shader program.
+     */
+    abstract ShaderError linkShaderProgram(long ctx, long shaderProgramId, long[] shaderIds);
+
+    /**
+     * Method to bind a vertex attribute name to the specified index.
+     */
+    abstract ShaderError bindVertexAttrName(long ctx, long shaderProgramId, String attrName, int attrIndex);
+
+    /**
+     * Method to lookup a list of (uniform) shader attribute names and return
+     * information about the attributes.
+     */
+    abstract void lookupShaderAttrNames(long ctx, long shaderProgramId, String[] attrNames, AttrNameInfo[] attrNameInfoArr);
+    
+    /*
+     * Method to lookup a list of vertex attribute names.
+     */
+    abstract void lookupVertexAttrNames(long ctx, long shaderProgramId, String[] attrNames, boolean[] errArr);
+
+    /**
+     * Method to use the native shader program.
+     */
+    abstract ShaderError enableShaderProgram(long ctx, long shaderProgramId);
+    
+    /**
+     * Method to disable the native shader program.
+     */
+    abstract ShaderError disableShaderProgram(long ctx);
+    
+    // ShaderAttributeValue methods
+
+    abstract ShaderError setUniform1i(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    int value);
+    
+    abstract ShaderError setUniform1f(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    float value);
+    
+    abstract ShaderError setUniform2i(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    int[] value);
+    
+    abstract ShaderError setUniform2f(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    float[] value);
+    
+    abstract ShaderError setUniform3i(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    int[] value);
+    
+    abstract ShaderError setUniform3f(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    float[] value);    
+    
+    abstract ShaderError setUniform4i(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    int[] value);
+    
+    abstract ShaderError setUniform4f(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    float[] value);    
+    
+    abstract ShaderError setUniformMatrix3f(long ctx,
+					   long shaderProgramId,
+				           long uniformLocation,
+					   float[] value);
+
+    abstract ShaderError setUniformMatrix4f(long ctx,
+					   long shaderProgramId,
+			         	   long uniformLocation,
+					   float[] value);
+
+
+    // ShaderAttributeArray methods
+    
+    abstract ShaderError setUniform1iArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      int[] value);
+    
+    abstract ShaderError setUniform1fArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      float[] value);
+    
+    abstract ShaderError setUniform2iArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      int[] value);
+    
+    abstract ShaderError setUniform2fArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      float[] value);
+    
+    abstract ShaderError setUniform3iArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      int[] value);
+    
+    abstract ShaderError setUniform3fArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      float[] value);    
+    
+    abstract ShaderError setUniform4iArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      int[] value);
+    
+    abstract ShaderError setUniform4fArray(long ctx,
+				      long shaderProgramId,
+				      long uniformLocation,
+				      int numElements,
+				      float[] value);    
+    
+    abstract ShaderError setUniformMatrix3fArray(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    int numElements,
+					    float[] value);
+
+    abstract ShaderError setUniformMatrix4fArray(long ctx,
+					    long shaderProgramId,
+					    long uniformLocation,
+					    int numElements,
+					    float[] value);
+
+    
+    /**
+     * Method to return a flag indicating whether this
+     * ShaderProgram is supported on the specified Canvas.
+     */
+    abstract boolean isSupported(Canvas3D cv);
+
+
+    void setLive(boolean backgroundGroup, int refCount) {
+	
+	// System.out.println("ShaderProgramRetained.setLive()");
+
+	if (shaders != null) {
+	    for (int i = 0; i < shaders.length; i++){
+		shaders[i].setLive(backgroundGroup, refCount);
+	    }
+	}
+	
+	super.doSetLive(backgroundGroup, refCount);
+        super.markAsLive();
+
+    }
+
+    void clearLive(int refCount) {
+
+        // System.out.println("ShaderProgramRetained.clearLive()");
+
+	super.clearLive(refCount);
+
+	if (shaders != null) {
+	    for (int i = 0; i < shaders.length; i++) {
+		shaders[i].clearLive(refCount);
+	    }
+	}
+    }
+
+    /**
+     * Method to enable the native shader program.
+     */
+    private ShaderError enableShaderProgram(Canvas3D cv, int cvRdrIndex) {
+        assert(cvRdrIndex >= 0);
+	synchronized(resourceLock) {
+            return enableShaderProgram(cv.ctx, 
+				       shaderProgramData[cvRdrIndex].getShaderProgramId());
+	}
+
+    }
+
+    /**
+     * Method to disable the native shader program.
+     */
+    private ShaderError disableShaderProgram(Canvas3D cv) {
+        return disableShaderProgram(cv.ctx);
+    }
+    
+    /**
+     * Initializes a mirror object.
+     */
+    synchronized void initMirrorObject() {
+        
+        // Create mirror copy of shaders
+        if (this.shaders == null) {
+            ((ShaderProgramRetained)mirror).shaders = null;
+        }
+        else {
+            ((ShaderProgramRetained)mirror).shaders = new ShaderRetained[this.shaders.length];
+            // Copy vertex and fragment shader
+            for (int i = 0; i < this.shaders.length; i++) {
+                ((ShaderProgramRetained)mirror).shaders[i] =
+                        (ShaderRetained)this.shaders[i].mirror;
+            }
+        }
+        ((ShaderProgramRetained)mirror).shaderProgramData = null;
+        
+        // Create mirror copy of vertex attribute names
+        if (this.vertexAttrNames == null) {
+            ((ShaderProgramRetained)mirror).vertexAttrNames = null;
+        }
+        else {
+            ((ShaderProgramRetained)mirror).vertexAttrNames = (String[])this.vertexAttrNames.clone();
+        }
+        
+        // Create mirror copy of shader attribute names
+        if (this.shaderAttrNames == null) {
+            ((ShaderProgramRetained)mirror).shaderAttrNames = null;
+        }
+        else {
+            ((ShaderProgramRetained)mirror).shaderAttrNames = (String[])this.shaderAttrNames.clone();
+        }
+        
+        // Clear shader attribute error set
+        ((ShaderProgramRetained)mirror).shaderAttrErrorSet = null;
+    }
+    
+    /**
+     * Update the "component" field of the mirror object with the  given "value"
+     */
+    synchronized void updateMirrorObject(int component, Object value) {
+	
+	// ShaderProgram can't be modified once it is live.
+	assert(false);	
+	System.out.println("ShaderProgramRetained : updateMirrorObject NOT IMPLEMENTED YET");
+    } 
+
+    /**
+     * Method to create a ShaderProgramData object for the specified
+     * canvas/renderer if it doesn't already exist
+     */
+    private void createShaderProgramData(int cvRdrIndex) {
+        // Create shaderProgram resources if it has not been done.
+        synchronized(resourceLock) {
+	    if(shaderProgramData == null) {
+                // We rely on Java to initial the array elements to null.
+                shaderProgramData = new ShaderProgramData[cvRdrIndex+1];
+	    }
+	    else if(shaderProgramData.length <= cvRdrIndex) {
+                // We rely on Java to initial the array elements to null.
+		ShaderProgramData[] tempSPData = new ShaderProgramData[cvRdrIndex+1];
+                System.arraycopy(shaderProgramData, 0,
+                        tempSPData, 0,
+                        shaderProgramData.length);
+                shaderProgramData = tempSPData;
+	    }
+
+            if(shaderProgramData[cvRdrIndex] == null) {
+                shaderProgramData[cvRdrIndex] = new ShaderProgramData();
+            }
+        }
+    }
+
+    /**
+     * Method to create the native shader program. We must already have
+     * called createShaderProgramData for this cvRdrIndex.
+     */
+    private ShaderError createShaderProgram(Canvas3D cv, int cvRdrIndex) {
+        // Create shaderProgram resources if it has not been done.
+        synchronized(resourceLock) {
+            assert(shaderProgramData[cvRdrIndex].getShaderProgramId() == 0);
+
+            long[] spIdArr = new long[1];
+            ShaderError err = createShaderProgram(cv.ctx, spIdArr);
+            if(err != null) {
+                return err;
+            }
+            shaderProgramData[cvRdrIndex].setShaderProgramId(spIdArr[0]);
+        }
+        
+        return null;
+    }
+
+    /**
+     * Method to link the native shader program.
+     */
+    private ShaderError linkShaderProgram(Canvas3D cv, int cvRdrIndex, 
+					  ShaderRetained[] shaders) {
+	synchronized(resourceLock) {
+            long[] shaderIds = new long[shaders.length];
+	    for(int i=0; i<shaders.length; i++) {
+                synchronized(shaders[i]) {
+                    shaderIds[i] = shaders[i].shaderIds[cvRdrIndex];
+                }
+	    }
+	    ShaderError err = 
+		linkShaderProgram(cv.ctx, 
+				  shaderProgramData[cvRdrIndex].getShaderProgramId(),
+				  shaderIds);
+            if(err != null) {
+                return err;
+            }
+	    shaderProgramData[cvRdrIndex].setLinked(true);
+	}
+
+	return null;
+    }
+
+    
+    private ShaderError bindVertexAttrName(Canvas3D cv, int cvRdrIndex, String attrName, int attrIndex) {
+        assert(attrName != null);
+        synchronized(resourceLock) {
+            long shaderProgramId = shaderProgramData[cvRdrIndex].getShaderProgramId();
+//            System.err.println("attrName = " + attrName);
+            ShaderError err = bindVertexAttrName(cv.ctx, shaderProgramId, attrName, attrIndex);
+            if (err != null) {
+                return err;
+            }
+        }
+        return null;
+    }
+    
+    private void lookupVertexAttrNames(Canvas3D cv, int cvRdrIndex, String[] attrNames) {
+        synchronized(resourceLock) {
+            long shaderProgramId = shaderProgramData[cvRdrIndex].getShaderProgramId();
+
+            boolean[] errArr = new boolean[attrNames.length];
+            lookupVertexAttrNames(cv.ctx, shaderProgramId, attrNames, errArr);
+        
+            for (int i = 0; i < attrNames.length; i++) {
+                // Report non-fatal error if detected
+                if (errArr[i]) {
+                    String errMsg = "Vertex Attribute name lookup failed: " + attrNames[i];
+                    ShaderError err = new ShaderError(ShaderError.VERTEX_ATTRIBUTE_LOOKUP_ERROR, errMsg);
+                    err.setShaderProgram((ShaderProgram)this.source);
+                    err.setCanvas3D(cv);
+                    notifyErrorListeners(cv, err);
+                }
+            }
+        }
+    }
+
+
+    private void lookupShaderAttrNames(Canvas3D cv, int cvRdrIndex, String[] attrNames) {
+        synchronized(resourceLock) {
+            long shaderProgramId = shaderProgramData[cvRdrIndex].getShaderProgramId();
+
+            AttrNameInfo[] attrNameInfoArr = new AttrNameInfo[attrNames.length];
+            lookupShaderAttrNames(cv.ctx, shaderProgramId, attrNames, attrNameInfoArr);
+        
+            for (int i = 0; i < attrNames.length; i++) {
+                shaderProgramData[cvRdrIndex].setAttrNameInfo(attrNames[i], attrNameInfoArr[i]);
+                
+                // Report non-fatal error if location is invalid (-1)
+                if (attrNameInfoArr[i].getLocation() == -1) {
+                    String errMsg = "Attribute name lookup failed: " + attrNames[i];
+                    ShaderError err = new ShaderError(ShaderError.SHADER_ATTRIBUTE_LOOKUP_ERROR, errMsg);
+                    err.setShaderProgram((ShaderProgram)this.source);
+                    err.setCanvas3D(cv);
+                    notifyErrorListeners(cv, err);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Method to return the shaderProgram data for the specified canvas or renderer
+     */
+    private ShaderProgramData getShaderProgramData(int cvRdrIndex) {
+        synchronized(resourceLock) {
+            return shaderProgramData[cvRdrIndex];
+        }
+    }
+
+    /**
+     * Method to create the native shader.
+     */
+    private ShaderError createShader(Canvas3D cv, int cvRdrIndex, ShaderRetained shader) {
+        
+        // Create shaderProgram resources if it has not been done.
+        synchronized(shader.resourceLock) {
+            if(shader.shaderIds == null){
+                // We rely on Java to initial the array elements to 0 or false;
+                shader.shaderIds = new long[cvRdrIndex+1];
+                shader.compiled = new boolean[cvRdrIndex+1];
+            } else if( shader.shaderIds.length <= cvRdrIndex) {
+                // We rely on Java to initial the array elements to 0 or false;
+                long[] tempSIds = new long[cvRdrIndex+1];
+                boolean[] tempCompiled = new boolean[cvRdrIndex+1];
+                
+                System.arraycopy(shader.shaderIds, 0,
+                        tempSIds, 0,
+                        shader.shaderIds.length);
+                shader.shaderIds = tempSIds;
+                
+                System.arraycopy(shader.compiled, 0,
+                        tempCompiled, 0,
+                        shader.compiled.length);
+                shader.compiled = tempCompiled;
+            }
+            
+            if(shader.shaderIds[cvRdrIndex] != 0) {
+                // We have already created the shaderId for this Canvas.
+                return null;
+            }
+            
+            long[] shaderIdArr = new long[1];
+            ShaderError err = createShader(cv.ctx, shader, shaderIdArr);
+            if(err != null) {
+                return err;
+            }
+            shader.shaderIds[cvRdrIndex] = shaderIdArr[0];
+        }
+        return null;
+    }
+
+    /**
+     * Method to compile the native shader.
+     */
+    private ShaderError compileShader(Canvas3D cv, int cvRdrIndex, ShaderRetained shader) {
+        
+        synchronized(shader.resourceLock) {
+            
+            if(shader.compiled[cvRdrIndex] == true) {
+                // We have already compiled the shaderId for this Canvas.
+                return null;
+            }
+            
+	    String source = ((SourceCodeShaderRetained)shader).getShaderSource();
+            ShaderError err = compileShader(cv.ctx, shader.shaderIds[cvRdrIndex], source);
+            if(err != null) {
+                return err;
+            }
+            shader.compiled[cvRdrIndex] = true;
+        }
+        
+        return null;
+    }
+    
+    /**
+     * Send a message to the notification thread, which will call the
+     * shader error listeners.
+     */
+    void notifyErrorListeners(Canvas3D cv, ShaderError err) {
+        J3dNotification notification = new J3dNotification();
+        notification.type = J3dNotification.SHADER_ERROR;
+        notification.universe = cv.view.universe;
+        notification.args[0] = err;
+        VirtualUniverse.mc.sendNotification(notification);
+    }
+    
+    
+    /**
+     * This method checks whether this ShaderProgram is supported on
+     * the specified Canvas. If it isn't supported, it will report a
+     * ShaderError unless an error has already been reported for this
+     * shader program.
+     */
+    private boolean verifyShaderProgramSupported(Canvas3D cv) {
+        boolean supported = isSupported(cv);
+        if (!supported && !unsupportedErrorReported) {
+            String errorMsg = J3dI18N.getString("ShaderProgramRetained0");
+            ShaderError err = new ShaderError(ShaderError.UNSUPPORTED_LANGUAGE_ERROR, errorMsg);
+            err.setShaderProgram((ShaderProgram)this.source);
+            err.setCanvas3D(cv);
+            notifyErrorListeners(cv, err);
+            unsupportedErrorReported = true;
+        }
+        return supported;
+    }
+
+    /**
+     * Method to destroy the native shader.
+     */
+    void destroyShader(Canvas3D cv, int cvRdrIndex, ShaderRetained shader) {
+        if (!verifyShaderProgramSupported(cv)) {
+            return;
+        }
+
+        // Destroy shader resource if it exists
+        synchronized(shader.resourceLock) {
+            // Check whether an entry in the shaderIds array has been allocated
+            if (shader.shaderIds == null || shader.shaderIds.length <= cvRdrIndex) {
+                return;
+            }
+            
+            // Nothing to do if the shaderId is 0
+            if (shader.shaderIds[cvRdrIndex] == 0) {
+                return;
+            }
+
+            // Destroy the native resource and set the ID to 0 for this canvas/renderer
+            // Ignore any possible shader error, because there is no meaningful way to report it
+            destroyShader(cv.ctx, shader.shaderIds[cvRdrIndex]);
+            shader.shaderIds[cvRdrIndex] = 0;
+        }
+    }
+
+
+    /**
+     * Method to destroy the native shader program.
+     */
+    void destroyShaderProgram(Canvas3D cv, int cvRdrIndex) {
+        if (!verifyShaderProgramSupported(cv)) {
+            return;
+        }
+        
+        // Destroy shaderProgram resource if it exists
+        synchronized(resourceLock) {
+            assert(shaderProgramData != null &&
+                   shaderProgramData.length > cvRdrIndex &&
+                   shaderProgramData[cvRdrIndex] != null);
+
+//            // Check whether an entry in the shaderProgramData array has been allocated
+//            if (shaderProgramData == null ||
+//                    shaderProgramData.length <= cvRdrIndex ||
+//                    shaderProgramData[cvRdrIndex] == null) {
+//                return;
+//            }
+            
+	    long shaderProgramId = shaderProgramData[cvRdrIndex].getShaderProgramId(); 
+            // Nothing to do if the shaderProgramId is 0
+            if (shaderProgramId == 0) {
+                return;
+            }
+
+            // Destroy the native resource, set the ID to 0 for this canvas/renderer,
+            // and clear the bit in the resourceCreationMask
+            // Ignore any possible shader error, because there is no meaningful way to report it
+            destroyShaderProgram(cv.ctx, shaderProgramId);
+            // Reset this ShaderProgramData object.
+	    shaderProgramData[cvRdrIndex].reset();
+        }
+    } 
+
+
+    /**
+     * updateNative is called while traversing the RenderBin to 
+     * update the shader program state
+     */
+    void updateNative(Canvas3D cv, boolean enable) {
+	// System.out.println("ShaderProgramRetained.updateNative : ");
+
+        final boolean useSharedCtx = cv.useSharedCtx && cv.screen.renderer.sharedCtx != 0;
+        final int cvRdrIndex = useSharedCtx ? cv.screen.renderer.rendererId : cv.canvasId;
+
+        // Create ShaderProgramData object for this canvas/renderer if it doesn't already exist
+        createShaderProgramData(cvRdrIndex);
+
+        // Check whether this shader program type is supported for this canvas
+        if (!verifyShaderProgramSupported(cv)) {
+            return;
+        }
+        
+        // Just disable shader program and return if enable parameter is set to false
+        if (!enable) {
+            // Given the current design, disableShaderProgram cannot return a non-null value,
+            // so no need to check it
+            disableShaderProgram(cv);
+            return;
+        }
+
+        // Just disable shader program and return if array of shaders is empty,
+        // or if a previous attempt to link resulted in an error
+        if (shaders == null || shaders.length == 0 || linkErrorOccurred) {
+            disableShaderProgram(cv);
+            return;
+        }
+
+	boolean loadShaderProgram = false; // flag indicating whether to reload all shaderProgram states
+        if (getShaderProgramData(cvRdrIndex).getShaderProgramId() == 0) {
+            loadShaderProgram = true;
+        }
+        
+	//System.out.println(".... loadShaderProgram = " + loadShaderProgram);
+	//System.out.println(".... resourceCreationMask= " + resourceCreationMask);
+ 
+        ShaderError err = null;
+        boolean errorOccurred = false;
+	if (loadShaderProgram) {
+            if (useSharedCtx) {
+	    // TODO : Need to test useSharedCtx case. ** Untested case **
+		cv.makeCtxCurrent(cv.screen.renderer.sharedCtx);
+            }
+        
+            // Create shader resources if not already done
+            for(int i=0; i < shaders.length; i++) {
+                if (shaders[i].compileErrorOccurred) {
+                    errorOccurred = true;
+                }
+                else {
+                    err = createShader(cv, cvRdrIndex, shaders[i]);
+                    if (err != null) {
+                        err.setShaderProgram((ShaderProgram)this.source);
+                        err.setShader((Shader)shaders[i].source);
+                        err.setCanvas3D(cv);
+                        notifyErrorListeners(cv, err);
+                        errorOccurred = true;
+                    }
+                    else {
+                        err = compileShader(cv, cvRdrIndex, shaders[i]);
+                        if (err != null) {
+                            err.setShaderProgram((ShaderProgram)this.source);
+                            err.setShader((Shader)shaders[i].source);
+                            err.setCanvas3D(cv);
+                            notifyErrorListeners(cv, err);
+                            destroyShader(cv, cvRdrIndex, shaders[i]);
+                            shaders[i].compileErrorOccurred = true;
+                            errorOccurred = true;
+                        }
+                    }
+                }
+            }
+            
+            // Create shader program
+            if (!errorOccurred) {
+                err = createShaderProgram(cv, cvRdrIndex);
+                if (err != null) {
+                    err.setShaderProgram((ShaderProgram)this.source);
+                    err.setCanvas3D(cv);
+                    notifyErrorListeners(cv, err);
+                    errorOccurred = true;
+                }
+            }
+            
+            boolean linked = getShaderProgramData(cvRdrIndex).isLinked();
+            if (!linked) {
+                // Bind vertex attribute names
+                if (!errorOccurred) {
+                    if (vertexAttrNames != null) {
+//                        System.err.println("vertexAttrNames.length = " + vertexAttrNames.length);
+                        for (int i = 0; i < vertexAttrNames.length; i++) {
+                            err = bindVertexAttrName(cv, cvRdrIndex, vertexAttrNames[i], i);
+                            // Report non-fatal error, if one was detected
+                            if (err != null) {
+                                err.setShaderProgram((ShaderProgram)this.source);
+                                err.setCanvas3D(cv);
+                                notifyErrorListeners(cv, err);
+                            }
+                        }
+                    }
+                }
+                
+                // Link shader program
+                if (!errorOccurred) {
+                    err = linkShaderProgram(cv, cvRdrIndex, shaders);
+                    if (err != null) {
+                        err.setShaderProgram((ShaderProgram)this.source);
+                        err.setCanvas3D(cv);
+                        notifyErrorListeners(cv, err);
+                        destroyShaderProgram(cv, cvRdrIndex);
+                        linkErrorOccurred = true;
+                        errorOccurred = true;
+                    }
+                }
+                
+                // lookup vertex attribute names
+                if (!errorOccurred) {
+                    if (vertexAttrNames != null) {
+                        lookupVertexAttrNames(cv, cvRdrIndex, vertexAttrNames);
+                    }
+                }
+                
+                // Lookup shader attribute names
+                if (!errorOccurred) {
+                    if (shaderAttrNames != null) {
+//                        System.err.println("shaderAttrNames.length = " + shaderAttrNames.length);
+                        lookupShaderAttrNames(cv, cvRdrIndex, shaderAttrNames);
+                    }
+                }
+            }
+            
+            // Restore current context if we changed it to the shareCtx
+            if (useSharedCtx) {
+                cv.makeCtxCurrent(cv.ctx);
+            }
+	   
+            // If compilation or link error occured, disable shader program and return
+            if (errorOccurred) {
+                disableShaderProgram(cv);
+                return;
+            }
+        }
+ 
+        // Now we can enable the shader program
+	enableShaderProgram(cv, cvRdrIndex);
+    }
+
+    /**
+     * Update native value for ShaderAttributeValue class
+     */
+    ShaderError setUniformAttrValue(long ctx, long shaderProgramId, long loc,
+				    ShaderAttributeValueRetained sav) {
+
+	switch (sav.getClassType()) {
+	case ShaderAttributeObjectRetained.TYPE_INTEGER:
+	    return setUniform1i(ctx, shaderProgramId, loc,
+				((int[])sav.attrWrapper.getRef())[0]);
+
+	case ShaderAttributeObjectRetained.TYPE_FLOAT:
+	    return setUniform1f(ctx, shaderProgramId, loc,
+				((float[])sav.attrWrapper.getRef())[0]);
+
+	case ShaderAttributeObjectRetained.TYPE_TUPLE2I:
+	    return setUniform2i(ctx, shaderProgramId, loc,
+				(int[])sav.attrWrapper.getRef());
+
+	case ShaderAttributeObjectRetained.TYPE_TUPLE2F:
+	    return setUniform2f(ctx, shaderProgramId, loc,
+				(float[])sav.attrWrapper.getRef());
+
+	case ShaderAttributeObjectRetained.TYPE_TUPLE3I:
+	    return setUniform3i(ctx, shaderProgramId, loc,
+				(int[])sav.attrWrapper.getRef());
+
+	case ShaderAttributeObjectRetained.TYPE_TUPLE3F:
+	    return setUniform3f(ctx, shaderProgramId, loc,
+				(float[])sav.attrWrapper.getRef());
+
+	case ShaderAttributeObjectRetained.TYPE_TUPLE4I:
+	    return setUniform4i(ctx, shaderProgramId, loc,
+				(int[])sav.attrWrapper.getRef());
+
+	case ShaderAttributeObjectRetained.TYPE_TUPLE4F:
+	    return setUniform4f(ctx, shaderProgramId, loc,
+				(float[])sav.attrWrapper.getRef());
+
+	case ShaderAttributeObjectRetained.TYPE_MATRIX3F:
+	    return setUniformMatrix3f(ctx, shaderProgramId, loc,
+				      (float[])sav.attrWrapper.getRef());
+
+	case ShaderAttributeObjectRetained.TYPE_MATRIX4F:
+	    return setUniformMatrix4f(ctx, shaderProgramId, loc,
+				      (float[])sav.attrWrapper.getRef());
+
+	default:
+	    // Should never get here
+	    assert false : "Unrecognized ShaderAttributeValue classType";
+	    return null;
+	}
+    }
+    
+     /**
+     * Update native value for ShaderAttributeArray class
+     */
+    ShaderError setUniformAttrArray(long ctx, long shaderProgramId, long loc,
+				    ShaderAttributeArrayRetained saa) {   
+
+        switch (saa.getClassType()) {
+            case ShaderAttributeObjectRetained.TYPE_INTEGER:
+                return  setUniform1iArray(ctx, shaderProgramId, loc, saa.length(),
+                        ((int[])saa.attrWrapper.getRef()));
+                
+            case ShaderAttributeObjectRetained.TYPE_FLOAT:
+                return setUniform1fArray(ctx, shaderProgramId, loc, saa.length(),
+                        ((float[])saa.attrWrapper.getRef()));
+                
+            case ShaderAttributeObjectRetained.TYPE_TUPLE2I:
+                return setUniform2iArray(ctx, shaderProgramId, loc, saa.length(),
+                        (int[])saa.attrWrapper.getRef());
+                
+            case ShaderAttributeObjectRetained.TYPE_TUPLE2F:
+                return setUniform2fArray(ctx, shaderProgramId, loc, saa.length(),
+                        (float[])saa.attrWrapper.getRef());
+                
+            case ShaderAttributeObjectRetained.TYPE_TUPLE3I:
+                return setUniform3iArray(ctx, shaderProgramId, loc, saa.length(),
+                        (int[])saa.attrWrapper.getRef());
+                
+            case ShaderAttributeObjectRetained.TYPE_TUPLE3F:
+                return setUniform3fArray(ctx, shaderProgramId, loc, saa.length(),
+                        (float[])saa.attrWrapper.getRef());
+                
+            case ShaderAttributeObjectRetained.TYPE_TUPLE4I:
+                return setUniform4iArray(ctx, shaderProgramId, loc, saa.length(),
+                        (int[])saa.attrWrapper.getRef());
+                
+            case ShaderAttributeObjectRetained.TYPE_TUPLE4F:
+                return setUniform4fArray(ctx, shaderProgramId, loc, saa.length(),
+                        (float[])saa.attrWrapper.getRef());
+                
+            case ShaderAttributeObjectRetained.TYPE_MATRIX3F:
+                return setUniformMatrix3fArray(ctx, shaderProgramId, loc, saa.length(),
+                        (float[])saa.attrWrapper.getRef());
+                
+            case ShaderAttributeObjectRetained.TYPE_MATRIX4F:
+                return setUniformMatrix4fArray(ctx, shaderProgramId, loc, saa.length(),
+                        (float[])saa.attrWrapper.getRef());
+                
+            default:
+                // Should never get here
+                assert false : "Unrecognized ShaderAttributeArray classType";
+                return null;
+        }
+    
+    }
+    
+    
+    void setShaderAttributes(Canvas3D cv, ShaderAttributeSetRetained attributeSet) {
+        final boolean useSharedCtx = cv.useSharedCtx && cv.screen.renderer.sharedCtx != 0;
+        final int cvRdrIndex = useSharedCtx ? cv.screen.renderer.rendererId : cv.canvasId;        
+        ShaderProgramData spData = getShaderProgramData(cvRdrIndex);
+        
+        // Just return if shader program wasn't linked successfully
+        if (!spData.isLinked()) {
+            return;
+        }
+        
+        long shaderProgramId = spData.getShaderProgramId();
+        
+        Iterator attrs = attributeSet.getAttrs().values().iterator();
+        while (attrs.hasNext()) {
+            ShaderError err = null;
+            ShaderAttributeRetained saRetained = (ShaderAttributeRetained)attrs.next();
+
+            // Lookup attribute info for the specified attrName; null means
+            // that the name does not appear in the ShaderProgram, so we will
+            // report an error.
+            AttrNameInfo attrNameInfo = spData.getAttrNameInfo(saRetained.getAttributeName());
+            if(attrNameInfo == null) {
+//                System.err.println("ShaderProgramRetained : attrLocation (" + saRetained.getAttributeName() + ") is null.");
+                String errMsg = "Attribute name not set in ShaderProgram: " + saRetained.getAttributeName(); // TODO: I18N
+                err = new ShaderError(ShaderError.SHADER_ATTRIBUTE_NAME_NOT_SET_ERROR, errMsg);
+            } else {
+                long loc = attrNameInfo.getLocation();
+                if (loc != -1) {
+                    if (saRetained instanceof ShaderAttributeValueRetained) {
+                        ShaderAttributeValueRetained savRetained = (ShaderAttributeValueRetained)saRetained;
+                        if (attrNameInfo.isArray() ||
+                                (savRetained.getClassType() != attrNameInfo.getType())) {
+                            String errMsg = "Attribute type mismatch: " + savRetained.getAttributeName(); // TODO: I18N
+                            err = new ShaderError(ShaderError.SHADER_ATTRIBUTE_TYPE_ERROR, errMsg);
+                        }
+                        else {
+                            err = setUniformAttrValue(cv.ctx, shaderProgramId, loc, savRetained);
+                        }
+                    } else if (saRetained instanceof ShaderAttributeArrayRetained) {
+                        ShaderAttributeArrayRetained saaRetained = (ShaderAttributeArrayRetained)saRetained;
+                        if (!attrNameInfo.isArray() ||
+                                (saaRetained.getClassType() != attrNameInfo.getType())) {
+                            String errMsg = "Attribute type mismatch: " + saaRetained.getAttributeName(); // TODO: I18N
+                            err = new ShaderError(ShaderError.SHADER_ATTRIBUTE_TYPE_ERROR, errMsg);
+                        }
+                        else {
+                            err = setUniformAttrArray(cv.ctx, shaderProgramId, loc, saaRetained);
+                        }
+                    } else if (saRetained instanceof ShaderAttributeBindingRetained) {
+                        assert false;
+                        throw new RuntimeException("not implemented");
+                    } else {
+                        assert false;
+                    }
+                }
+            }
+            
+            if (err != null) {
+                // Before reporting the ShaderAttribute error, check
+                // whether it has already been reported for this ShaderProgram
+                if (shaderAttrErrorSet == null) {
+                    shaderAttrErrorSet = new HashSet();
+                }
+                if (shaderAttrErrorSet.add(saRetained.source)) {
+                    err.setShaderProgram((ShaderProgram)this.source);
+                    err.setShaderAttributeSet((ShaderAttributeSet)attributeSet.source);
+                    err.setShaderAttribute((ShaderAttribute)saRetained.source);
+                    err.setCanvas3D(cv);
+                    notifyErrorListeners(cv, err);
+                }
+            }
+        }
+    }
+    
+    class ShaderProgramData extends Object {
+	
+	// shaderProgramId use by native code. 
+	private long shaderProgramId = 0;
+	
+	// linked flag for native.
+	private boolean linked = false;
+	
+	// A map of locations for ShaderAttributes.
+	private HashMap attrNameInfoMap = new HashMap();
+
+	/** ShaderProgramData Constructor */
+	ShaderProgramData() {
+	}
+        
+        void reset() {
+            shaderProgramId = 0;
+            linked = false;
+            attrNameInfoMap.clear();
+        }
+
+	void setShaderProgramId(long shaderProgramId) {
+	    this.shaderProgramId = shaderProgramId;
+	}
+
+	long getShaderProgramId() {
+	    return this.shaderProgramId;
+	}
+
+	void setLinked(boolean linked) {
+	    this.linked = linked;
+	}
+
+	boolean isLinked() {
+	    return linked;
+	}
+
+	void setAttrNameInfo(String shaderAttribute, AttrNameInfo attrNameInfo) {
+	    assert(shaderAttribute != null);
+	    attrNameInfoMap.put(shaderAttribute, attrNameInfo);
+	}
+
+	AttrNameInfo getAttrNameInfo(String shaderAttribute) {
+	    return  (AttrNameInfo) attrNameInfoMap.get(shaderAttribute);
+	}
+
+
+    }
+    
+    // Data associated with an attribute name
+    class AttrNameInfo {
+        void setLocation(long loc) {
+            this.loc = loc;
+        }
+
+        long getLocation() {
+            return loc;
+        }
+
+        void setType(int type) {
+            this.type = type;
+        }
+
+        int getType() {
+            return type;
+        }
+        
+        boolean isArray() {
+            return isArray;
+        }
+
+        void setArray(boolean isArray) {
+            this.isArray = isArray;
+        }
+
+        // Location of attribute name in linked shader program
+        private long loc;
+
+        // boolean indicating whether the attribute is an array
+        private boolean isArray;
+        
+        // type of shader attribute
+        private int type;
+    }
+
+}
diff --git a/src/classes/share/javax/media/j3d/ShaderRetained.java b/src/classes/share/javax/media/j3d/ShaderRetained.java
new file mode 100644
index 0000000..a050b50
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/ShaderRetained.java
@@ -0,0 +1,78 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+import java.util.*;
+
+/**
+ * The ShaderRetained object is the abstract base class for programmable
+ * shader code. Currently, only text-based source code shaders are
+ * supported, so the only subclass of Shader is SourceCodeShader. We
+ * leave open the possibility for binary (object code) shaders in the
+ * future.
+ */
+abstract class ShaderRetained extends NodeComponentRetained {
+    int shadingLanguage;
+    int shaderType;
+
+    // shaderId use by native code. One per Canvas.
+    long[] shaderIds;
+    boolean[] compiled;
+
+    // Flag indicating whether a COMPILE_ERROR has occurred for this shader
+    // object.  It is set in updateNative to indicate that the compileShader
+    // operation failed. It is cleared in setLive or clearLive.
+    // TODO KCR: Add code to clear this in setLive or clearLive
+    boolean compileErrorOccurred = false;
+
+    // need to synchronize access from multiple rendering threads 
+    Object resourceLock = new Object();
+
+    void initializeShader(int shadingLanguage, int shaderType) {
+	this.shadingLanguage = shadingLanguage;
+	this.shaderType = shaderType;
+    }
+
+    int getShadingLanguage() {
+	return shadingLanguage;
+    }
+
+    int getShaderType() {
+	return shaderType;
+    }
+ 
+    void setLive(boolean inBackgroundGroup, int refCount) {
+	// System.out.println("SourceCodeShaderRetained.setLive()");
+	super.setLive(inBackgroundGroup, refCount);
+    }
+
+    void clearLive(int refCount) {
+	// System.out.println("SourceCodeShaderRetained.clearLive()");
+	super.clearLive(refCount);
+    }
+
+     /**
+      * Shader object doesn't really have mirror object.
+      * But it's using the updateMirrorObject interface to propagate
+      * the changes to the users
+      */
+     synchronized void updateMirrorObject(int component, Object value) {
+	System.out.println("Shader.updateMirrorObject not implemented yet!");
+     }
+
+    void handleFrequencyChange(int bit) {
+	System.out.println("Shader.handleFrequencyChange not implemented yet!");
+    }
+
+}
+
diff --git a/src/classes/share/javax/media/j3d/Shape3D.java b/src/classes/share/javax/media/j3d/Shape3D.java
index 6a9f5d7..5c4394c 100644
--- a/src/classes/share/javax/media/j3d/Shape3D.java
+++ b/src/classes/share/javax/media/j3d/Shape3D.java
@@ -111,6 +111,14 @@ public class Shape3D extends Leaf {
     public static final int ALLOW_APPEARANCE_OVERRIDE_WRITE =
 	CapabilityBits.SHAPE3D_ALLOW_APPEARANCE_OVERRIDE_WRITE;
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_GEOMETRY_READ,
+        ALLOW_APPEARANCE_READ,
+        ALLOW_COLLISION_BOUNDS_READ,        
+        ALLOW_APPEARANCE_OVERRIDE_READ        
+    };
+    
     /**
      * Constructs a Shape3D node with default parameters.  The default
      * values are as follows:
@@ -127,6 +135,8 @@ public class Shape3D extends Leaf {
      * that default values are used for all appearance attributes.
      */
     public Shape3D() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);        
     }
 
     /**
@@ -141,7 +151,10 @@ public class Shape3D extends Leaf {
      * this shape node.
      */
     public Shape3D(Geometry geometry) {
-	((Shape3DRetained)retained).setGeometry(geometry, 0);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((Shape3DRetained)retained).setGeometry(geometry, 0);
     }
 
     /**
@@ -155,7 +168,10 @@ public class Shape3D extends Leaf {
      * @param appearance the appearance component of the shape node
      */
     public Shape3D(Geometry geometry, Appearance appearance) {
-	((Shape3DRetained)retained).setGeometry(geometry, 0);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((Shape3DRetained)retained).setGeometry(geometry, 0);
 	((Shape3DRetained)retained).setAppearance(appearance);
     }
 
diff --git a/src/classes/share/javax/media/j3d/Shape3DCompileRetained.java b/src/classes/share/javax/media/j3d/Shape3DCompileRetained.java
index e4514d4..a3e604d 100644
--- a/src/classes/share/javax/media/j3d/Shape3DCompileRetained.java
+++ b/src/classes/share/javax/media/j3d/Shape3DCompileRetained.java
@@ -424,81 +424,139 @@ class Shape3DCompileRetained extends Shape3DRetained {
      * @exception IllegalArgumentException if <code>path</code> is
      * invalid.
      */
-
     boolean intersect(SceneGraphPath path,
-		      PickShape pickShape, double[] dist) {
-	
-	// This method will not do bound intersect check, as it assume
-	// caller has already done that. ( For performance and code
-	// simplification reasons. )
-       
-	Transform3D localToVworld = path.getTransform();
-	int i;
-
-	if (localToVworld == null) {
+            PickShape pickShape, double[] dist) {
+        
+        int flags;
+        PickInfo pickInfo = new PickInfo();
+        
+        Transform3D localToVworld = path.getTransform();
+        if (localToVworld == null) {
 	    throw new IllegalArgumentException(J3dI18N.getString("Shape3DRetained3"));   
 	}
-	
-	Transform3D t3d = VirtualUniverse.mc.getTransform3D(null);
-	t3d.invert(localToVworld);	
-	PickShape newPS = pickShape.transform(t3d);
-	FreeListManager.freeObject(FreeListManager.TRANSFORM3D, t3d);
-
-	Shape3D shape  = (Shape3D) path.getObject();
+        pickInfo.setLocalToVWorldRef( localToVworld);
+        
+        Shape3D shape  = (Shape3D) path.getObject();
 	// Get the geometries for this shape only, since the compiled
 	// geomtryList contains several shapes
-	ArrayList glist =  (ArrayList) geometryInfo.get(shape.id);	     
-
-	int geomListSize = glist.size();
-
-	Point3d iPnt = Shape3DRetained.getPoint3d();
+	ArrayList glist =  (ArrayList) geometryInfo.get(shape.id);	        
+        
+        // System.out.println("Shape3DCompileRetained.intersect() : ");
+        if (dist == null) {
+            // System.out.println("      no dist request ....");
+            return intersect(pickInfo, pickShape, 0, glist);
+        }
+        
+        flags = PickInfo.CLOSEST_DISTANCE;
+        if (intersect(pickInfo, pickShape, flags, glist)) {
+            dist[0] = pickInfo.getClosestDistance();
+            return true;
+        }
+        
+        return false;
+          
+      }
+    
+      boolean intersect(PickInfo pickInfo, PickShape pickShape, int flags,
+              ArrayList geometryList) {
+          
+        Transform3D localToVworld = pickInfo.getLocalToVWorldRef();
+                
+ 	Transform3D t3d = new Transform3D();
+	t3d.invert(localToVworld);
+	PickShape newPS = pickShape.transform(t3d);     
+
+	int geomListSize = geometryList.size();
 	GeometryRetained geometry;
 
-	if (dist == null) {
-	    for (i=0; i < geomListSize; i++) {
-		Geometry g =  (Geometry) glist.get(i);	     
-		if (g != null && g.retained != null) {
-		    geometry = (GeometryRetained)g.retained;
+        if (((flags & PickInfo.CLOSEST_INTERSECTION_POINT) == 0) &&
+            ((flags & PickInfo.CLOSEST_DISTANCE) == 0) &&
+            ((flags & PickInfo.CLOSEST_GEOM_INFO) == 0) &&
+            ((flags & PickInfo.ALL_GEOM_INFO) == 0)) {
+            
+	    for (int i=0; i < geomListSize; i++) {
+		geometry =  (GeometryRetained) geometryList.get(i);	     
+		if (geometry != null) {
 		    if (geometry.mirrorGeometry != null) {
 			geometry = geometry.mirrorGeometry;
 		    }
-		    
-		    if (geometry.intersect(newPS, null, iPnt)) {
-			Shape3DRetained.freePoint3d(iPnt);
+                    // Need to modify this method
+		    // if (geometry.intersect(newPS, null, null)) {
+                    if (geometry.intersect(newPS, null, 0, null)) {
 			return true;
 		    }
 		}
 	    }
-	} else {
+	}
+        else {
+            double distance;
 	    double minDist = Double.POSITIVE_INFINITY;
-	    // TODO : BugId 4351579 -- Need to return the index of nearest
-	    //                         intersected geometry too.
-
-	    for (i=0; i < geomListSize; i++) {
-		Geometry g =  (Geometry) glist.get(i);
-		if (g != null && g.retained != null) {
-		    geometry = (GeometryRetained)g.retained;
+            Point3d closestIPnt = new Point3d();
+            Point3d iPnt = new Point3d();            
+            Point3d iPntVW = new Point3d();            
+            PickInfo.IntersectionInfo closestInfo = null;            
+            PickInfo.IntersectionInfo intersectionInfo
+                    = pickInfo.createIntersectionInfo();
+            
+            if ((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) {
+                closestInfo = pickInfo.createIntersectionInfo();
+            }
+            
+	    for (int i=0; i < geomListSize; i++) {
+		geometry =  (GeometryRetained) geometryList.get(i);
+		if (geometry != null) {
 		    if (geometry.mirrorGeometry != null) {
 			geometry = geometry.mirrorGeometry;
 		    }
-		    if (geometry.intersect(newPS, dist, iPnt)) {
-			localToVworld.transform(iPnt);
-			dist[0] = pickShape.distance(iPnt);
-			if (minDist > dist[0]) {
-			    minDist = dist[0];
-			}
+                    if (geometry.intersect(newPS, intersectionInfo, flags, iPnt)) {  
+
+                        iPntVW.set(iPnt);
+                        localToVworld.transform(iPntVW);
+			distance = pickShape.distance(iPntVW);
+                       
+			if (minDist > distance) {
+			    minDist = distance; 
+                            closestIPnt.set(iPnt);
+                            
+                            if ((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) {
+                                closestInfo.setGeometry((Geometry) geometry.source);
+                                closestInfo.setGeometryIndex(i);
+                                closestInfo.setIntersectionPoint(closestIPnt);
+                                closestInfo.setDistance(distance);
+                                closestInfo.setVertexIndices(intersectionInfo.getVertexIndices());
+                            }
+                        }
+                        
+                        if ((flags & PickInfo.ALL_GEOM_INFO) != 0) {
+                            
+                            intersectionInfo.setGeometry((Geometry) geometry.source);
+                            intersectionInfo.setGeometryIndex(i);
+                            intersectionInfo.setIntersectionPoint(iPnt);
+                            intersectionInfo.setDistance(distance);
+                           // VertexIndices has been computed in intersect method.                            
+                            pickInfo.insertIntersectionInfo(intersectionInfo);
+                            intersectionInfo = pickInfo.createIntersectionInfo();
+                        }                        
 		    }
 		}
 	    }
-	
-	    if (minDist < Double.POSITIVE_INFINITY) {
-		dist[0] = minDist;
-		Shape3DRetained.freePoint3d(iPnt);
+            
+	    if (minDist < Double.POSITIVE_INFINITY) {                 
+                if ((flags & PickInfo.CLOSEST_DISTANCE) != 0) {
+                    pickInfo.setClosestDistance(minDist);
+                }
+                if((flags & PickInfo.CLOSEST_INTERSECTION_POINT) != 0) {
+                    pickInfo.setClosestIntersectionPoint(closestIPnt);
+                }
+                if ((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) {
+                    pickInfo.insertIntersectionInfo(closestInfo);  
+                }
 		return true;
 	    }	
 	}
-
-	Shape3DRetained.freePoint3d(iPnt);
+        
 	return false;
-    }
+       
+    }            
+
 }
diff --git a/src/classes/share/javax/media/j3d/Shape3DRetained.java b/src/classes/share/javax/media/j3d/Shape3DRetained.java
index 8e097bd..8d3002c 100644
--- a/src/classes/share/javax/media/j3d/Shape3DRetained.java
+++ b/src/classes/share/javax/media/j3d/Shape3DRetained.java
@@ -580,97 +580,6 @@ class Shape3DRetained extends LeafRetained {
         return (appearance == null ? null: (Appearance) appearance.source);
     }
 
-  
-    /**
-     * Check if the geometry component of this shape node under path
-     * intersects with the pickShape.
-     * This is an expensive method. It should only be called if and only
-     * if the path's bound intersects pickShape.  
-     * @exception IllegalArgumentException if <code>path</code> is
-     * invalid.
-     */
-
-      boolean intersect(SceneGraphPath path,
-		      PickShape pickShape, double[] dist) {
-	
-	// This method will not do bound intersect check, as it assume
-	// caller has already done that. ( For performance and code
-	// simplification reasons. )
-       
-	Transform3D localToVworld = path.getTransform();
-	int i;
-
-	if (localToVworld == null) {
-	    throw new IllegalArgumentException(J3dI18N.getString("Shape3DRetained3"));   
-	}
-	
-	// Support OrientedShape3D here.
-	// TODO - BugId : 4363899 - APIs issue : OrientedShape3D's intersect needs view
-	//                          info. temp. fix use the primary view.
-	if (this instanceof OrientedShape3DRetained) {
-	    Transform3D orientedTransform = ((OrientedShape3DRetained)this).
-		getOrientedTransform(getPrimaryViewIdx());
-	    localToVworld.mul(orientedTransform);
-	}
-
-	Transform3D t3d = VirtualUniverse.mc.getTransform3D(null);
-	t3d.invert(localToVworld);	
-	PickShape newPS = pickShape.transform(t3d);
-	FreeListManager.freeObject(FreeListManager.TRANSFORM3D, t3d);
-
-	// TODO: For optimization - Should do a geobounds check of
-	// each geometry first. But this doesn't work for
-	// OrientedShape3D case...
-	int geomListSize = geometryList.size();
-   	Point3d iPnt = getPoint3d();
-	GeometryRetained geometry;
-
-	if (dist == null) {
-	    for (i=0; i < geomListSize; i++) {
-		geometry =  (GeometryRetained) geometryList.get(i);	     
-		if (geometry != null) {
-		    if (geometry.mirrorGeometry != null) {
-			geometry = geometry.mirrorGeometry;
-		    }
-		    if (geometry.intersect(newPS, null, iPnt)) {
-			freePoint3d(iPnt);
-			return true;
-		    }
-		}
-	    }
-	} else {
-	    double minDist = Double.POSITIVE_INFINITY;
-	    // TODO : BugId 4351579 -- Need to return the index of nearest
-	    //                         intersected geometry too.
-
-	    for (i=0; i < geomListSize; i++) {
-		geometry =  (GeometryRetained) geometryList.get(i);
-		if (geometry != null) {
-		    if (geometry.mirrorGeometry != null) {
-			geometry = geometry.mirrorGeometry;
-		    }
-		    if (geometry.intersect(newPS, dist, iPnt)) {
-			localToVworld.transform(iPnt);
-			dist[0] = pickShape.distance(iPnt);
-			if (minDist > dist[0]) {
-			    minDist = dist[0];
-			}
-		    }
-		}
-	    }
-	
-	    if (minDist < Double.POSITIVE_INFINITY) {
-		dist[0] = minDist;
-		freePoint3d(iPnt);
-		return true;
-	    }	
-	}
-
-	freePoint3d(iPnt);
-	
-	return false;
-      }
-
     void setAppearanceOverrideEnable(boolean flag) {
 	if (((Shape3D)this.source).isLive()) {
 	    
@@ -703,7 +612,153 @@ class Shape3DRetained extends LeafRetained {
     boolean getAppearanceOverrideEnable() {
 	return appearanceOverrideEnable;
     }
+    
+    boolean intersect(PickInfo pickInfo, PickShape pickShape, int flags ) {
+          
+        Transform3D localToVworld = pickInfo.getLocalToVWorldRef();
+        
+        // Support OrientedShape3D here.
+	// Note - BugId : 4363899 - APIs issue : OrientedShape3D's intersect needs view
+	//                          info. temp. fix use the primary view.
+	if (this instanceof OrientedShape3DRetained) {
+	    Transform3D orientedTransform = ((OrientedShape3DRetained)this).
+		getOrientedTransform(getPrimaryViewIdx());
+	    localToVworld.mul(orientedTransform);
+	}
+        
+ 	Transform3D t3d = new Transform3D();
+	t3d.invert(localToVworld);
+	PickShape newPS = pickShape.transform(t3d);
 
+	// Note: For optimization - Should do a geobounds check of
+	// each geometry first. But this doesn't work for
+	// OrientedShape3D case...
+	int geomListSize = geometryList.size();
+	GeometryRetained geometry;
+
+        if (((flags & PickInfo.CLOSEST_INTERSECTION_POINT) == 0) &&
+                ((flags & PickInfo.CLOSEST_DISTANCE) == 0) &&
+                ((flags & PickInfo.CLOSEST_GEOM_INFO) == 0) &&
+                ((flags & PickInfo.ALL_GEOM_INFO) == 0)) {
+            
+            for (int i=0; i < geomListSize; i++) {
+                geometry =  (GeometryRetained) geometryList.get(i);
+                if (geometry != null) {
+                    if (geometry.mirrorGeometry != null) {
+                        geometry = geometry.mirrorGeometry;
+                    }
+                    if (geometry.intersect(newPS, null, 0, null)) {
+                        return true;
+                    }
+                }
+            }
+        } else {
+            double distance;
+            double minDist = Double.POSITIVE_INFINITY;
+            Point3d closestIPnt = new Point3d();
+            Point3d iPnt = new Point3d();
+            Point3d iPntVW = new Point3d();            
+            PickInfo.IntersectionInfo closestInfo = null;
+            PickInfo.IntersectionInfo intersectionInfo
+                    = pickInfo.createIntersectionInfo();
+            
+            if ((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) {
+                closestInfo = pickInfo.createIntersectionInfo();
+            }
+            
+            for (int i=0; i < geomListSize; i++) {
+                geometry =  (GeometryRetained) geometryList.get(i);
+                if (geometry != null) {
+                    if (geometry.mirrorGeometry != null) {
+                        geometry = geometry.mirrorGeometry;
+                    }
+                    if (geometry.intersect(newPS, intersectionInfo, flags, iPnt)) {
+                        
+                        iPntVW.set(iPnt);
+                        localToVworld.transform(iPntVW);
+                        distance = pickShape.distance(iPntVW);
+                        
+                        if (minDist > distance) {
+                            minDist = distance;
+                            closestIPnt.set(iPnt);
+                            
+                            if ((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) {
+                                closestInfo.setGeometry((Geometry) geometry.source);
+                                closestInfo.setGeometryIndex(i);
+                                closestInfo.setIntersectionPoint(closestIPnt);
+                                closestInfo.setDistance(distance);
+                                closestInfo.setVertexIndices(intersectionInfo.getVertexIndices());
+                            }
+                        }
+                        
+                        if ((flags & PickInfo.ALL_GEOM_INFO) != 0) {
+                            
+                            intersectionInfo.setGeometry((Geometry) geometry.source);
+                            intersectionInfo.setGeometryIndex(i);
+                            intersectionInfo.setIntersectionPoint(iPnt);
+                            intersectionInfo.setDistance(distance);
+                           // VertexIndices has been computed in intersect method.
+                            pickInfo.insertIntersectionInfo(intersectionInfo);
+                            intersectionInfo = pickInfo.createIntersectionInfo();
+                        }
+                    }
+                }
+            }
+            
+            if (minDist < Double.POSITIVE_INFINITY) {
+                if ((flags & PickInfo.CLOSEST_DISTANCE) != 0) {
+                    pickInfo.setClosestDistance(minDist);
+                }
+                if((flags & PickInfo.CLOSEST_INTERSECTION_POINT) != 0) {
+                    pickInfo.setClosestIntersectionPoint(closestIPnt);
+                }
+		if ((flags & PickInfo.CLOSEST_GEOM_INFO) != 0) {
+                    pickInfo.insertIntersectionInfo(closestInfo);
+                }
+                return true;
+            }
+        }
+        
+	return false;
+       
+    }    
+    
+    
+    /**
+     * Check if the geometry component of this shape node under path
+     * intersects with the pickShape.
+     * This is an expensive method. It should only be called if and only
+     * if the path's bound intersects pickShape.  
+     * @exception IllegalArgumentException if <code>path</code> is
+     * invalid.
+     */
+ 
+    boolean intersect(SceneGraphPath path,
+            PickShape pickShape, double[] dist) {
+        
+        int flags;
+        PickInfo pickInfo = new PickInfo();
+        
+        Transform3D localToVworld = path.getTransform();
+        if (localToVworld == null) {
+	    throw new IllegalArgumentException(J3dI18N.getString("Shape3DRetained3"));   
+	}
+        pickInfo.setLocalToVWorldRef( localToVworld);
+        //System.out.println("Shape3DRetained.intersect() : ");
+        if (dist == null) {
+            //System.out.println("      no dist request ....");
+            return intersect(pickInfo, pickShape, 0);
+        }
+        
+        flags = PickInfo.CLOSEST_DISTANCE;
+        if (intersect(pickInfo, pickShape, flags)) {
+            dist[0] = pickInfo.getClosestDistance();
+            return true;
+        }
+        
+        return false;
+          
+      }
 
     /**
      * This sets the immedate mode context flag
diff --git a/src/classes/share/javax/media/j3d/SharedGroup.java b/src/classes/share/javax/media/j3d/SharedGroup.java
index 3f7ce8d..446c3f2 100644
--- a/src/classes/share/javax/media/j3d/SharedGroup.java
+++ b/src/classes/share/javax/media/j3d/SharedGroup.java
@@ -67,11 +67,17 @@ public class SharedGroup extends Group {
     public static final int
     ALLOW_LINK_READ = CapabilityBits.SHARED_GROUP_ALLOW_LINK_READ;
 
+    // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_LINK_READ
+    };
 
     /**
      * Constructs and initializes a new SharedGroup node object.
      */
     public SharedGroup() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);                
     }
 
 
@@ -82,10 +88,12 @@ public class SharedGroup extends Group {
      * @since Java 3D 1.3
      */
     public Link[] getLinks() {
-	if (isLiveOrCompiled())
-	    if (!this.getCapability(ALLOW_LINK_READ))
+	if (isLiveOrCompiled()) {
+	    if (!this.getCapability(ALLOW_LINK_READ)) {
 			throw new CapabilityNotSetException(J3dI18N.getString("SharedGroup1"));
-		return ((SharedGroupRetained)retained).getLinks();	
+            }
+        }
+        return ((SharedGroupRetained)retained).getLinks();	
     }
 
 
diff --git a/src/classes/share/javax/media/j3d/SharedGroupRetained.java b/src/classes/share/javax/media/j3d/SharedGroupRetained.java
index ed69d9f..694671f 100644
--- a/src/classes/share/javax/media/j3d/SharedGroupRetained.java
+++ b/src/classes/share/javax/media/j3d/SharedGroupRetained.java
@@ -329,7 +329,7 @@ class SharedGroupRetained extends GroupRetained implements TargetsInterface {
         s.transformTargets = savedTransformTargets;
         s.hashkeyIndex = savedHashkeyIndex;
 /*
-// TODO : port this
+// XXXX : port this
         for (int i=0; i < children.size(); i++) {
             if ((childContains[i][0] & ILLEGAL_LEAF_MASK) != 0) {
                 throw new IllegalSharingException(J3dI18N.getString("SharedGroupRetained0"));            }
@@ -472,7 +472,7 @@ class SharedGroupRetained extends GroupRetained implements TargetsInterface {
         s.switchTargets = null;
 
 
-	// TODO: This is a hack since removeNodeData is called before
+	// XXXX: This is a hack since removeNodeData is called before
 	// children are clearLives
 	int[] tempIndex = null;
 	// Don't keep the indices if everything will be cleared
@@ -778,7 +778,7 @@ class SharedGroupRetained extends GroupRetained implements TargetsInterface {
     public void propagateTargetThreads(int type, int childTargetThreads) {
         if (type == TargetsInterface.TRANSFORM_TARGETS) {
             LinkRetained ln;
-	    // TODO : For now we'll OR more than exact.
+	    // XXXX : For now we'll OR more than exact.
             //targetThreads = localTargetThreads | childTargetThreads;
 	    targetThreads = targetThreads | childTargetThreads;
             for(int i=0; i<parents.size(); i++) {
diff --git a/src/classes/share/javax/media/j3d/Sound.java b/src/classes/share/javax/media/j3d/Sound.java
index a11d8a9..ac70b9b 100644
--- a/src/classes/share/javax/media/j3d/Sound.java
+++ b/src/classes/share/javax/media/j3d/Sound.java
@@ -440,7 +440,26 @@ public abstract class Sound extends Leaf {
      */
     public static final int INFINITE_LOOPS = -1;
 
-
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_CHANNELS_USED_READ,
+        ALLOW_CONT_PLAY_READ,
+        ALLOW_DURATION_READ,
+        ALLOW_ENABLE_READ,
+        ALLOW_INITIAL_GAIN_READ,
+        ALLOW_IS_PLAYING_READ,
+        ALLOW_IS_READY_READ,
+        ALLOW_LOOP_READ,
+        ALLOW_MUTE_READ,
+        ALLOW_PAUSE_READ,
+        ALLOW_PRIORITY_READ,
+        ALLOW_RATE_SCALE_FACTOR_READ,
+        ALLOW_RELEASE_READ,
+        ALLOW_SCHEDULING_BOUNDS_READ,
+        ALLOW_SOUND_DATA_READ  
+    };
+    
+    
     /**
      * Constructs and initializes a new Sound node using default
      * parameters.  The following defaults values are used:
@@ -460,6 +479,8 @@ public abstract class Sound extends Leaf {
      * </ul>
      */
     public Sound() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     } 
 
     /**
@@ -471,6 +492,9 @@ public abstract class Sound extends Leaf {
      * @param initialGain overall amplitude scale factor applied to sound source
      */
     public Sound(MediaContainer soundData, float initialGain) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((SoundRetained)this.retained).setSoundData(soundData);
         ((SoundRetained)this.retained).setInitialGain(initialGain);
     } 
@@ -498,6 +522,9 @@ public abstract class Sound extends Leaf {
                  boolean enable,
                  Bounds  region,
                  float   priority ) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((SoundRetained)this.retained).setSoundData(soundData);
         ((SoundRetained)this.retained).setInitialGain(initialGain);
         ((SoundRetained)this.retained).setLoop(loopCount);
@@ -533,6 +560,9 @@ public abstract class Sound extends Leaf {
                  Bounds  region,
                  float   priority,
                  float   rateFactor ) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+        
         ((SoundRetained)this.retained).setSoundData(soundData);
         ((SoundRetained)this.retained).setInitialGain(initialGain);
         ((SoundRetained)this.retained).setLoop(loopCount);
diff --git a/src/classes/share/javax/media/j3d/SoundRetained.java b/src/classes/share/javax/media/j3d/SoundRetained.java
index 5d6e744..f0b2145 100644
--- a/src/classes/share/javax/media/j3d/SoundRetained.java
+++ b/src/classes/share/javax/media/j3d/SoundRetained.java
@@ -448,7 +448,7 @@ abstract class SoundRetained extends LeafRetained
             if (debugFlag)
 		debugPrint("setSchedulingBounds for a NULL region");
         }
-        // TODO: test that this works - could not new Bounds() since
+        // XXXX: test that this works - could not new Bounds() since
         //       Bounds is an abstract class and can't be instantiated
         dispatchAttribChange(BOUNDS_DIRTY_BIT, region);
 	if (source != null && source.isLive()) {
@@ -501,7 +501,7 @@ abstract class SoundRetained extends LeafRetained
 	} else {
 	    boundingLeaf = null;
 	}
-        // TODO: since BoundingLeaf constructor only takes Bounds 
+        // XXXX: since BoundingLeaf constructor only takes Bounds 
         //       test if region passed into dispatchAttribChange correctly.
         dispatchAttribChange(BOUNDING_LEAF_DIRTY_BIT, region);
 	if (source != null && source.isLive()) {
@@ -628,7 +628,7 @@ abstract class SoundRetained extends LeafRetained
             }
             loadedAtoms[atomCount-1] = atom;  // store reference to new atom
             // all atoms sample durations SHOULD be the same so store it in node
-            this.duration = atom.sampleLength; // TODO: refine later? in ms
+            this.duration = atom.sampleLength; // XXXX: refine later? in ms
         }
         else {  // atom is NOT loaded or has been unloaded; remove from list
             if (atomCount == 0)
@@ -1004,7 +1004,7 @@ abstract class SoundRetained extends LeafRetained
         ms.inImmCtx = inImmCtx;
         ms.setSoundData(getSoundData());
 
-// TODO: copy ms.atoms array from this.atoms
+// XXXX: copy ms.atoms array from this.atoms
 
         ms.parent = parent;
         ms.inSharedGroup = false;
@@ -1136,7 +1136,7 @@ abstract class SoundRetained extends LeafRetained
 
 	super.clearLive(s);
 
-// TODO: if (inSharedGroup)
+// XXXX: if (inSharedGroup)
 
         if (s.inSharedGroup) {
             for (int i=0; i<s.keys.length; i++) {
@@ -1195,7 +1195,7 @@ abstract class SoundRetained extends LeafRetained
 /*
     // This makes passed in sound look just like this sound
 // QUESTION: DOesn't appread to be called
-// TODO:      ...if so, remove...
+// XXXX:      ...if so, remove...
     synchronized void update(SoundRetained sound) {
         if (debugFlag)
             debugPrint("Sound.update ******** entered ***** this = " + this +
@@ -1219,13 +1219,13 @@ abstract class SoundRetained extends LeafRetained
 // QUESTION: With code below, no sound schedulingRegion found
 //        sound.schedulingRegion = schedulingRegion;
 //        sound.boundingLeaf = boundingLeaf;
-// TODO: clone of region used in Traverse code, why not here???
+// XXXX: clone of region used in Traverse code, why not here???
 //        if (schedulingRegion != null)
 //            sound.schedulingRegion = (Bounds)schedulingRegion.clone();
-// TODO:  BoundingLeafRetained boundingLeaf ...
+// XXXX:  BoundingLeafRetained boundingLeaf ...
 //        WHAT ABOUT transformedRegion??
 
-// TODO: Update ALL fields
+// XXXX: Update ALL fields
 // ALL THE BELOW USED TO COMMENTED OUT vvvvvvvvvvvvvvvvvvvvvvvvvvvvv
         sound.sampleLength = sampleLength;
         sound.loopStartOffset = loopStartOffset;
diff --git a/src/classes/share/javax/media/j3d/SoundScheduler.java b/src/classes/share/javax/media/j3d/SoundScheduler.java
index 778c366..15981b6 100644
--- a/src/classes/share/javax/media/j3d/SoundScheduler.java
+++ b/src/classes/share/javax/media/j3d/SoundScheduler.java
@@ -102,7 +102,7 @@ class SoundScheduler extends J3dStructure {
      * This prioritized sound list is NO longer re-create instead sounds
      * are insert, shuffled or removed as messages are processed.
      */
-    // TODO: (Enhancement) should have a seperate list for
+    // XXXX: (Enhancement) should have a seperate list for
     //       background sound and a list for positional sounds
     ArrayList prioritizedSounds = new ArrayList();
 
@@ -322,7 +322,7 @@ class SoundScheduler extends J3dStructure {
 		auralAttribsChanged = true;
 	    }
 	    else if (node instanceof ViewPlatformRetained) {
-		// TODO: don't support multiple viewPlatforms per scheduler
+		// XXXX: don't support multiple viewPlatforms per scheduler
 		/*
 		// useful for resetting VP ??
 		addViewPlatform((ViewPlatformRetained) node);
@@ -753,7 +753,7 @@ class SoundScheduler extends J3dStructure {
 	    debugPrint(".deactivate()");
 	//
 
-	// TODO: The following code is clearly erroneous.
+	// XXXX: The following code is clearly erroneous.
 	// The indendation, along with the 2nd test of
 	// "if (debugFlag)" in the else clause, suggests that
 	// the intent was to make the else clause apply to
@@ -858,7 +858,7 @@ class SoundScheduler extends J3dStructure {
 	    return;
 	}
 
-	// TODO: Does not support multiple canvases per view, thus
+	// XXXX: Does not support multiple canvases per view, thus
 	//      multiple GraphicsContext3Ds
 	// QUESTION: what does that mean for sound -
 	//      being applied to only ONE graphics context?
@@ -929,7 +929,7 @@ class SoundScheduler extends J3dStructure {
 	int numActiveSounds = 0;
 	if (debugFlag)
 	    debugPrint(" renderChanges begun");
-	// TODO: BUG?? should wait if audioDevice is NULL or nSounds = 0
+	// XXXX: BUG?? should wait if audioDevice is NULL or nSounds = 0
 	//          when a new sound is added or deleted from list, or
 	//          when the audioDevice is set into PhysicalEnvironment
 
@@ -955,7 +955,7 @@ class SoundScheduler extends J3dStructure {
 		    if (debugFlag)
 			debugPrint("        "+ nIntersected +
 				   " active SoundScapes found");
-		    // TODO: (Performance) calling clone everytime, even
+		    // XXXX: (Performance) calling clone everytime, even
 		    //     though closest AA has NOT changed, is expensive
 		    aaRetained = (AuralAttributesRetained)
 			(findClosestAAttribs(nIntersected)).clone();
@@ -1007,7 +1007,7 @@ class SoundScheduler extends J3dStructure {
 	    if (!prioritizedSounds.isEmpty()) {
 		prioritizedSounds.clear();
 	    }
-	    // TODO: sync soundStructure sound list
+	    // XXXX: sync soundStructure sound list
 	    UnorderList retainedSounds = universe.soundStructure.getSoundList(view);
 	    // QUESTION: what is in this sound list??
 	    //          mirror node or actual node???
@@ -1020,7 +1020,7 @@ class SoundScheduler extends J3dStructure {
 		addPrioritizedSound((SoundRetained)retainedSounds.get(i));
 		nRetainedSounds++;
 	    }
-	    // TODO: sync canvases
+	    // XXXX: sync canvases
 	    Enumeration canvases = view.getAllCanvas3Ds();
 	    while (canvases.hasMoreElements()) {
 		Canvas3D canvas = (Canvas3D)canvases.nextElement();
@@ -1300,7 +1300,7 @@ class SoundScheduler extends J3dStructure {
 	    if (attribs != null) {
 	        synchronized (attribs) {
 /*
-		// TODO: remove use of aaDirty from AuralAttrib node
+		// XXXX: remove use of aaDirty from AuralAttrib node
 		if ((attribs != lastAA) || attribs.aaDirty)
 */
 		    if (debugFlag) {
@@ -1476,7 +1476,7 @@ class SoundScheduler extends J3dStructure {
 	// Sounds that have finished playing are not put into list
 	if ((soundAtom.status == SoundSchedulerAtom.SOUND_COMPLETE) &&
 	    (soundAtom.enabled != SoundSchedulerAtom.PENDING_ON )) {
-	    // TODO:/QUESTION test for immediate mode (?)
+	    // XXXX:/QUESTION test for immediate mode (?)
 
 	    // Unless the sound has been re-started, there's no need
 	    // to process sound the finished playing the last time thru
@@ -2050,7 +2050,7 @@ class SoundScheduler extends J3dStructure {
 	synchronized (prioritizedSounds) {
 	    nAtoms = prioritizedSounds.size();
 	    for (int i=0; i<nAtoms; i++) {
-		// TODO: (Enhancement) Get all sound node fields here
+		// XXXX: (Enhancement) Get all sound node fields here
 		//          and store locally for performance
 		soundAtom = (SoundSchedulerAtom)prioritizedSounds.get(i);
 		mirSound = soundAtom.sound;
@@ -2070,7 +2070,7 @@ class SoundScheduler extends J3dStructure {
 		// check to see if aural attributes changed and have to be updated
 		// must be done before list of sound processed so that Aural Attributes
 		// that affect Sound fields can be set in AudioDevice
-		// TODO: this is not effient if auralAttribs always the same
+		// XXXX: this is not effient if auralAttribs always the same
 		if (sound.getInImmCtx()) {
 		    if (graphicsCtx !=null && graphicsCtx.auralAttributes !=null) {
 			aaImmed = (AuralAttributesRetained)
@@ -2154,7 +2154,7 @@ class SoundScheduler extends J3dStructure {
 		case SoundSchedulerAtom.MAKE_SILENT:
 		    // change status to silent AFTER calling render so
 		    // that a currently audible sound will be muted.
-		    // TODO: why set status AFTER??
+		    // XXXX: why set status AFTER??
 		    render(false, soundAtom, attribs);
 		    soundAtom.status = SoundSchedulerAtom.SOUND_SILENT;
 		    numActiveSounds++;
@@ -2373,7 +2373,7 @@ class SoundScheduler extends J3dStructure {
 		if (debugFlag)
 		    debugPrint("silenceAll " + nAtoms + " Sounds");
 		for (int i=0; i<nAtoms; i++) {
-		    // TODO: (Enhancement) Get all sound node fields here
+		    // XXXX: (Enhancement) Get all sound node fields here
 		    //          and store locally for performance
 		    soundAtom = (SoundSchedulerAtom)prioritizedSounds.get(i);
 		    mirSound = soundAtom.sound;
@@ -2430,7 +2430,7 @@ class SoundScheduler extends J3dStructure {
 		if (debugFlag)
 		    debugPrint(":pauseAll " + nAtoms + " Sounds");
 		for (int i=0; i<nAtoms; i++) {
-		    // TODO: (Enhancement) Get all sound node fields here
+		    // XXXX: (Enhancement) Get all sound node fields here
 		    //          and store locally for performance
 		    SoundSchedulerAtom soundAtom =
 			(SoundSchedulerAtom)prioritizedSounds.get(i);
@@ -2475,7 +2475,7 @@ class SoundScheduler extends J3dStructure {
 		    debugPrint(": resumeAll " + nAtoms + " Sounds ");
 
 		for (int i=0; i<nAtoms; i++) {
-		    // TODO: (Enhancement) Get all sound node fields here
+		    // XXXX: (Enhancement) Get all sound node fields here
 		    //          and store locally for performance
 		    SoundSchedulerAtom soundAtom  =
 			(SoundSchedulerAtom)prioritizedSounds.get(i);
@@ -2528,7 +2528,7 @@ class SoundScheduler extends J3dStructure {
 		    debugPrint(": stopAll " + nAtoms + " Sounds ");
 
 		for (int i=0; i<nAtoms; i++) {
-		    // TODO: (Enhancement) Get all sound node fields here
+		    // XXXX: (Enhancement) Get all sound node fields here
 		    //          and store locally for performance
 		    SoundSchedulerAtom soundAtom =
 			(SoundSchedulerAtom)prioritizedSounds.get(i);
@@ -2547,7 +2547,7 @@ class SoundScheduler extends J3dStructure {
 	    debugPrint(".stopAllSounds exited");
     }
 
-	    // TODO: Mute All Sounds, complementary to Stop All Sounds
+	    // XXXX: Mute All Sounds, complementary to Stop All Sounds
 	    //     "should return from run loop - but simply WAIT until sounds
 	    //     are unmuted. " ???
 
@@ -2618,7 +2618,7 @@ class SoundScheduler extends J3dStructure {
 	// Set Transform
 
 	/*
-	// TODO: per sound tranforms can now be passed to AudioDevice
+	// XXXX: per sound tranforms can now be passed to AudioDevice
 	//     modify and execute the code below
 
 	//     MoveAppBoundingLeaf > ~/Current/MoveAppBoundingLeaf.outted,
@@ -2652,7 +2652,7 @@ class SoundScheduler extends J3dStructure {
 		    }
 		    audioDevice3D.setVworldXfrm(index, xform);
 		    soundAtom.clearStateDirtyFlag( SoundRetained.XFORM_DIRTY_BIT);
-	// TODO: make sure position and direction are already transformed and stored
+	// XXXX: make sure position and direction are already transformed and stored
 	//     into xformXxxxxxx fields.
 		}
 	//      ^^^^^^^^^^^^^^^^^^^^^
@@ -2679,7 +2679,7 @@ class SoundScheduler extends J3dStructure {
 	    ConeSoundRetained cn = (ConeSoundRetained)mirrorPtSound;
 	    ConeSoundRetained cnSound = (ConeSoundRetained)mirrorPtSound.sgSound;
 	    if (updateAll ||
-		// TODO: test for XFORM_DIRTY only in for 1.2
+		// XXXX: test for XFORM_DIRTY only in for 1.2
 		soundAtom.testDirtyFlag(soundAtom.attribsDirty,
 					(SoundRetained.DIRECTION_DIRTY_BIT |
 					 SoundRetained.XFORM_DIRTY_BIT) ) ) {
@@ -2992,7 +2992,7 @@ class SoundScheduler extends J3dStructure {
 	    soundAtom.sampleLength = duration;
 	    soundAtom.loopLength = soundAtom.sampleLength;
 
-	    // TODO: for most this will be 0 but not all
+	    // XXXX: for most this will be 0 but not all
 	    soundAtom.loopStartOffset = 0;
 	    soundAtom.attackLength = 0;    // portion of sample before loop section
 	    soundAtom.releaseLength = 0;   // portion of sample after loop section
@@ -3027,7 +3027,7 @@ class SoundScheduler extends J3dStructure {
 			atomFound++;
 			// orginal app node pass into method
 			// QUESTION: is mirror node still correct?
-			// TODO: ensure only mirror nodes passed into method
+			// XXXX: ensure only mirror nodes passed into method
 			if (atomFound == nthInstance) {
 			    returnAtom = soundAtom;
 			    break;
diff --git a/src/classes/share/javax/media/j3d/SoundSchedulerAtom.java b/src/classes/share/javax/media/j3d/SoundSchedulerAtom.java
index 1ebf628..a4dcccc 100644
--- a/src/classes/share/javax/media/j3d/SoundSchedulerAtom.java
+++ b/src/classes/share/javax/media/j3d/SoundSchedulerAtom.java
@@ -241,7 +241,7 @@ class SoundSchedulerAtom extends Object {
     }
  
 
-// TODO: remove this
+// XXXX: remove this
 // just set the state after debug no longer needed
     void setEnableState(int state) {
         enabled = state;
@@ -269,7 +269,7 @@ class SoundSchedulerAtom extends Object {
         }
     }   
  
-// TODO: remove this
+// XXXX: remove this
 // just set the state after debug no longer needed
     void setMuteState(int state) {
         muted = state;
@@ -297,7 +297,7 @@ class SoundSchedulerAtom extends Object {
         }
     }   
  
-// TODO: remove this
+// XXXX: remove this
 // just set the state after debug no longer needed
     void setPauseState(int state) {
         paused = state;
@@ -603,8 +603,8 @@ class SoundSchedulerAtom extends Object {
         return (action);
     } // end of calcInactiveSchedAction
 
-// TODO isPLaying
-// TODO setLoadingState
+// XXXX: isPLaying
+// XXXX: setLoadingState
 
     // Debug print mechanism for Sound nodes
     static final boolean debugFlag = false;
diff --git a/src/classes/share/javax/media/j3d/SoundStructure.java b/src/classes/share/javax/media/j3d/SoundStructure.java
index 90fdba5..e2407b6 100644
--- a/src/classes/share/javax/media/j3d/SoundStructure.java
+++ b/src/classes/share/javax/media/j3d/SoundStructure.java
@@ -99,7 +99,7 @@ class SoundStructure extends J3dStructure {
 		break;
 	    case J3dMessage.SOUNDSCAPE_CHANGED:
 	    case J3dMessage.AURALATTRIBUTES_CHANGED:
-		// TODO: this needs to be changed
+		// XXXX: this needs to be changed
 		changeNodeAttrib(m);
 		break;
 	    case J3dMessage.TRANSFORM_CHANGED:
@@ -115,7 +115,7 @@ class SoundStructure extends J3dStructure {
 	    case J3dMessage.VIEWSPECIFICGROUP_CHANGED:
 		updateViewSpecificGroupChanged(m);
 		break;
-	    // TODO: case J3dMessage.BOUNDINGLEAF_CHANGED
+	    // XXXX: case J3dMessage.BOUNDINGLEAF_CHANGED
 	    }
 
 	    /*
@@ -187,7 +187,7 @@ class SoundStructure extends J3dStructure {
 	    }
 	}
 	    /*
-	    // TODO:
+	    // XXXX:
             if (node instanceof AuralAttributesRetained) {
             }
             else if (node instanceof ViewPlatformRetained) {
@@ -326,7 +326,7 @@ class SoundStructure extends J3dStructure {
             if (debugFlag)
                 debugPrint("         Sound node dirty bit = " + attribDirty);
             if ((attribDirty & SoundRetained.PRIORITY_DIRTY_BIT) > 0) {
-		// TODO: shuffle in SoundScheduler
+		// XXXX: shuffle in SoundScheduler
 		/*
                 shuffleSound((SoundRetained) node);
 		*/
@@ -346,7 +346,7 @@ class SoundStructure extends J3dStructure {
 /*
             }
 */
-// TODO: have no dirty flag for soundscape, just auralAttributes...
+// XXXX: have no dirty flag for soundscape, just auralAttributes...
 //          what if reference to AA changes in soundscape???
         }
 
@@ -400,7 +400,7 @@ class SoundStructure extends J3dStructure {
             View[] views = vpLists[i].getViewList();
             for (int j=(views.length-1); j>=0; j--) {
                 View v = (View)(views[j]);
-// TODO: Shouldn't this be done with messages??
+// XXXX: Shouldn't this be done with messages??
                 v.soundScheduler.loadSound(sound, forceLoad);
             }
         }        
@@ -501,7 +501,7 @@ class SoundStructure extends J3dStructure {
 
 
 /*
-// TODO: how is immediate mode handled? below code taken from SoundSchedule
+// XXXX: how is immediate mode handled? below code taken from SoundSchedule
 // Don't know how we'll process immediate mode sounds;
 // Append immediate mode sounds to live sounds list
         if (graphicsCtx != null) {
diff --git a/src/classes/share/javax/media/j3d/Soundscape.java b/src/classes/share/javax/media/j3d/Soundscape.java
index b995bc9..2e61e42 100644
--- a/src/classes/share/javax/media/j3d/Soundscape.java
+++ b/src/classes/share/javax/media/j3d/Soundscape.java
@@ -72,7 +72,12 @@ public class Soundscape extends Leaf {
      public static final int
     ALLOW_ATTRIBUTES_WRITE = CapabilityBits.SOUNDSCAPE_ALLOW_ATTRIBUTES_WRITE;
 
-    /**
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_APPLICATION_BOUNDS_READ,
+        ALLOW_ATTRIBUTES_READ        
+    };
+     /**
      * Constructs and initializes a new Sound node using following
      * defaults:
      *<UL>   application region: null (no active region)</UL>
@@ -80,6 +85,8 @@ public class Soundscape extends Leaf {
      */  
     public Soundscape() {
          // Just use default values
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -90,6 +97,9 @@ public class Soundscape extends Leaf {
      */  
     public Soundscape(Bounds region,
                       AuralAttributes attributes) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
         ((SoundscapeRetained)this.retained).setApplicationBounds(region);
         ((SoundscapeRetained)this.retained).setAuralAttributes(attributes);
     }
diff --git a/src/classes/share/javax/media/j3d/SourceCodeShader.java b/src/classes/share/javax/media/j3d/SourceCodeShader.java
new file mode 100644
index 0000000..b940255
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SourceCodeShader.java
@@ -0,0 +1,121 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The SourceCodeShader object is a shader that is defined using
+ * text-based source code. It is used to define the source code for
+ * both vertex and fragment shaders. The currently supported shading
+ * languages are Cg and GLSL.
+ *
+ * @see ShaderProgram
+ *
+ * @since Java 3D 1.4
+ */
+
+public class SourceCodeShader extends Shader {
+
+    /**
+     * Not a public constructor, for internal use
+     */
+    SourceCodeShader() {
+    }
+
+    /**
+     * Constructs a new shader object of the specified shading
+     * language and shader type from the specified source string.
+     *
+     * @param shadingLanguage the specified shading language, one of:
+     * <code>SHADING_LANGUAGE_GLSL</code> or
+     * <code>SHADING_LANGUAGE_CG</code>.
+     *
+     * @param shaderType the shader type, one of:
+     * <code>SHADER_TYPE_VERTEX</code> or
+     * <code>SHADER_TYPE_FRAGMENT</code>.
+     *
+     * @param shaderSource the shader source code
+     *
+     * @exception NullPointerException if shaderSource is null.
+     */
+
+    public SourceCodeShader(int shadingLanguage, int shaderType, String shaderSource) {
+	super(shadingLanguage, shaderType);
+        if (shaderSource == null) {
+            throw new NullPointerException();
+        }
+	((SourceCodeShaderRetained)this.retained).initShaderSource(shaderSource);
+    }
+
+    /**
+     * Retrieves the shader source string from this shader object.
+     *
+     * @return the shader source string.
+     */
+    public String getShaderSource() {
+	return ((SourceCodeShaderRetained)this.retained).getShaderSource();
+    }
+
+
+    /**
+     * Creates a retained mode SourceCodeShaderRetained object that this
+     * SourceCodeShader component object will point to.
+     */
+    void createRetained() {
+	this.retained = new SourceCodeShaderRetained();
+	this.retained.setSource(this);
+	// System.out.println("SourceCodeShader.createRetained()");
+    }
+    
+    /**
+     * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
+     */
+    public NodeComponent cloneNodeComponent() {
+	SourceCodeShaderRetained scsRetained = (SourceCodeShaderRetained) retained;
+	
+	SourceCodeShader scs = new SourceCodeShader(scsRetained.getShadingLanguage(),
+						    scsRetained.getShaderType(),
+						    scsRetained.getShaderSource());
+	scs.duplicateNodeComponent(this);
+	return scs;
+    }
+
+      
+   /**
+     * Copies all node information from <code>originalNodeComponent</code> 
+     * into the current node.  This method is called from the
+     * <code>duplicateNode</code> method. This routine does
+     * the actual duplication of all "local data" (any data defined in
+     * this object). 
+     *
+     * @param originalNodeComponent the original node to duplicate
+     * @param forceDuplicate when set to <code>true</code>, causes the
+     *  <code>duplicateOnCloneTree</code> flag to be ignored.  When
+     *  <code>false</code>, the value of each node's
+     *  <code>duplicateOnCloneTree</code> variable determines whether
+     *  NodeComponent data is duplicated or copied.
+     *
+     * @see Node#cloneTree
+     * @see NodeComponent#setDuplicateOnCloneTree
+     */
+    void duplicateAttributes(NodeComponent originalNodeComponent,
+			     boolean forceDuplicate) { 
+	super.duplicateAttributes(originalNodeComponent, forceDuplicate);
+
+	String sc = ((SourceCodeShaderRetained) originalNodeComponent.retained).getShaderSource();
+
+	if (sc != null) {
+	    ((SourceCodeShaderRetained) retained).setShaderSource(sc);
+	}
+    }
+
+}
diff --git a/src/classes/share/javax/media/j3d/SourceCodeShaderRetained.java b/src/classes/share/javax/media/j3d/SourceCodeShaderRetained.java
new file mode 100644
index 0000000..d415ae8
--- /dev/null
+++ b/src/classes/share/javax/media/j3d/SourceCodeShaderRetained.java
@@ -0,0 +1,85 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package javax.media.j3d;
+
+/**
+ * The SourceCodeShaderRetained object is a shader that is defined using
+ * text-based source code. It is used to define the source code for
+ * both vertex and fragment shaders. The currently supported shading
+ * languages are Cg and GLSL.
+ */
+
+class SourceCodeShaderRetained extends ShaderRetained {
+
+    private String shaderSource = null;
+
+    /**
+     * Constructs a new shader retained object of the specified shading
+     * language and shader type from the specified source string.
+     */
+
+    SourceCodeShaderRetained() {
+    }
+
+    // This method is similar to setShaderSource(). 
+    // To conform to j3d frame in retained creation, we will stick with method 
+    // with init name.
+    final void initShaderSource(String shaderSource) {
+	this.shaderSource = shaderSource;
+    }    
+
+    final void set(int shadingLanguage, int shaderType, String shaderSource) {
+	this.shadingLanguage = shadingLanguage;
+	this.shaderType = shaderType;
+	this.shaderSource = shaderSource;
+    }
+
+    /**
+     * Retrieves the shader source string from this shader object.
+     *
+     * @return the shader source string.
+     */
+    final String getShaderSource() {
+	return shaderSource;
+    }
+
+    final void setShaderSource(String shaderSource) {
+	this.shaderSource = shaderSource;
+    }    
+
+    synchronized void createMirrorObject() {
+	// System.out.println("SourceCodeShaderRetained : createMirrorObject");
+
+	if (mirror == null) {
+	    SourceCodeShaderRetained  mirrorSCS = new SourceCodeShaderRetained();
+	    mirror = mirrorSCS;
+	}
+
+	initMirrorObject();
+    }
+    
+    /**
+     * Initializes a mirror object.
+     */
+    synchronized void initMirrorObject() {
+	mirror.source = source;
+
+	((SourceCodeShaderRetained) mirror).set(shadingLanguage, shaderType, shaderSource);
+	((SourceCodeShaderRetained) mirror).shaderIds = null;	
+    }
+
+    synchronized void updateMirrorObject(int component, Object value) {
+	System.out.println("SourceCodeShader.updateMirrorObject not implemented yet!");
+    }
+
+}
diff --git a/src/classes/share/javax/media/j3d/SpotLight.java b/src/classes/share/javax/media/j3d/SpotLight.java
index b8fc2fc..9f2900d 100644
--- a/src/classes/share/javax/media/j3d/SpotLight.java
+++ b/src/classes/share/javax/media/j3d/SpotLight.java
@@ -94,6 +94,13 @@ public class SpotLight extends PointLight {
   public static final int
     ALLOW_DIRECTION_READ = CapabilityBits.SPOT_LIGHT_ALLOW_DIRECTION_READ;
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+	ALLOW_SPREAD_ANGLE_READ,
+	ALLOW_CONCENTRATION_READ,
+	ALLOW_DIRECTION_READ
+    };
+
     /**
      * Constructs a SpotLight node with default parameters.
      * The default values are as follows:
@@ -104,6 +111,8 @@ public class SpotLight extends PointLight {
      * </ul>
      */
     public SpotLight() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -124,6 +133,10 @@ public class SpotLight extends PointLight {
 		     float spreadAngle,
 		     float concentration) {
 	super(color, position, attenuation);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
 	((SpotLightRetained)this.retained).initDirection(direction);
 	((SpotLightRetained)this.retained).initSpreadAngle(spreadAngle);
 	((SpotLightRetained)this.retained).initConcentration(concentration);
@@ -148,6 +161,10 @@ public class SpotLight extends PointLight {
 		     float spreadAngle,
 		     float concentration) {
 	super(lightOn, color, position, attenuation);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
 	((SpotLightRetained)this.retained).initDirection(direction);
 	((SpotLightRetained)this.retained).initSpreadAngle(spreadAngle);
 	((SpotLightRetained)this.retained).initConcentration(concentration);
diff --git a/src/classes/share/javax/media/j3d/Switch.java b/src/classes/share/javax/media/j3d/Switch.java
index 28acc2a..359e365 100644
--- a/src/classes/share/javax/media/j3d/Switch.java
+++ b/src/classes/share/javax/media/j3d/Switch.java
@@ -64,6 +64,11 @@ public class Switch extends Group {
      */
     public static final int CHILD_MASK = -3;
 
+    // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_SWITCH_READ
+    };
+    
     /**
      * Constructs a Switch node with default parameters.
      * The default values are as follows:
@@ -73,6 +78,8 @@ public class Switch extends Group {
      * </ul>
      */
     public Switch() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);        
     }
 
     /**
@@ -81,7 +88,10 @@ public class Switch extends Group {
      * @param whichChild the initial child selection index
      */
     public Switch(int whichChild) {
-	((SwitchRetained)this.retained).setWhichChild(whichChild, true);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);        
+
+        ((SwitchRetained)this.retained).setWhichChild(whichChild, true);
     }
 
     /**
@@ -91,7 +101,10 @@ public class Switch extends Group {
      * @param childMask the initial child selection mask
      */
     public Switch(int whichChild, BitSet childMask){
-	((SwitchRetained)this.retained).setWhichChild(whichChild, true);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);        
+
+        ((SwitchRetained)this.retained).setWhichChild(whichChild, true);
 	((SwitchRetained)this.retained).setChildMask(childMask);
     }
 
diff --git a/src/classes/share/javax/media/j3d/TexCoordGeneration.java b/src/classes/share/javax/media/j3d/TexCoordGeneration.java
index 2915a27..f19c34c 100644
--- a/src/classes/share/javax/media/j3d/TexCoordGeneration.java
+++ b/src/classes/share/javax/media/j3d/TexCoordGeneration.java
@@ -230,6 +230,14 @@ public class TexCoordGeneration extends NodeComponent {
      */
     public static final int TEXTURE_COORDINATE_4 = 2;
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_ENABLE_READ,
+        ALLOW_FORMAT_READ,
+        ALLOW_MODE_READ,
+        ALLOW_PLANE_READ        
+    };
+    
     /**
      * Constructs a TexCoordGeneration object with default parameters.
      * The default values are as follows:
@@ -245,6 +253,8 @@ public class TexCoordGeneration extends NodeComponent {
      */
     public TexCoordGeneration() {
 	// Just use the defaults
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -259,7 +269,10 @@ public class TexCoordGeneration extends NodeComponent {
      * @see Canvas3D#queryProperties
      */
     public TexCoordGeneration(int genMode, int format) {
-	((TexCoordGenerationRetained)this.retained).initGenMode(genMode);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((TexCoordGenerationRetained)this.retained).initGenMode(genMode);
 	((TexCoordGenerationRetained)this.retained).initFormat(format);
     }
 
@@ -276,7 +289,10 @@ public class TexCoordGeneration extends NodeComponent {
      * @see Canvas3D#queryProperties
      */
     public TexCoordGeneration(int genMode, int format, Vector4f planeS) {
-	((TexCoordGenerationRetained)this.retained).initGenMode(genMode);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((TexCoordGenerationRetained)this.retained).initGenMode(genMode);
 	((TexCoordGenerationRetained)this.retained).initFormat(format);
 	((TexCoordGenerationRetained)this.retained).initPlaneS(planeS);
     }
@@ -296,7 +312,10 @@ public class TexCoordGeneration extends NodeComponent {
      */
     public TexCoordGeneration(int genMode, int format, Vector4f planeS, 
 			      Vector4f planeT) {
-	((TexCoordGenerationRetained)this.retained).initGenMode(genMode);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((TexCoordGenerationRetained)this.retained).initGenMode(genMode);
 	((TexCoordGenerationRetained)this.retained).initFormat(format);
 	((TexCoordGenerationRetained)this.retained).initPlaneS(planeS);
 	((TexCoordGenerationRetained)this.retained).initPlaneT(planeT);
@@ -317,7 +336,10 @@ public class TexCoordGeneration extends NodeComponent {
      */
     public TexCoordGeneration(int genMode, int format, Vector4f planeS, 
 			      Vector4f planeT, Vector4f planeR) {
-	((TexCoordGenerationRetained)this.retained).initGenMode(genMode);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((TexCoordGenerationRetained)this.retained).initGenMode(genMode);
 	((TexCoordGenerationRetained)this.retained).initFormat(format);
 	((TexCoordGenerationRetained)this.retained).initPlaneS(planeS);
 	((TexCoordGenerationRetained)this.retained).initPlaneT(planeT);
@@ -343,7 +365,10 @@ public class TexCoordGeneration extends NodeComponent {
     public TexCoordGeneration(int genMode, int format, Vector4f planeS, 
 			      Vector4f planeT, Vector4f planeR,
 			      Vector4f planeQ) {
-	((TexCoordGenerationRetained)this.retained).initGenMode(genMode);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((TexCoordGenerationRetained)this.retained).initGenMode(genMode);
 	((TexCoordGenerationRetained)this.retained).initFormat(format);
 	((TexCoordGenerationRetained)this.retained).initPlaneS(planeS);
 	((TexCoordGenerationRetained)this.retained).initPlaneT(planeT);
diff --git a/src/classes/share/javax/media/j3d/Text3D.java b/src/classes/share/javax/media/j3d/Text3D.java
index af9e81d..c62fee4 100644
--- a/src/classes/share/javax/media/j3d/Text3D.java
+++ b/src/classes/share/javax/media/j3d/Text3D.java
@@ -206,6 +206,17 @@ public class Text3D extends Geometry {
      */
     public static final int PATH_DOWN = 3;
 
+    // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+	ALLOW_FONT3D_READ,
+	ALLOW_STRING_READ,
+	ALLOW_POSITION_READ,
+	ALLOW_ALIGNMENT_READ,
+	ALLOW_PATH_READ,
+	ALLOW_CHARACTER_SPACING_READ,
+	ALLOW_BOUNDING_BOX_READ
+    };
+
     /**
      * Constructs a Text3D object with default parameters.
      * The default values are as follows:
@@ -219,6 +230,8 @@ public class Text3D extends Geometry {
      * </ul>
      */
     public Text3D() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -227,6 +240,9 @@ public class Text3D extends Geometry {
      * @see Font3D
      */
     public Text3D(Font3D font3D) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
 	((Text3DRetained)this.retained).setFont3D(font3D);
     }
 
@@ -239,6 +255,9 @@ public class Text3D extends Geometry {
      * @see Font3D
      */
     public Text3D(Font3D font3D, String string) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
 	((Text3DRetained)this.retained).setFont3D(font3D);
 	((Text3DRetained)this.retained).setString(string);
     }
@@ -252,6 +271,9 @@ public class Text3D extends Geometry {
      * @see Font3D
      */
     public Text3D(Font3D font3D, String string, Point3f position) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
 	((Text3DRetained)this.retained).setFont3D(font3D);
 	((Text3DRetained)this.retained).setString(string);
 	((Text3DRetained)this.retained).setPosition(position);
@@ -268,6 +290,9 @@ public class Text3D extends Geometry {
      */
     public Text3D(Font3D font3D, String string, Point3f position,
 		  int alignment, int path) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
 	((Text3DRetained)this.retained).setFont3D(font3D);
 	((Text3DRetained)this.retained).setString(string);
 	((Text3DRetained)this.retained).setPosition(position);
diff --git a/src/classes/share/javax/media/j3d/Text3DRetained.java b/src/classes/share/javax/media/j3d/Text3DRetained.java
index f446850..23c2ae6 100644
--- a/src/classes/share/javax/media/j3d/Text3DRetained.java
+++ b/src/classes/share/javax/media/j3d/Text3DRetained.java
@@ -472,7 +472,7 @@ class Text3DRetained extends GeometryRetained {
 				// use its bounds and not localBounds.
 				// bounds is actually a reference to
 				// mirrorShape3D.source.localBounds.
-				// TODO : Should only need to update distinct localBounds.
+				// XXXX : Should only need to update distinct localBounds.
 				s.getCombineBounds((BoundingBox)s.bounds);
 			    }
 
@@ -838,45 +838,39 @@ class Text3DRetained extends GeometryRetained {
       super.markAsLive();
     }
 
-
-    boolean intersect(PickShape pickShape, double dist[], Point3d iPnt) {
-	Transform3D tempT3D = VirtualUniverse.mc.getTransform3D(null);
+    boolean intersect(PickShape pickShape, PickInfo.IntersectionInfo iInfo,  int flags, Point3d iPnt) {
+	Transform3D tempT3D = new Transform3D();
 	GeometryArrayRetained geo = null;
 	int sIndex = -1;
 	PickShape newPS;
-	double x = 0, y = 0, z = 0;
 	double minDist = Double.MAX_VALUE;
-
+        double distance =0.0;
+        Point3d closestIPnt = new Point3d();
+        
 	for (int i=0; i < numChars; i++) {
 	    geo= geometryList[i];
 	    if (geo != null) {
 		tempT3D.invert(charTransforms[i]);
 		newPS = pickShape.transform(tempT3D);
-		if (geo.intersect(newPS, dist, iPnt)) {
-		    if (dist == null) {
+		if (geo.intersect(newPS, iInfo, flags, iPnt)) {
+		    if (flags == 0) {
 			return true;
 		    }
-		    if (dist[0] < minDist) {
+                    distance = newPS.distance(iPnt);
+		    if (distance < minDist) {
 			sIndex = i;
-			minDist = dist[0];
-			x = iPnt.x;
-			y = iPnt.y;
-			z = iPnt.z;
+			minDist = distance;
+                        closestIPnt.set(iPnt);
 		    }    
 		}
 	    }
 	}
 	
-	FreeListManager.freeObject(FreeListManager.TRANSFORM3D, tempT3D);
-
 	if (sIndex >= 0) {
 	    // We need to transform iPnt to the vworld to compute the actual distance.
 	    // In this method we'll transform iPnt by its char. offset. Shape3D will
 	    // do the localToVworld transform.
-	    iPnt.x = x;
-	    iPnt.y = y;
-	    iPnt.z = z;
-	    dist[0] = minDist;
+	    iPnt.set(closestIPnt);
 	    charTransforms[sIndex].transform(iPnt);
 	    return true;
 	} 
diff --git a/src/classes/share/javax/media/j3d/Texture.java b/src/classes/share/javax/media/j3d/Texture.java
index 4eaae1e..7e28ef8 100644
--- a/src/classes/share/javax/media/j3d/Texture.java
+++ b/src/classes/share/javax/media/j3d/Texture.java
@@ -534,6 +534,22 @@ public abstract class Texture extends NodeComponent {
      */
     public static final int ANISOTROPIC_SINGLE_VALUE = 1;
 
+       // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_ANISOTROPIC_FILTER_READ,
+        ALLOW_BOUNDARY_COLOR_READ,
+        ALLOW_BOUNDARY_MODE_READ,        
+        ALLOW_ENABLE_READ,
+        ALLOW_FILTER4_READ,
+        ALLOW_FILTER_READ,        
+        ALLOW_FORMAT_READ,
+        ALLOW_IMAGE_READ,
+        ALLOW_LOD_RANGE_READ,
+        ALLOW_MIPMAP_MODE_READ,        
+        ALLOW_SHARPEN_TEXTURE_READ,
+        ALLOW_SIZE_READ        
+    };
+    
     /**
      * Constructs a Texture object with default parameters.
      * The default values are as follows:
@@ -566,6 +582,8 @@ public abstract class Texture extends NodeComponent {
      */
     public Texture() {
 	// Just use default values
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -600,6 +618,9 @@ public abstract class Texture extends NodeComponent {
 	    (format != RGB) && (format != RGBA)) {
 	    throw new IllegalArgumentException(J3dI18N.getString("Texture1"));
 	}
+        
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
 
 	int widPower = getPowerOf2(width);
 	if (widPower == -1)
@@ -654,6 +675,9 @@ public abstract class Texture extends NodeComponent {
 	    throw new IllegalArgumentException(J3dI18N.getString("Texture1"));
 	}
 
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
 	int widPower = getPowerOf2(width);
 	if (widPower == -1)
 	    throw new IllegalArgumentException(J3dI18N.getString("Texture2"));
@@ -1722,7 +1746,8 @@ public abstract class Texture extends NodeComponent {
 	  rt.initImage(i, image);
 	}
       }
-      // TODO: clone new v1.2 attributes
+      // XXXX: clone new v1.2 attributes?
+      // NOTE: This sppears to have already been done
     }
 
  /** 
diff --git a/src/classes/share/javax/media/j3d/Texture2D.java b/src/classes/share/javax/media/j3d/Texture2D.java
index 5fa7be7..dc5be50 100644
--- a/src/classes/share/javax/media/j3d/Texture2D.java
+++ b/src/classes/share/javax/media/j3d/Texture2D.java
@@ -131,7 +131,10 @@ public class Texture2D extends Texture {
      */
     public static final int DETAIL_MODULATE = 1;
 
-
+    // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+	ALLOW_DETAIL_TEXTURE_READ
+    };
 
     /**
      * Constructs a texture object using default values.
@@ -149,6 +152,9 @@ public class Texture2D extends Texture {
      */
     public Texture2D() {
 	super();
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
     }
 
   /**
@@ -173,6 +179,9 @@ public class Texture2D extends Texture {
 	    int		height){
 
 	super(mipMapMode, format, width, height);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
 
@@ -209,6 +218,9 @@ public class Texture2D extends Texture {
                    int          boundaryWidth) {
 
         super(mipMapMode, format, width, height, boundaryWidth);
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
diff --git a/src/classes/share/javax/media/j3d/TextureAttributes.java b/src/classes/share/javax/media/j3d/TextureAttributes.java
index d302e50..cb2f002 100644
--- a/src/classes/share/javax/media/j3d/TextureAttributes.java
+++ b/src/classes/share/javax/media/j3d/TextureAttributes.java
@@ -411,6 +411,15 @@ public class TextureAttributes extends NodeComponent {
      */
     public static final int COMBINE_ONE_MINUS_SRC_ALPHA	= 3;
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_BLEND_COLOR_READ,
+        ALLOW_COLOR_TABLE_READ,
+        ALLOW_COMBINE_READ,
+        ALLOW_MODE_READ,
+        ALLOW_TRANSFORM_READ        
+    };
+    
     /**
      * Constructs a TextureAttributes object with default parameters.
      * The default values are as follows:
@@ -441,6 +450,8 @@ public class TextureAttributes extends NodeComponent {
      * </ul>
      */
     public TextureAttributes()  {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
   
     /**
@@ -474,7 +485,10 @@ public class TextureAttributes extends NodeComponent {
 	    throw new IllegalArgumentException(J3dI18N.getString("TextureAttributes9"));
 	}
 
-	((TextureAttributesRetained)this.retained).initTextureMode(textureMode);
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+
+        ((TextureAttributesRetained)this.retained).initTextureMode(textureMode);
 	((TextureAttributesRetained)this.retained).initTextureBlendColor(textureBlendColor);
 	((TextureAttributesRetained)this.retained).initTextureTransform(transform);
 	((TextureAttributesRetained)this.retained).initPerspectiveCorrectionMode(perspCorrectionMode);
diff --git a/src/classes/share/javax/media/j3d/TextureAttributesRetained.java b/src/classes/share/javax/media/j3d/TextureAttributesRetained.java
index c77e743..d4b0993 100644
--- a/src/classes/share/javax/media/j3d/TextureAttributesRetained.java
+++ b/src/classes/share/javax/media/j3d/TextureAttributesRetained.java
@@ -525,9 +525,11 @@ class TextureAttributesRetained extends NodeComponentRetained {
 				int[] combineRgbFcn, int[] combineAlphaFcn,
 				int combineRgbScale, int combineAlphaScale);
 
-    native void restoreBlend1Pass(long ctx);
-    native void updateBlend2Pass(long ctx);
-
+    // NOTE: the following native methods are not used any more, since
+    // we no longer do simulated multi-pass by default
+    // (and with shaders, this won't work anyway)
+//    native void restoreBlend1Pass(long ctx);
+//    native void updateBlend2Pass(long ctx);
 
     void updateNative(Canvas3D cv, boolean simulate, int textureFormat) {
 
@@ -653,31 +655,31 @@ class TextureAttributesRetained extends NodeComponentRetained {
 	    case TextureAttributes.COMBINE:
 	    case TextureAttributes.REPLACE:
 	         cv.setBlendFunc(cv.ctx,
-			TransparencyAttributesRetained.BLEND_ONE,
-	                TransparencyAttributesRetained.BLEND_ZERO);
+			TransparencyAttributes.BLEND_ONE,
+	                TransparencyAttributes.BLEND_ZERO);
 		 break;
 	    case TextureAttributes.MODULATE:
 	         cv.setBlendFunc(cv.ctx,
-			TransparencyAttributesRetained.BLEND_DST_COLOR,
-	                TransparencyAttributesRetained.BLEND_ZERO);
+			TransparencyAttributes.BLEND_DST_COLOR,
+	                TransparencyAttributes.BLEND_ZERO);
 		 break;
 	    case TextureAttributes.DECAL:
 		 if (textureFormat == Texture.RGBA) {
 	             cv.setBlendFunc(cv.ctx,
-			TransparencyAttributesRetained.BLEND_SRC_ALPHA,
-	                TransparencyAttributesRetained.BLEND_ONE_MINUS_SRC_ALPHA);
+			TransparencyAttributes.BLEND_SRC_ALPHA,
+	                TransparencyAttributes.BLEND_ONE_MINUS_SRC_ALPHA);
 		 } else {
 	             cv.setBlendFunc(cv.ctx,
-			TransparencyAttributesRetained.BLEND_ONE,
-	                TransparencyAttributesRetained.BLEND_ZERO);
+			TransparencyAttributes.BLEND_ONE,
+	                TransparencyAttributes.BLEND_ZERO);
 		 }
 		 break;
 	    case TextureAttributes.BLEND:
 		cv.setBlendColor(cv.ctx, textureBlendColor.x, textureBlendColor.y,
 				 textureBlendColor.z, textureBlendColor.w);
 		cv.setBlendFunc(cv.ctx,
-				TransparencyAttributesRetained.BLEND_CONSTANT_COLOR,
-				TransparencyAttributesRetained.BLEND_ONE_MINUS_SRC_COLOR);
+				TransparencyAttributes.BLEND_CONSTANT_COLOR,
+				TransparencyAttributes.BLEND_ONE_MINUS_SRC_COLOR);
 		break;
 	    }
 	}
diff --git a/src/classes/share/javax/media/j3d/TextureBin.java b/src/classes/share/javax/media/j3d/TextureBin.java
index 7145adf..3e1c91f 100644
--- a/src/classes/share/javax/media/j3d/TextureBin.java
+++ b/src/classes/share/javax/media/j3d/TextureBin.java
@@ -27,10 +27,10 @@ class TextureBin extends Object implements ObjectUpdate {
     TextureUnitStateRetained [] texUnitState = null;
 
     // last active texture unit
-    int lastActiveTexUnitIndex;
+    private int lastActiveTexUnitIndex;
 
     // number of active texture unit
-    int numActiveTexUnit;
+    private int numActiveTexUnit;
 
     /**
      * The RenderBin for this object
@@ -38,10 +38,20 @@ class TextureBin extends Object implements ObjectUpdate {
     RenderBin renderBin = null;
 
     /**
-     * The AttribureBin that this TextureBin resides
+     * The EnvironmentSet that this TextureBin resides
+     */
+    EnvironmentSet environmentSet = null;
+
+    /**
+     * The AttributeBin that this TextureBin resides
      */
     AttributeBin attributeBin = null;
 
+    /**
+     * The ShaderBin that this TextureBin resides
+     */
+    ShaderBin shaderBin = null;
+
     /**
      * The references to the next and previous TextureBins in the
      * list.
@@ -172,7 +182,6 @@ class TextureBin extends Object implements ObjectUpdate {
         int i, j;
 	boolean foundDisableUnit = false;
 	numActiveTexUnit = 0;
-	boolean d3dBlendMode = false;
 	lastActiveTexUnitIndex = 0;
 	boolean soleUser = ((tbFlag & TextureBin.SOLE_USER) != 0);
 	TextureRetained prevFirstTexture = null;
@@ -321,11 +330,13 @@ class TextureBin extends Object implements ObjectUpdate {
 		    }
 
 
-		    // track the last active texture unit
-		    // and the total number of active texture unit
+		    // Track the last active texture unit and the total number
+                    // of active texture units. Note that this total number
+                    // now includes disabled units so that there is always
+                    // a one-to-one mapping. We no longer remap texture units.
 		    if (texUnitState[i].isTextureEnabled()) {
-			numActiveTexUnit++;
 			lastActiveTexUnitIndex = i;
+                        numActiveTexUnit = i + 1;
 
 			if (foundDisableUnit) {
 
@@ -337,7 +348,7 @@ class TextureBin extends Object implements ObjectUpdate {
 			foundDisableUnit = true;
 		    }
 		}
-	    } 
+	    }
 
 	    // check to see if the TextureBin sorting criteria is
 	    // modified for this textureBin; if yes, mark that
@@ -698,12 +709,14 @@ class TextureBin extends Object implements ObjectUpdate {
 	    tbFlag |= TextureBin.CONTIGUOUS_ACTIVE_UNITS;
 	    for (int i = 0; i < texUnitState.length; i++) {
 
-                // track the last active texture unit
-		// and the total number of active texture unit
+                // Track the last active texture unit and the total number
+                // of active texture units. Note that this total number
+                // now includes disabled units so that there is always
+                // a one-to-one mapping. We no longer remap texture units.
                 if (texUnitState[i] != null &&
 			texUnitState[i].isTextureEnabled()) {
-                    numActiveTexUnit++;
                     lastActiveTexUnitIndex = i;
+                    numActiveTexUnit = i + 1;
 
                     if (foundDisableUnit) {
 
@@ -754,7 +767,7 @@ class TextureBin extends Object implements ObjectUpdate {
 	    // sorted transparency
 	    if (transparentRMList == null &&
 		(renderBin.transpSortMode == View.TRANSPARENCY_SORT_NONE ||
-		attributeBin.environmentSet.lightBin.geometryBackground != null)) {
+		environmentSet.lightBin.geometryBackground != null)) {
 		//		System.out.println("========> addTransparentTextureBin "+this); 
 		transparentRMList = addAll(transparentRenderMoleculeMap, 
 				addTransparentRMs, transparentRMList, false);
@@ -895,7 +908,7 @@ class TextureBin extends Object implements ObjectUpdate {
     }
 
 
-    // TODO: Could the analysis be done during insertRenderMolecule?
+    // XXXX: Could the analysis be done during insertRenderMolecule?
     // Return the head of the list,
     // if the insertion occurred at beginning of the list
     RenderMolecule insertRenderMolecule(RenderMolecule r, 
@@ -1046,7 +1059,7 @@ class TextureBin extends Object implements ObjectUpdate {
 	}
 	// If the renderMolecule removed is not opaque then ..
 	if (!r.isOpaqueOrInOG && transparentRMList == null && (renderBin.transpSortMode == View.TRANSPARENCY_SORT_NONE ||
-							       attributeBin.environmentSet.lightBin.geometryBackground != null)) {
+							       environmentSet.lightBin.geometryBackground != null)) {
 	    renderBin.removeTransparentObject(this);
 	}
 	// If the rm removed is the one that is referenced in the tinfo
@@ -1065,12 +1078,11 @@ class TextureBin extends Object implements ObjectUpdate {
 		renderBin.removeTextureBin(this);
 	    }
 
-            attributeBin.removeTextureBin(this);
+            shaderBin.removeTextureBin(this);
 	    texUnitState = null;
         }
     }
 
-
     /**
      * This method is called to update the state for this
      * TextureBin. This is only applicable in the single-pass case.
@@ -1078,28 +1090,48 @@ class TextureBin extends Object implements ObjectUpdate {
      * state update.
      */
     void updateAttributes(Canvas3D cv, int pass) {
-	
+
         boolean dirty = ((cv.canvasDirty & (Canvas3D.TEXTUREBIN_DIRTY|
 					    Canvas3D.TEXTUREATTRIBUTES_DIRTY)) != 0);
 
-
 	if (cv.textureBin == this  && !dirty) {
 	    return;
 	}
 
 	cv.textureBin = this;
-
+        
 	// save the current number of active texture unit so as
         // to be able to reset the one that is not enabled in this bin
 
 	int lastActiveTexUnitIdx = -1;
 
-	// set the number active texture unit in Canvas3D
-	cv.setNumActiveTexUnit(numActiveTexUnit);
+        // Get the number of available texture units; this depends on
+        // whether or not shaders are being used.
+        boolean useShaders = (shaderBin.shaderProgram != null);
+        int availableTextureUnits =
+                useShaders ? cv.maxTextureImageUnits : cv.maxTextureUnits;
+
+        // If the number of active texture units is greater than the number of
+        // supported units, and we don't allow simulated multi-texture, then we
+        // need to set a flag indicating that the texture units are invalid.
+        boolean disableTexture = false;
+
+        if (pass < 0 && numActiveTexUnit > availableTextureUnits) {
+            disableTexture = true;
+//            System.err.println("*** TextureBin : number of texture units exceeded");
+        }
+
+        // set the number active texture unit in Canvas3D
+        if (disableTexture) {
+            cv.setNumActiveTexUnit(0);
+        }
+        else {
+            cv.setNumActiveTexUnit(numActiveTexUnit);
+        }
 
 	// state update
-	if (numActiveTexUnit <= 0) {
-	    if (cv.getLastActiveTexUnit() >= 0) {
+	if (numActiveTexUnit <= 0 || disableTexture) {
+            if (cv.getLastActiveTexUnit() >= 0) {
 	        // no texture units enabled
 
 	        // when the canvas supports multi texture units,
@@ -1117,20 +1149,8 @@ class TextureBin extends Object implements ObjectUpdate {
 		cv.setLastActiveTexUnit(-1);
 	    }
 	} else if (pass < 0) {
-	    int j = 0;		
-	    boolean oneToOneMapping;
-
-	    if ((pass == USE_VERTEXARRAY) || VirtualUniverse.mc.isD3D()) {
-		// d3d  or when the texUnitStateMap requires more texture
-		// units than what is supported by the canvas, then
-		// we'll need a compact texture unit mapping, that is,
-		// only the enabled texUnitStates will be mapped to 
-		// texture units. And as a matter of fact, the
-		// render atoms will be rendered as vertex array.
-		oneToOneMapping = false;
-	    } else {
-		oneToOneMapping = true;
-	    }
+
+            int j = 0;
 
 	    for (int i = 0; i < texUnitState.length; i++) {
 
@@ -1156,24 +1176,13 @@ class TextureBin extends Object implements ObjectUpdate {
 		    // unit to a texture unit state
 
 		    lastActiveTexUnitIdx = j;
-		    cv.setTexUnitStateMap(i, j++);
-
-
-		} else if (oneToOneMapping) {
-		    // one to one mapping is needed when display list
-		    // is used to render multi-textured geometries,
-		    // since when display list is created, the texture
-		    // unit state to texture unit mapping is based on
-		    // the geometry texCoordMap only. At render time,
-		    // the texture unit state enable flags could have
-		    // been changed. In keeping a one to one mapping,
-		    // we'll not need to rebuild the display list
+		} else {
 		    if (j <= cv.getLastActiveTexUnit()) { 
 			cv.resetTexture(cv.ctx, j);
 		    }
-
-		    cv.setTexUnitStateMap(i, j++);
 		}
+                
+                j++;
 	    }
 
             // make sure to disable the remaining texture units
@@ -1196,7 +1205,7 @@ class TextureBin extends Object implements ObjectUpdate {
             cv.activeTextureUnit(cv.ctx, 0);
 
 	} else {
-	    // update the last active texture unit state
+            // update the last active texture unit state
 	    if (dirty || cv.texUnitState[0].mirror == null ||
 			cv.texUnitState[0].mirror != 
 		    		texUnitState[lastActiveTexUnitIndex].mirror) {
@@ -1205,7 +1214,6 @@ class TextureBin extends Object implements ObjectUpdate {
 		cv.texUnitState[0].mirror = 
 		    texUnitState[lastActiveTexUnitIndex].mirror;
 
-		cv.setTexUnitStateMap(0, 0);
 		cv.setLastActiveTexUnit(0);
 	    }
 	}
@@ -1222,46 +1230,43 @@ class TextureBin extends Object implements ObjectUpdate {
 
     void render(Canvas3D cv, Object rlist) {
 
-	boolean d3dBlendMode = false;
 	cv.texLinearMode = false;
 
 	/*
 	System.out.println("TextureBin/render " + this +
 		" numActiveTexUnit= " + numActiveTexUnit + 
-		" numTexUnitSupported= " + cv.numTexUnitSupported);
+		" maxTextureUnits= " + cv.maxTextureUnits);
 	*/
 
 	// include this TextureBin to the to-be-updated state set in canvas
 	cv.setStateToUpdate(Canvas3D.TEXTUREBIN_BIT, this);
-	
-	if ((texUnitState != null) &&
-	    VirtualUniverse.mc.isD3D()) {
+
+        // For D3D - set the texLinearMode flag in the canvas if texcoord
+        // generation is enabled in object_linear mode for any texture unit.
+	if ((texUnitState != null) && VirtualUniverse.mc.isD3D()) {
 	    TextureUnitStateRetained tus;
-	    // use multi-pass if one of the stage use blend mode
 	    for (int i = 0; i < texUnitState.length; i++) {
 		tus = texUnitState[i];
-		if ((tus != null) &&
-		    tus.isTextureEnabled()) {
-		    if (tus.needBlend2Pass(cv)) {
-			d3dBlendMode = true;
-		    }
+		if ((tus != null) && tus.isTextureEnabled()) {
 		    if ((tus.texGen != null) &&
-			(tus.texGen.genMode ==
-			 TexCoordGeneration.OBJECT_LINEAR)) {
+			(tus.texGen.genMode == TexCoordGeneration.OBJECT_LINEAR)) {
 			cv.texLinearMode = true;
 		    }
 		}
 	    }
-	}
+        }
+
+        // If shaders are not being used, and if allowSimulatedMultiTexture
+        // property is set, then we will use simulated (multi-pass)
+        // multi-texture when the requested number of texture units exceeds
+        // the available number of texture units
+        boolean useShaders = (shaderBin.shaderProgram != null);
+        int availableTextureUnits =
+                useShaders ? cv.maxTextureImageUnits : cv.maxTextureUnits;
 
-	if ((numActiveTexUnit > cv.numTexUnitSupported) || 
-	    d3dBlendMode) {
+        if (!useShaders && (numActiveTexUnit > availableTextureUnits) &&
+                VirtualUniverse.mc.allowSimulatedMultiTexture) {
 	    multiPassRender(cv, rlist);
-	} else if ((numActiveTexUnit > 0) &&
-		   !VirtualUniverse.mc.isD3D() &&
-		   (texUnitState.length > cv.numTexUnitSupported) && 
-		   ((tbFlag & TextureBin.CONTIGUOUS_ACTIVE_UNITS) == 0)) {
-	    renderList(cv, USE_VERTEXARRAY, rlist);
 	} else {
 	    renderList(cv, USE_DISPLAYLIST, rlist);
 	} 
@@ -1286,7 +1291,7 @@ class TextureBin extends Object implements ObjectUpdate {
      */
     void renderList(Canvas3D cv, int pass, RenderMolecule rlist) {
 
-	// bit mask of all attr fields that are equivalent across 
+        // bit mask of all attr fields that are equivalent across 
 	// renderMolecules thro. ORing of invisible RMs.
 	int combinedDirtyBits = 0;
 	boolean rmVisible = true;
@@ -1330,9 +1335,11 @@ class TextureBin extends Object implements ObjectUpdate {
     /**
      * multi rendering pass to simulate multiple texture units 
      */
-    void multiPassRender(Canvas3D cv, Object rlist) {
+    private void multiPassRender(Canvas3D cv, Object rlist) {
+        
+        assert VirtualUniverse.mc.allowSimulatedMultiTexture;
 
-	boolean startToSimulate = false;
+        boolean startToSimulate = false;
 	boolean isFogEnabled = false;
 
 	// No lazy download of texture for multi-pass,
@@ -1366,7 +1373,7 @@ class TextureBin extends Object implements ObjectUpdate {
         // first check if there is fog in the path
 	// if there is, then turn off fog now and turn it back on
 	// for the last pass only
-        isFogEnabled = (attributeBin.environmentSet.fog != null);
+        isFogEnabled = (environmentSet.fog != null);
 
 	TextureUnitStateRetained tus;
 
@@ -1388,31 +1395,12 @@ class TextureBin extends Object implements ObjectUpdate {
 		    }
 		}
 		
-		if (!tus.needBlend2Pass(cv)) {
-		    // turn on fog again in the last pass
+                // turn on fog again in the last pass
 
-		    if (i == lastActiveTexUnitIndex && isFogEnabled) {
-		        cv.setFogEnableFlag(cv.ctx, true);
-		    }
-		    renderList(cv, i, rlist);
-
-		} else {
-		    // D3d needs two passes to simulate Texture.Blend mode
-		    tus.texAttrs.updateNative(cv, false, tus.texture.format);
-		    renderList(cv, i, rlist);
-
-		    tus.texAttrs.updateBlend2Pass(cv.ctx);
-
-		    // turn on fog again in the last pass
-
-		    if (i == lastActiveTexUnitIndex && isFogEnabled) {
-		        cv.setFogEnableFlag(cv.ctx, true);
-		    }
-		    renderList(cv, i, rlist);
-
-                    // restore original blend mode in case
-		    tus.texAttrs.restoreBlend1Pass(cv.ctx);
-		}
+                if (i == lastActiveTexUnitIndex && isFogEnabled) {
+                    cv.setFogEnableFlag(cv.ctx, true);
+                }
+                renderList(cv, i, rlist);
 	    }
 	}
 
@@ -1465,16 +1453,22 @@ class TextureBin extends Object implements ObjectUpdate {
 		transparentRMList = head;
 		if (transparentRMList == null &&
 		    (renderBin.transpSortMode == View.TRANSPARENCY_SORT_NONE ||
-		     attributeBin.environmentSet.lightBin.geometryBackground != null)) {
+		     environmentSet.lightBin.geometryBackground != null)) {
 		    renderBin.removeTransparentObject(this);
 		}
+		// Issue 129: remove the RM's render atoms from the
+		// list of transparent render atoms
+		if ((renderBin.transpSortMode == View.TRANSPARENCY_SORT_GEOMETRY) &&
+		    (environmentSet.lightBin.geometryBackground == null)) {
+		    r.addRemoveTransparentObject(renderBin, false);
+		}
 	    }
 	}
 	HashMap renderMoleculeMap;
 	RenderMolecule startList;
 	
 	// Now insert in the other bin
-	r.evalAlphaUsage(attributeBin.definingRenderingAttributes, texUnitState);
+	r.evalAlphaUsage(shaderBin.attributeBin.definingRenderingAttributes, texUnitState);
 	r.isOpaqueOrInOG = r.isOpaque() ||r.inOrderedGroup;
 	if (r.isOpaqueOrInOG) {
 	    startList = opaqueRMList;
@@ -1565,14 +1559,20 @@ class TextureBin extends Object implements ObjectUpdate {
 	    // If transparent and not in bg geometry and inodepth sorted transparency
 	    if (transparentRMList == null&&
 		(renderBin.transpSortMode == View.TRANSPARENCY_SORT_NONE ||
-		 attributeBin.environmentSet.lightBin.geometryBackground != null)) {
+		 environmentSet.lightBin.geometryBackground != null)) {
 		transparentRMList = startList;
 		renderBin.addTransparentObject(this);
 	    }
 	    else {
 		transparentRMList = startList;
 	    }
-		
+	    // Issue 129: add the RM's render atoms to the list of
+	    // transparent render atoms
+	    // XXXX: do we need to resort the list after the add???
+	    if ((renderBin.transpSortMode == View.TRANSPARENCY_SORT_GEOMETRY) &&
+		(environmentSet.lightBin.geometryBackground == null)) {
+		r.addRemoveTransparentObject(renderBin, true);
+	    }
 	}
     }
 
@@ -1659,10 +1659,10 @@ class TextureBin extends Object implements ObjectUpdate {
 	if (numEditingRenderMolecules == 0) {
 
 	    // if number of editing renderMolecules goes to 0,
-	    // inform the attributeBin that this textureBin goes to
+	    // inform the shaderBin that this textureBin goes to
 	    // zombie state
 
-	    attributeBin.decrActiveTextureBin();
+	    shaderBin.decrActiveTextureBin();
 	}
     }
 
@@ -1671,9 +1671,9 @@ class TextureBin extends Object implements ObjectUpdate {
 	if (numEditingRenderMolecules == 0) {
 
 	    // if this textureBin is in zombie state, inform
-	    // the attributeBin that this textureBin is activated again.
+	    // the shaderBin that this textureBin is activated again.
 
-	    attributeBin.incrActiveTextureBin();
+	    shaderBin.incrActiveTextureBin();
 	}
 	    
 	numEditingRenderMolecules++;
diff --git a/src/classes/share/javax/media/j3d/TextureRetained.java b/src/classes/share/javax/media/j3d/TextureRetained.java
index 190c43b..545ad3a 100644
--- a/src/classes/share/javax/media/j3d/TextureRetained.java
+++ b/src/classes/share/javax/media/j3d/TextureRetained.java
@@ -12,9 +12,9 @@
 
 package javax.media.j3d;
 
+import java.awt.image.BufferedImage;
 import java.util.*;
 import javax.vecmath.*;
-import java.awt.image.DataBuffer;
 import java.awt.image.DataBufferByte;
 
 /**
@@ -949,7 +949,7 @@ abstract class TextureRetained extends NodeComponentRetained {
 
         super.doSetLive(backgroundGroup, refCount);
 
-	// TODO: for now, do setLive for all the defined images.
+	// XXXX: for now, do setLive for all the defined images.
 	// But in theory, we only need to setLive those within the
 	// baseLevel and maximumLevel range. But then we'll need
 	// setLive and clearLive image when the range changes.
@@ -1366,8 +1366,9 @@ abstract class TextureRetained extends NodeComponentRetained {
 		yoffset = image.height - yoffset - height;
 
 	    } else {
+                // Fix issue 132
 		imageData = ((DataBufferByte)
-			image.bImage[0].getData().getDataBuffer()).getData();
+			((BufferedImage)image.bImage[0]).getRaster().getDataBuffer()).getData();
 
 	        // based on the yUp flag in the associated ImageComponent,
 	        // adjust the yoffset
@@ -2031,12 +2032,12 @@ abstract class TextureRetained extends NodeComponentRetained {
 	if (arg == null) {
 	    // no subimage info, so the entire image is to be updated
 	    info.entireImage = true;
-
-	} else if ((arg.width >= width/2) && (arg.height >= height/2)) {
-
-	    // if the subimage dimension is close to the complete dimension,
-            // use the full update (it's more efficient)
-	    info.entireImage = true;
+        // Fix issue 117 using ogl subimage always
+//	} else if ((arg.width >= width/2) && (arg.height >= height/2)) {
+//
+//	    // if the subimage dimension is close to the complete dimension,
+//            // use the full update (it's more efficient)
+//	    info.entireImage = true;
 	} else {
 	    info.entireImage = false;
 	}
@@ -2137,30 +2138,30 @@ abstract class TextureRetained extends NodeComponentRetained {
 	    mirrorTexture.addImageUpdateInfo(level, face, null);
 
 	} else if ((component & IMAGES_CHANGED) != 0) {
-
+	    
 	    Object [] arg = (Object []) value;
 	    ImageComponent [] images = (ImageComponent[])arg[0];
-            int face = ((Integer)arg[1]).intValue();
-
+	    int face = ((Integer)arg[1]).intValue();
+	    
 	    for (int i = 0; i < images.length; i++) {
-
+		
 		// first remove texture from the userList of the current
-                // referencing image
-	        if (mirrorTexture.images[face][i] != null) {
-	            mirrorTexture.images[face][i].removeUser(mirror);
-	        }
-
-	        // assign the new image and add texture to the userList
-	        if (images[i] == null) {
-	            mirrorTexture.images[face][i] = null;
-	        } else {
-	            mirrorTexture.images[face][i] = 
-				(ImageComponentRetained)images[i].retained;
-	            mirrorTexture.images[face][i].addUser(mirror);
-                }
+		// referencing image
+		if (mirrorTexture.images[face][i] != null) {
+		    mirrorTexture.images[face][i].removeUser(mirror);
+		}
+		
+		// assign the new image and add texture to the userList
+		if (images[i] == null) {
+		    mirrorTexture.images[face][i] = null;
+		} else {
+		    mirrorTexture.images[face][i] = 
+			(ImageComponentRetained)images[i].retained;
+		    mirrorTexture.images[face][i].addUser(mirror);
+		}
 	    }
 	    mirrorTexture.updateResourceCreationMask();
-
+	    
 	    // NOTE: the old images have to be removed from the
 	    // renderBins' NodeComponentList and new image have to be
 	    // added to the lists. This will be taken care of
@@ -2169,7 +2170,7 @@ abstract class TextureRetained extends NodeComponentRetained {
 
 	} else if ((component & BASE_LEVEL_CHANGED) != 0) {
 	    int level = ((Integer)value).intValue();
-
+	    
 	    if (level < mirrorTexture.baseLevel) {
 
 		// add texture to the userList of those new levels of 
diff --git a/src/classes/share/javax/media/j3d/TextureUnitState.java b/src/classes/share/javax/media/j3d/TextureUnitState.java
index 635a499..fa9949c 100644
--- a/src/classes/share/javax/media/j3d/TextureUnitState.java
+++ b/src/classes/share/javax/media/j3d/TextureUnitState.java
@@ -66,7 +66,11 @@ public class TextureUnitState extends NodeComponent {
     public static final int ALLOW_STATE_WRITE =
 	CapabilityBits.TEXTURE_UNIT_STATE_ALLOW_STATE_WRITE;
 
-
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_STATE_READ
+    };
+    
     /**
      * Constructs a TextureUnitState component object using defaults for all
      * state variables. All component object references are initialized
@@ -74,6 +78,8 @@ public class TextureUnitState extends NodeComponent {
      */
     public TextureUnitState() {
 	// Just use default values
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -90,6 +96,8 @@ public class TextureUnitState extends NodeComponent {
     public TextureUnitState(Texture texture,
 			     TextureAttributes textureAttributes,
 			     TexCoordGeneration texCoordGeneration) {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
 
 	((TextureUnitStateRetained)this.retained).initTexture(texture);
 	((TextureUnitStateRetained)this.retained).initTextureAttributes(
diff --git a/src/classes/share/javax/media/j3d/TextureUnitStateRetained.java b/src/classes/share/javax/media/j3d/TextureUnitStateRetained.java
index 86dce99..266ef12 100644
--- a/src/classes/share/javax/media/j3d/TextureUnitStateRetained.java
+++ b/src/classes/share/javax/media/j3d/TextureUnitStateRetained.java
@@ -592,17 +592,6 @@ class TextureUnitStateRetained extends NodeComponentRetained {
 	return (texture != null && texture.enable);
     }
 
-    // use by D3D to simulate OGL blend mode using multi-pass
-    final boolean needBlend2Pass(Canvas3D cv) {
-	return ((texAttrs != null) && 
-		VirtualUniverse.mc.isD3D() &&
-		((cv.textureExtendedFeatures &
-		      Canvas3D.TEXTURE_LERP) == 0) &&
-		(texAttrs.textureMode == TextureAttributes.BLEND) &&
-		(texture.format != Texture.ALPHA) &&
-		(texture.format != Texture.INTENSITY));
-    }
-
     void handleFrequencyChange(int bit) {
         switch (bit) {
         case TextureUnitState.ALLOW_STATE_WRITE: {
diff --git a/src/classes/share/javax/media/j3d/TimerThread.java b/src/classes/share/javax/media/j3d/TimerThread.java
index ae34fc6..a5f8878 100644
--- a/src/classes/share/javax/media/j3d/TimerThread.java
+++ b/src/classes/share/javax/media/j3d/TimerThread.java
@@ -33,7 +33,7 @@ class TimerThread extends Thread {
     // Wakeup {all?} Sound Scheduler{s} for every sample time reach
     // QUESTION: this sampling time is set to a very large value so Sound
     //          Schedulers are not pinged often unless explicitly requested
-    // TODO: need a way to remove/null this condition when all
+    // XXXX: need a way to remove/null this condition when all
     //          soundschedulers are halted
     private WakeupOnElapsedTime soundSchedCond = 
         new WakeupOnElapsedTime(120000);  // every 2 minutes
@@ -61,7 +61,7 @@ class TimerThread extends Thread {
     }
 
     void addSoundSchedCond(long wakeupTime) {
-        // TODO: there are potentially multiple sound schedulers.
+        // XXXX: there are potentially multiple sound schedulers.
         //     this code will force a wait up on ALL sound schedulers
         //     even though only one needs to process the sound that
         //     this wakeup condition is triggered by.
diff --git a/src/classes/share/javax/media/j3d/Transform3D.java b/src/classes/share/javax/media/j3d/Transform3D.java
index 69e5c62..99920d4 100644
--- a/src/classes/share/javax/media/j3d/Transform3D.java
+++ b/src/classes/share/javax/media/j3d/Transform3D.java
@@ -4588,7 +4588,7 @@ public class Transform3D {
 	double[] svdScales = new double[3];
 
 
-	// TODO: initialize to 0's if alread allocd? Should not have to, since
+	// XXXX: initialize to 0's if alread allocd? Should not have to, since
 	// no operations depend on these being init'd to zero.
 
 	int converged, negCnt=0;
@@ -4833,7 +4833,7 @@ public class Transform3D {
 	    }
 	}
 
-	// TODO: could eliminate use of t1 and t1 by making a new method which
+	// XXXX: could eliminate use of t1 and t1 by making a new method which
 	// transposes and multiplies two matricies
 	transpose_mat(u1, t1);
 	transpose_mat(v1, t2);
diff --git a/src/classes/share/javax/media/j3d/TransformGroup.java b/src/classes/share/javax/media/j3d/TransformGroup.java
index 5aae8d2..a4b3f19 100644
--- a/src/classes/share/javax/media/j3d/TransformGroup.java
+++ b/src/classes/share/javax/media/j3d/TransformGroup.java
@@ -61,11 +61,18 @@ public class TransformGroup extends Group {
   public static final int
     ALLOW_TRANSFORM_WRITE = CapabilityBits.TRANSFORM_GROUP_ALLOW_TRANSFORM_WRITE;
 
+    // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_TRANSFORM_READ
+    };
+
     /**
      * Constructs and initializes a TransformGroup using an
      * identity transform.
      */
     public TransformGroup() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);        
     }
 
     /**
@@ -74,11 +81,14 @@ public class TransformGroup extends Group {
      * @param t1 the transform3D object
      * @exception BadTransformException if the transform is not affine.
      */
-    public TransformGroup(Transform3D t1) {
+    public TransformGroup(Transform3D t1) {        
 	if (!t1.isAffine()) {
 	    throw new BadTransformException(J3dI18N.getString("TransformGroup0"));
 	}
-
+        
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
+        
 	((TransformGroupRetained)this.retained).setTransform(t1);
     }
 
diff --git a/src/classes/share/javax/media/j3d/TransformGroupData.java b/src/classes/share/javax/media/j3d/TransformGroupData.java
index e41a086..e14d3c3 100644
--- a/src/classes/share/javax/media/j3d/TransformGroupData.java
+++ b/src/classes/share/javax/media/j3d/TransformGroupData.java
@@ -14,8 +14,8 @@ package javax.media.j3d;
 
 class TransformGroupData extends NodeData {
     // per path node data
-    // TODO: replace per path mirror objects with node data
-    // TODO: move other TransfromGroup related data here
+    // XXXX: replace per path mirror objects with node data
+    // XXXX: move other TransfromGroup related data here
     boolean switchDirty = false;
 
     // use for eliminate multiple updates and generate unique targets
diff --git a/src/classes/share/javax/media/j3d/TransformGroupRetained.java b/src/classes/share/javax/media/j3d/TransformGroupRetained.java
index 734a6b3..0672359 100644
--- a/src/classes/share/javax/media/j3d/TransformGroupRetained.java
+++ b/src/classes/share/javax/media/j3d/TransformGroupRetained.java
@@ -92,7 +92,7 @@ class TransformGroupRetained extends GroupRetained implements TargetsInterface
     // User copy.
     CachedTargets[] cachedTargets = null;
 
-    // Contains per path data, TODO: move to NodeRetained
+    // Contains per path data, XXXX: move to NodeRetained
     TransformGroupData[] perPathData = null;
 
 
@@ -512,7 +512,7 @@ class TransformGroupRetained extends GroupRetained implements TargetsInterface
       int len;
       Object obj;
       
-      // TODO - optimization for targetThreads computation, require
+      // XXXX - optimization for targetThreads computation, require
       // cleanup in GroupRetained.doSetLive()
       //int savedTargetThreads = 0;
       //savedTargetThreads = s.transformTargetThreads;
@@ -765,7 +765,7 @@ class TransformGroupRetained extends GroupRetained implements TargetsInterface
             }
         }
         }
-	// TODO: recontruct targetThreads
+	// XXXX: recontruct targetThreads
     }
 
    
@@ -1155,7 +1155,7 @@ class TransformGroupRetained extends GroupRetained implements TargetsInterface
         // type is ignored here, only need for SharedGroup
 	
         if (type == TargetsInterface.TRANSFORM_TARGETS) {
-	    // TODO : For now we'll OR more than exact.
+	    // XXXX : For now we'll OR more than exact.
 	    //targetThreads = localTargetThreads | childTargetThreads;
 	    targetThreads = targetThreads | childTargetThreads;
             if (parentTransformLink != null) {
diff --git a/src/classes/share/javax/media/j3d/TransformStructure.java b/src/classes/share/javax/media/j3d/TransformStructure.java
index 0232f98..0edc91c 100644
--- a/src/classes/share/javax/media/j3d/TransformStructure.java
+++ b/src/classes/share/javax/media/j3d/TransformStructure.java
@@ -182,7 +182,7 @@ class TransformStructure extends J3dStructure implements ObjectUpdate {
 	    }
 	    processCurrentLocalToVworld();
 	    
-	    // TODO: temporary -- processVwcBounds will be
+	    // XXXX: temporary -- processVwcBounds will be
 	    // done in GeometryStructure
 	    if (objectList.size() > 0) { 
 		processGeometryAtomVwcBounds();
@@ -393,7 +393,7 @@ class TransformStructure extends J3dStructure implements ObjectUpdate {
 	    tg  = (TransformGroupRetained)dirtyTransformGroups.get(i);
 	    // Check if the transformGroup is still alive
 	    
-	    // TODO: This is a hack, should be fixed after EA
+	    // XXXX: This is a hack, should be fixed after EA
 	    // Null pointer checking should be removed!
 	    // should call trans = tg.getCurrentChildLocalToVworld(key);
 	    synchronized(tg) {
diff --git a/src/classes/share/javax/media/j3d/TransparencyAttributes.java b/src/classes/share/javax/media/j3d/TransparencyAttributes.java
index 9e88f62..d5be231 100644
--- a/src/classes/share/javax/media/j3d/TransparencyAttributes.java
+++ b/src/classes/share/javax/media/j3d/TransparencyAttributes.java
@@ -14,18 +14,18 @@ package javax.media.j3d;
 
 /**
  * The TransparencyAttributes object defines all attributes affecting
- * transparency of the object. The transparency attributes are:<P>
- * <UL>
- * <LI>Transparency mode - defines how transparency is applied to
- * this Appearance component object:</LI><P>
- * <UL>
- * <LI>FASTEST - uses the fastest available method for transparency.</LI><P>
- * <LI>NICEST - uses the nicest available method for transparency.</LI><P>
- * <LI>SCREEN_DOOR - uses screen-door transparency. This is done using 
+ * transparency of the object. The transparency attributes are:<p>
+ * <ul>
+ * <li>Transparency mode - defines how transparency is applied to
+ * this Appearance component object:</li><p>
+ * <ul>
+ * <li>FASTEST - uses the fastest available method for transparency.</li><p>
+ * <li>NICEST - uses the nicest available method for transparency.</li><p>
+ * <li>SCREEN_DOOR - uses screen-door transparency. This is done using 
  * an on/off stipple pattern in which the percentage of transparent pixels 
  * is approximately equal to the value specified by the transparency 
- * parameter.</LI><P>
- * <LI>BLENDED - uses alpha blended transparency. The blend equation is 
+ * parameter.</li><p>
+ * <li>BLENDED - uses alpha blended transparency. The blend equation is 
  * specified by the srcBlendFunction and dstBlendFunction attributes. 
  * The default equation is:
  * <ul>
@@ -45,27 +45,36 @@ package javax.media.j3d;
  * alpha<sub><font size=-1>pix</font></sub> *
  * (1-transparency)</code>.
  * </ul>
- * </LI><P>
- * <LI>NONE - no transparency; opaque object.</LI><P>
- * </UL>
- * <LI>Blend function - used in blended transparency and antialiasing
+ * </li><p>
+ * <li>NONE - no transparency; opaque object.</li><p>
+ * </ul>
+ * <li>Transparency value - the amount of transparency to be applied to this
+ * Appearance component object. The transparency values are in the
+ * range [0.0,&nbsp;1.0], with 0.0 being fully opaque and 1.0 being
+ * fully transparent.</li><p>
+ * <li>Blend function - used in blended transparency and antialiasing
  * operations. The source function specifies the factor that is 
  * multiplied by the source color. This value is added to the product 
  * of the destination factor and the destination color. The default 
  * source blend function is BLEND_SRC_ALPHA. The source blend function 
- * is one of the following:</LI><P>
- * <UL>
- * <LI>BLEND_ZERO - the blend function is <code>f = 0</code>.</LI>
- * <LI>BLEND_ONE - the blend function is <code>f = 1</code>.</LI>
- * <LI>BLEND_SRC_ALPHA - the blend function is <code>f = 
- * alpha<sub><font size=-1>src</font></sub></code>.</LI>
- * <LI>BLEND_ONE_MINUS_SRC_ALPHA - the blend function is <code>f = 
- * 1 - alpha<sub><font size=-1>src</font></sub></code>.</LI></UL><P>
- * <LI>Blend value - the amount of transparency to be applied to this
- * Appearance component object. The transparency values are in the
- * range [0.0, 1.0], with 0.0 being fully opaque and 1.0 being
- * fully transparent.</LI><P>
- * </UL>
+ * is one of the following:</li><p>
+ * <ul>
+ * <li>BLEND_ZERO - the blend function is <code>f = 0</code></li>
+ * <li>BLEND_ONE - the blend function is <code>f = 1</code></li>
+ * <li>BLEND_SRC_ALPHA - the blend function is <code>f = 
+ * alpha<sub><font size=-1>src</font></sub></code></li>
+ * <li>BLEND_ONE_MINUS_SRC_ALPHA - the blend function is <code>f = 
+ * 1 - alpha<sub><font size=-1>src</font></sub></code></li>
+ * <li>BLEND_DST_COLOR - the blend function is <code>f = 
+ * color<sub><font size=-1>dst</font></sub></code></li>
+ * <li>BLEND_ONE_MINUS_DST_COLOR - the blend function is <code>f = 
+ * 1 - color<sub><font size=-1>dst</font></sub></code></li>
+ * <li>BLEND_SRC_COLOR - the blend function is <code>f = 
+ * color<sub><font size=-1>src</font></sub></code></li>
+ * <li>BLEND_ONE_MINUS_SRC_COLOR - the blend function is <code>f = 
+ * 1 - color<sub><font size=-1>src</font></sub></code></li>
+ * </ul>
+ * </ul>
  */
 public class TransparencyAttributes extends NodeComponent {
     /**
@@ -173,6 +182,7 @@ public class TransparencyAttributes extends NodeComponent {
      * Blend function: <code>f = 0</code>.
      * @see #setSrcBlendFunction
      * @see #setDstBlendFunction
+     *
      * @since Java 3D 1.2
      */
     public static final int BLEND_ZERO = 0;
@@ -181,6 +191,7 @@ public class TransparencyAttributes extends NodeComponent {
      * Blend function: <code>f = 1</code>.
      * @see #setSrcBlendFunction
      * @see #setDstBlendFunction
+     *
      * @since Java 3D 1.2
      */
     public static final int BLEND_ONE = 1;
@@ -190,6 +201,7 @@ public class TransparencyAttributes extends NodeComponent {
      * <code>f = alpha<sub><font size=-1>src</font></sub></code>.
      * @see #setSrcBlendFunction
      * @see #setDstBlendFunction
+     *
      * @since Java 3D 1.2
      */
     public static final int BLEND_SRC_ALPHA = 2;
@@ -199,11 +211,66 @@ public class TransparencyAttributes extends NodeComponent {
      * <code>f = 1-alpha<sub><font size=-1>src</font></sub></code>.
      * @see #setSrcBlendFunction
      * @see #setDstBlendFunction
+     *
      * @since Java 3D 1.2
      */
     public static final int BLEND_ONE_MINUS_SRC_ALPHA = 3;
 
+    /**
+     * Blend function:
+     * <code>f = color<sub><font size=-1>dst</font></sub></code>.
+     * <p>Note that this function may <i>only</i> be used as a source
+     * blend function.</p>
+     * @see #setSrcBlendFunction
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int BLEND_DST_COLOR = 4;
+
+    /**
+     * Blend function:
+     * <code>f = 1-color<sub><font size=-1>dst</font></sub></code>.
+     * <p>Note that this function may <i>only</i> be used as a source
+     * blend function.</p>
+     * @see #setSrcBlendFunction
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int BLEND_ONE_MINUS_DST_COLOR = 5;
 
+    /**
+     * Blend function:
+     * <code>f = color<sub><font size=-1>src</font></sub></code>.
+     * <p>Note that this function may <i>only</i> be used as a destination
+     * blend function.</p>
+     * @see #setDstBlendFunction
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int BLEND_SRC_COLOR = 6;
+
+    /**
+     * Blend function:
+     * <code>f = 1-color<sub><font size=-1>src</font></sub></code>.
+     * <p>Note that this function may <i>only</i> be used as a destination
+     * blend function.</p>
+     * @see #setDstBlendFunction
+     *
+     * @since Java 3D 1.4
+     */
+    public static final int BLEND_ONE_MINUS_SRC_COLOR = 7;
+
+    static final int BLEND_CONSTANT_COLOR = 8;
+
+    static final int MAX_BLEND_FUNC_TABLE_SIZE = 9;
+
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_BLEND_FUNCTION_READ,
+        ALLOW_MODE_READ,
+        ALLOW_VALUE_READ
+    };
+    
     /**
      * Constructs a TransparencyAttributes object with default parameters.
      * The default values are as follows:
@@ -216,6 +283,8 @@ public class TransparencyAttributes extends NodeComponent {
      */
     public TransparencyAttributes() {
 	// Just use the default for all attributes
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
@@ -238,20 +307,20 @@ public class TransparencyAttributes extends NodeComponent {
      * @param tVal the transparency value
      * @param srcBlendFunction the blend function to be used for the source
      * color, one of <code>BLEND_ZERO</code>, <code>BLEND_ONE</code>,
-     * <code>BLEND_SRC_ALPHA</code>, or <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+     * <code>BLEND_SRC_ALPHA</code>, <code>BLEND_ONE_MINUS_SRC_ALPHA</code>,
+     * <code>BLEND_DST_COLOR</code>, or <code>BLEND_ONE_MINUS_DST_COLOR</code>.
      * @param dstBlendFunction the blend function to be used for the
      * destination
      * color, one of <code>BLEND_ZERO</code>, <code>BLEND_ONE</code>,
-     * <code>BLEND_SRC_ALPHA</code>, or <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+     * <code>BLEND_SRC_ALPHA</code>, <code>BLEND_ONE_MINUS_SRC_ALPHA</code>,
+     * <code>BLEND_SRC_COLOR</code>, or <code>BLEND_ONE_MINUS_SRC_COLOR</code>.
      * @exception IllegalArgumentException if
      * <code>tMode</code> is a value other than
      * <code>NONE</code>, <code>FASTEST</code>, <code>NICEST</code>, 
      * <code>SCREEN_DOOR</code>, or <code>BLENDED</code>
      * @exception IllegalArgumentException if
      * <code>srcBlendFunction</code> or <code>dstBlendFunction</code>
-     * is a value other than <code>BLEND_ZERO</code>, <code>BLEND_ONE</code>,
-     * <code>BLEND_SRC_ALPHA</code>, or
-     * <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+     * is a value other than one of the supported functions listed above.
      *
      * @since Java 3D 1.2
      */
@@ -263,15 +332,32 @@ public class TransparencyAttributes extends NodeComponent {
 	    throw new IllegalArgumentException(J3dI18N.getString("TransparencyAttributes6"));	    
 	}
 
-	if ((srcBlendFunction < BLEND_ZERO) || 
-	    (srcBlendFunction > BLEND_ONE_MINUS_SRC_ALPHA)) {
-	    throw new IllegalArgumentException(J3dI18N.getString("TransparencyAttributes7"));	    
-	}
-	
-	if ((dstBlendFunction < BLEND_ZERO) || 
-	    (dstBlendFunction > BLEND_ONE_MINUS_SRC_ALPHA)) {
-	    throw new IllegalArgumentException(J3dI18N.getString("TransparencyAttributes8"));	    
-	}
+        switch (srcBlendFunction) {
+        case BLEND_ZERO:
+        case BLEND_ONE:
+        case BLEND_SRC_ALPHA:
+        case BLEND_ONE_MINUS_SRC_ALPHA:
+        case BLEND_DST_COLOR:
+        case BLEND_ONE_MINUS_DST_COLOR:
+            break;
+            default:
+            throw new IllegalArgumentException(J3dI18N.getString("TransparencyAttributes7"));
+        }
+
+        switch (dstBlendFunction) {
+        case BLEND_ZERO:
+        case BLEND_ONE:
+        case BLEND_SRC_ALPHA:
+        case BLEND_ONE_MINUS_SRC_ALPHA:
+        case BLEND_SRC_COLOR:
+        case BLEND_ONE_MINUS_SRC_COLOR:
+            break;
+            default:
+            throw new IllegalArgumentException(J3dI18N.getString("TransparencyAttributes8"));
+        }
+
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
 
         ((TransparencyAttributesRetained)this.retained).initTransparencyMode(tMode);
         ((TransparencyAttributesRetained)this.retained).initTransparency(tVal);
@@ -370,15 +456,13 @@ public class TransparencyAttributes extends NodeComponent {
      *
      * @param blendFunction the blend function to be used for the source
      * color, one of <code>BLEND_ZERO</code>, <code>BLEND_ONE</code>,
-     * <code>BLEND_SRC_ALPHA</code>, or <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+     * <code>BLEND_SRC_ALPHA</code>, <code>BLEND_ONE_MINUS_SRC_ALPHA</code>,
+     * <code>BLEND_DST_COLOR</code>, or <code>BLEND_ONE_MINUS_DST_COLOR</code>.
+     *
      * @exception CapabilityNotSetException if appropriate capability is 
      * not set and this object is part of live or compiled scene graph
-     * @exception IllegalArgumentException if
-     * <code>blendFunction</code>
-     * is a value other than <code>BLEND_ZERO</code>, 
-     * <code>BLEND_ONE</code>,
-     * <code>BLEND_SRC_ALPHA</code>, or
-     * <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+     * @exception IllegalArgumentException if <code>blendFunction</code>
+     * is a value other than one of the supported functions listed above.
      *
      * @since Java 3D 1.2
      */
@@ -388,11 +472,17 @@ public class TransparencyAttributes extends NodeComponent {
 		throw new
 		    CapabilityNotSetException(J3dI18N.getString("TransparencyAttributes4"));
 
-	if ((blendFunction < BLEND_ZERO) || 
-	    (blendFunction > BLEND_ONE_MINUS_SRC_ALPHA)) {
-	    throw new IllegalArgumentException(J3dI18N.getString("TransparencyAttributes7"));	    
-	}
-
+        switch (blendFunction) {
+        case BLEND_ZERO:
+        case BLEND_ONE:
+        case BLEND_SRC_ALPHA:
+        case BLEND_ONE_MINUS_SRC_ALPHA:
+        case BLEND_DST_COLOR:
+        case BLEND_ONE_MINUS_DST_COLOR:
+            break;
+            default:
+            throw new IllegalArgumentException(J3dI18N.getString("TransparencyAttributes7"));
+        }
 
 	if (isLive()) 
 	    ((TransparencyAttributesRetained)this.retained).setSrcBlendFunction(blendFunction);
@@ -428,15 +518,13 @@ public class TransparencyAttributes extends NodeComponent {
      *
      * @param blendFunction the blend function to be used for the destination
      * color, one of <code>BLEND_ZERO</code>, <code>BLEND_ONE</code>,
-     * <code>BLEND_SRC_ALPHA</code>, or <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+     * <code>BLEND_SRC_ALPHA</code>, <code>BLEND_ONE_MINUS_SRC_ALPHA</code>,
+     * <code>BLEND_SRC_COLOR</code>, or <code>BLEND_ONE_MINUS_SRC_COLOR</code>.
+     *
      * @exception CapabilityNotSetException if appropriate capability is 
      * not set and this object is part of live or compiled scene graph
-     * @exception IllegalArgumentException if
-     * <code>blendFunction</code>
-     * is a value other than <code>BLEND_ZERO</code>, 
-     * <code>BLEND_ONE</code>,
-     * <code>BLEND_SRC_ALPHA</code>, or
-     * <code>BLEND_ONE_MINUS_SRC_ALPHA</code>.
+     * @exception IllegalArgumentException if <code>blendFunction</code>
+     * is a value other than one of the supported functions listed above.
      *
      * @since Java 3D 1.2
      */
@@ -445,10 +533,17 @@ public class TransparencyAttributes extends NodeComponent {
 	    if (!this.getCapability(ALLOW_BLEND_FUNCTION_WRITE))
 		throw new CapabilityNotSetException(J3dI18N.getString("TransparencyAttributes4"));
 
-	if ((blendFunction < BLEND_ZERO) || 
-	    (blendFunction > BLEND_ONE_MINUS_SRC_ALPHA)) {
-	    throw new IllegalArgumentException(J3dI18N.getString("TransparencyAttributes8"));	    
-	}
+        switch (blendFunction) {
+        case BLEND_ZERO:
+        case BLEND_ONE:
+        case BLEND_SRC_ALPHA:
+        case BLEND_ONE_MINUS_SRC_ALPHA:
+        case BLEND_SRC_COLOR:
+        case BLEND_ONE_MINUS_SRC_COLOR:
+            break;
+            default:
+            throw new IllegalArgumentException(J3dI18N.getString("TransparencyAttributes8"));
+        }
 
 	if (isLive()) 
 	    ((TransparencyAttributesRetained)this.retained).setDstBlendFunction(blendFunction);
diff --git a/src/classes/share/javax/media/j3d/TransparencyAttributesRetained.java b/src/classes/share/javax/media/j3d/TransparencyAttributesRetained.java
index 0123fc2..defbb44 100644
--- a/src/classes/share/javax/media/j3d/TransparencyAttributesRetained.java
+++ b/src/classes/share/javax/media/j3d/TransparencyAttributesRetained.java
@@ -38,16 +38,6 @@ class TransparencyAttributesRetained extends NodeComponentRetained {
     int srcBlendFunction = TransparencyAttributes.BLEND_SRC_ALPHA;
     int dstBlendFunction = TransparencyAttributes.BLEND_ONE_MINUS_SRC_ALPHA;
 
-    // Here are some blend functions that are used in multi-pass only
-    static final int BLEND_ZERO                = 0;
-    static final int BLEND_ONE                 = 1;
-    static final int BLEND_SRC_ALPHA           = 2;
-    static final int BLEND_ONE_MINUS_SRC_ALPHA = 3;
-    static final int BLEND_DST_COLOR           = 4;
-    static final int BLEND_SRC_COLOR           = 5;
-    static final int BLEND_ONE_MINUS_SRC_COLOR = 6;
-    static final int BLEND_CONSTANT_COLOR      = 7;
-
     /**
      * Sets the transparency mode for this
      * appearance component object.
diff --git a/src/classes/share/javax/media/j3d/TransparentRenderingInfo.java b/src/classes/share/javax/media/j3d/TransparentRenderingInfo.java
index bca1018..27c4e7d 100644
--- a/src/classes/share/javax/media/j3d/TransparentRenderingInfo.java
+++ b/src/classes/share/javax/media/j3d/TransparentRenderingInfo.java
@@ -11,10 +11,9 @@
  */
 
 package javax.media.j3d;
-import javax.vecmath.*;
-import java.util.*;
 
-class TransparentRenderingInfo extends Object {
+
+class TransparentRenderingInfo extends Object implements com.sun.j3d.utils.scenegraph.transparency.TransparencySortGeom {
     // For DepthSortedTransparency, rm is the rendermolecule
     // that this rInfo is part of
     // For non depth sorted transparency, rm is one of the rendermolecules
@@ -24,8 +23,9 @@ class TransparentRenderingInfo extends Object {
     RenderAtomListInfo rInfo;
     TransparentRenderingInfo prev;
     TransparentRenderingInfo next;
+    GeometryAtom geometryAtom;
     double zVal; // Used in DepthSorted Transparency
-    // TODO: Add Dirty info
+    // XXXX: Add Dirty info
 
     /**
      * update state before rendering transparent objects 
@@ -34,7 +34,8 @@ class TransparentRenderingInfo extends Object {
 
 	TextureBin textureBin = rm.textureBin;
 	AttributeBin attributeBin = textureBin.attributeBin;
-
+        ShaderBin shaderBin = textureBin.shaderBin;
+        
 	// Get a collection to check if switch is on
 
 	RenderMolecule rm = textureBin.transparentRMList ;
@@ -57,40 +58,44 @@ class TransparentRenderingInfo extends Object {
 	    return false;
 	}
 
-	if (cv.environmentSet != attributeBin.environmentSet) {
-		
-	    boolean visible = (attributeBin.definingRenderingAttributes == null ||
-				   attributeBin.definingRenderingAttributes.visible);
+        // XXXX : Code cleanup needed : The following code segment should simply test
+        //        each bin independently and update it if necessary.
+        if (cv.environmentSet != attributeBin.environmentSet) {
+            
+            boolean visible = (attributeBin.definingRenderingAttributes == null ||
+                    attributeBin.definingRenderingAttributes.visible);
+            
+            if ( (attributeBin.environmentSet.renderBin.view.viewCache.visibilityPolicy
+                    == View.VISIBILITY_DRAW_VISIBLE && !visible) ||
+                    (attributeBin.environmentSet.renderBin.view.viewCache.visibilityPolicy
+                    == View.VISIBILITY_DRAW_INVISIBLE && visible)) {
+                return false;
+            }
+            attributeBin.environmentSet.lightBin.updateAttributes(cv);
+            attributeBin.environmentSet.updateAttributes(cv);
+            attributeBin.updateAttributes(cv);
+            shaderBin.updateTransparentAttributes(cv);
+        } else if (cv.attributeBin != attributeBin) {
+            boolean visible = (attributeBin.definingRenderingAttributes == null ||
+                    attributeBin.definingRenderingAttributes.visible);
+            
+            if ( (attributeBin.environmentSet.renderBin.view.viewCache.visibilityPolicy
+                    == View.VISIBILITY_DRAW_VISIBLE && !visible) ||
+                    (attributeBin.environmentSet.renderBin.view.viewCache.visibilityPolicy
+                    == View.VISIBILITY_DRAW_INVISIBLE && visible)) {
+                return false;
+            }
+            attributeBin.updateAttributes(cv);
+            shaderBin.updateTransparentAttributes(cv);
+        } else if (cv.shaderBin != shaderBin) {
+            shaderBin.updateTransparentAttributes(cv);
+        } 
 
-	    if ( (attributeBin.environmentSet.renderBin.view.viewCache.visibilityPolicy
-		  == View.VISIBILITY_DRAW_VISIBLE && !visible) ||
-		 (attributeBin.environmentSet.renderBin.view.viewCache.visibilityPolicy
-		  == View.VISIBILITY_DRAW_INVISIBLE && visible)) {
-		return false;
-	    }
-	    attributeBin.environmentSet.lightBin.updateAttributes(cv);
-	    attributeBin.environmentSet.updateAttributes(cv);
-	    attributeBin.updateAttributes(cv);
-	}
-	else {
-	    if (cv.attributeBin != attributeBin) {
-		boolean visible = (attributeBin.definingRenderingAttributes == null ||
-				   attributeBin.definingRenderingAttributes.visible);
-
-		if ( (attributeBin.environmentSet.renderBin.view.viewCache.visibilityPolicy
-		      == View.VISIBILITY_DRAW_VISIBLE && !visible) ||
-		     (attributeBin.environmentSet.renderBin.view.viewCache.visibilityPolicy
-		      == View.VISIBILITY_DRAW_INVISIBLE && visible)) {
-		    return false;
-		}
-		attributeBin.updateAttributes(cv);
-	    } 
-	}
-	return true;
+        return true;
     }
 
     void render(Canvas3D cv) {
-	if (updateState(cv)) {
+	if (updateState(cv)) {      
 	    rm.textureBin.render(cv, rm.textureBin.transparentRMList);
 	}
     }
@@ -101,4 +106,25 @@ class TransparentRenderingInfo extends Object {
 	    rm.textureBin.render(cv, this);
 	}
     }
+
+    public double getDistanceSquared() {
+        return zVal;
+    }
+
+    public Geometry getGeometry() {
+        // XXXX: verify 0 is always the correct index. Assumption is that for 
+        // Shape3D with multiple geometry each geometry is put in it's 
+        // own geometryAtom.
+        if (geometryAtom.geometryArray[0]==null)
+            return null;
+        return (Geometry)geometryAtom.geometryArray[0].source;
+    }
+
+    public void getLocalToVWorld(Transform3D localToVW) {
+        localToVW.set(rm.localToVworld[NodeRetained.LAST_LOCAL_TO_VWORLD]);
+    }
+
+    public Shape3D getShape3D() {
+        return (Shape3D)geometryAtom.source.source;
+    }
 }
diff --git a/src/classes/share/javax/media/j3d/TriangleArray.java b/src/classes/share/javax/media/j3d/TriangleArray.java
index 32bb57b..8487007 100644
--- a/src/classes/share/javax/media/j3d/TriangleArray.java
+++ b/src/classes/share/javax/media/j3d/TriangleArray.java
@@ -24,23 +24,22 @@ public class TriangleArray extends GeometryArray {
     TriangleArray() {}
 
     /**
-     * Constructs an empty TriangleArray object with the specified
-     * number of vertices, and vertex format.
-     * @param vertexCount the number of vertex elements in this array
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
+     * Constructs an empty TriangleArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
      * @exception IllegalArgumentException if vertexCount is less than 3
      * or vertexCount is <i>not</i> a multiple of 3
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int)}
+     * for more exceptions that can be thrown
      */
     public TriangleArray(int vertexCount, int vertexFormat) {
 	super(vertexCount,vertexFormat);
@@ -50,60 +49,30 @@ public class TriangleArray extends GeometryArray {
     }
 
     /**
-     * Constructs an empty TriangleArray object with the specified
-     * number of vertices, and vertex format, number of texture coordinate
-     * sets, and texture coordinate mapping array.
-     *
-     * @param vertexCount the number of vertex elements in this array<p>
-     *
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
-     *
-     * @param texCoordSetCount the number of texture coordinate sets
-     * in this GeometryArray object.  If <code>vertexFormat</code>
-     * does not include one of <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetCount</code> parameter is not used.<p>
-     *
-     * @param texCoordSetMap an array that maps texture coordinate
-     * sets to texture units.  The array is indexed by texture unit
-     * number for each texture unit in the associated Appearance
-     * object.  The values in the array specify the texture coordinate
-     * set within this GeometryArray object that maps to the
-     * corresponding texture
-     * unit.  All elements within the array must be less than
-     * <code>texCoordSetCount</code>.  A negative value specifies that
-     * no texture coordinate set maps to the texture unit
-     * corresponding to the index.  If there are more texture units in
-     * any associated Appearance object than elements in the mapping
-     * array, the extra elements are assumed to be -1.  The same
-     * texture coordinate set may be used for more than one texture
-     * unit.  Each texture unit in every associated Appearance must
-     * have a valid source of texture coordinates: either a
-     * non-negative texture coordinate set must be specified in the
-     * mapping array or texture coordinate generation must be enabled.
-     * Texture coordinate generation will take precedence for those
-     * texture units for which a texture coordinate set is specified
-     * and texture coordinate generation is enabled.  If
-     * <code>vertexFormat</code> does not include one of
-     * <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetMap</code> array is not used.
+     * Constructs an empty TriangleArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
      *
      * @exception IllegalArgumentException if vertexCount is less than 3
      * or vertexCount is <i>not</i> a multiple of 3
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for more exceptions that can be thrown
      *
      * @since Java 3D 1.2
      */
@@ -119,6 +88,54 @@ public class TriangleArray extends GeometryArray {
 	    throw new IllegalArgumentException(J3dI18N.getString("TriangleArray0"));
     }
 
+    /**
+     * Constructs an empty TriangleArray object using the specified
+     * parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrSizes
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @exception IllegalArgumentException if vertexCount is less than 3
+     * or vertexCount is <i>not</i> a multiple of 3
+     * ;<br>
+     * See {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for more exceptions that can be thrown
+     *
+     * @since Java 3D 1.4
+     */
+    public TriangleArray(int vertexCount,
+			 int vertexFormat,
+			 int texCoordSetCount,
+			 int[] texCoordSetMap,
+			 int vertexAttrCount,
+			 int[] vertexAttrSizes) {
+
+	super(vertexCount, vertexFormat,
+	      texCoordSetCount, texCoordSetMap,
+	      vertexAttrCount, vertexAttrSizes);
+
+        if (vertexCount < 3 || ((vertexCount%3) != 0))
+	    throw new IllegalArgumentException(J3dI18N.getString("TriangleArray0"));
+    }
+
+
     /**
      * Creates the retained mode TriangleArrayRetained object that this
      * TriangleArray object will point to.
@@ -133,22 +150,26 @@ public class TriangleArray extends GeometryArray {
      * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
      */
     public NodeComponent cloneNodeComponent() {
-	TriangleArrayRetained rt = (TriangleArrayRetained) retained;
-	int texSetCount = rt.getTexCoordSetCount();
-	TriangleArray t;
-
-	if (texSetCount == 0) {
-	    t = new TriangleArray(rt.getVertexCount(), 
-				  rt.getVertexFormat());
-	} else {
-	    int texMap[] = new int[rt.getTexCoordSetMapLength()];
-	    rt.getTexCoordSetMap(texMap);
-	    t = new TriangleArray(rt.getVertexCount(), 
-				  rt.getVertexFormat(),
-				  texSetCount,
-				  texMap);
-	}
-	t.duplicateNodeComponent(this);
+        TriangleArrayRetained rt = (TriangleArrayRetained) retained;
+        int texSetCount = rt.getTexCoordSetCount();
+        int[] texMap = null;
+        int vertexAttrCount = rt.getVertexAttrCount();
+        int[] vertexAttrSizes = null;
+        if (texSetCount > 0) {
+            texMap = new int[rt.getTexCoordSetMapLength()];
+            rt.getTexCoordSetMap(texMap);
+        }
+        if (vertexAttrCount > 0) {
+            vertexAttrSizes = new int[vertexAttrCount];
+            rt.getVertexAttrSizes(vertexAttrSizes);
+        }
+        TriangleArray t = new TriangleArray(rt.getVertexCount(),
+                rt.getVertexFormat(),
+                texSetCount,
+                texMap,
+                vertexAttrCount,
+                vertexAttrSizes);
+        t.duplicateNodeComponent(this);
         return t;
      }
 }
diff --git a/src/classes/share/javax/media/j3d/TriangleArrayRetained.java b/src/classes/share/javax/media/j3d/TriangleArrayRetained.java
index 6332bad..577495f 100644
--- a/src/classes/share/javax/media/j3d/TriangleArrayRetained.java
+++ b/src/classes/share/javax/media/j3d/TriangleArrayRetained.java
@@ -27,14 +27,15 @@ class TriangleArrayRetained extends GeometryArrayRetained {
 	this.geoType = GEO_TYPE_TRI_SET;
     }
 
-    boolean intersect(PickShape pickShape, double dist[],  Point3d iPnt) {
+    boolean intersect(PickShape pickShape, PickInfo.IntersectionInfo iInfo,  int flags, Point3d iPnt) {
 	Point3d pnts[] = new Point3d[3];
 	double sdist[] = new double[1];
 	double minDist = Double.MAX_VALUE;
 	double x = 0, y = 0, z = 0;
 	int i = ((vertexFormat & GeometryArray.BY_REFERENCE) == 0 ?
 		 initialVertexIndex : initialCoordIndex);
-
+        int count = 0;
+        int minICount = 0; 
 	pnts[0] = new Point3d();
 	pnts[1] = new Point3d();
 	pnts[2] = new Point3d();
@@ -47,12 +48,14 @@ class TriangleArrayRetained extends GeometryArrayRetained {
 		getVertexData(i++, pnts[0]);
 		getVertexData(i++, pnts[1]);
 		getVertexData(i++, pnts[2]);
+                count += 3;                
 		if (intersectRay(pnts, pickRay, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
-			minDist = sdist[0];
+			minDist = sdist[0];                        
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -62,22 +65,24 @@ class TriangleArrayRetained extends GeometryArrayRetained {
 	    break;
 	case PickShape.PICKSEGMENT:
 	    PickSegment pickSegment = (PickSegment) pickShape;
-	    while (i < validVertexCount) {
+
+            while (i < validVertexCount) {
 		getVertexData(i++, pnts[0]);
 		getVertexData(i++, pnts[1]);
 		getVertexData(i++, pnts[2]);
+                count += 3;
 		if (intersectSegment(pnts, pickSegment.start,
-				     pickSegment.end, sdist, iPnt)
-		    && (sdist[0] <= 1.0)) {
-		    if (dist == null) {
+				     pickSegment.end, sdist, iPnt)) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
-			}
+                    }
 		}
 	    }
 	    break;
@@ -89,15 +94,17 @@ class TriangleArrayRetained extends GeometryArrayRetained {
 		getVertexData(i++, pnts[0]);
 		getVertexData(i++, pnts[1]);
 		getVertexData(i++, pnts[2]);
+                count += 3;                
 		if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
-			    z = iPnt.z;
+			z = iPnt.z;
 		    }
 		}
 	    }
@@ -110,12 +117,14 @@ class TriangleArrayRetained extends GeometryArrayRetained {
 		getVertexData(i++, pnts[0]);
 		getVertexData(i++, pnts[1]);
 		getVertexData(i++, pnts[2]);
+                count += 3;
 		if (intersectBoundingSphere(pnts, bsphere, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -131,13 +140,15 @@ class TriangleArrayRetained extends GeometryArrayRetained {
 		getVertexData(i++, pnts[0]);
 		getVertexData(i++, pnts[1]);
 		getVertexData(i++, pnts[2]);
+                count += 3;
 		if (intersectBoundingPolytope(pnts, bpolytope,
 					      sdist,iPnt)) { 
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -151,13 +162,15 @@ class TriangleArrayRetained extends GeometryArrayRetained {
 		getVertexData(i++, pnts[0]);
 		getVertexData(i++, pnts[1]);
 		getVertexData(i++, pnts[2]);
+                count += 3;
 		if (intersectCylinder(pnts, pickCylinder, sdist,
 				      iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -172,12 +185,14 @@ class TriangleArrayRetained extends GeometryArrayRetained {
 		getVertexData(i++, pnts[0]);
 		getVertexData(i++, pnts[1]);
 		getVertexData(i++, pnts[2]);
+                count += 3;
 		if (intersectCone(pnts, pickCone, sdist, iPnt)) {
-		    if (dist == null) {
+		    if (flags == 0) {
 			return true;
 		    }
 		    if (sdist[0] < minDist) {
 			minDist = sdist[0];
+                        minICount = count;
 			x = iPnt.x;
 			y = iPnt.y;
 			z = iPnt.z;
@@ -194,7 +209,15 @@ class TriangleArrayRetained extends GeometryArrayRetained {
 
     
 	if (minDist < Double.MAX_VALUE) {
-	    dist[0] = minDist;
+            assert(minICount >=3);
+            int[] vertexIndices = iInfo.getVertexIndices();
+            if (vertexIndices == null) {
+                vertexIndices = new int[3];
+                iInfo.setVertexIndices(vertexIndices);
+            }
+            vertexIndices[0] = minICount - 3;
+            vertexIndices[1] = minICount - 2;
+            vertexIndices[2] = minICount - 1;
 	    iPnt.x = x;
 	    iPnt.y = y;
 	    iPnt.z = z;
@@ -263,8 +286,7 @@ class TriangleArrayRetained extends GeometryArrayRetained {
 	    break;
 	}
 	return false;
-    }
-    
+    }    
   
     boolean intersect(Transform3D thisToOtherVworld,
 		      GeometryRetained geom) {
diff --git a/src/classes/share/javax/media/j3d/TriangleFanArray.java b/src/classes/share/javax/media/j3d/TriangleFanArray.java
index d8a9539..e536158 100644
--- a/src/classes/share/javax/media/j3d/TriangleFanArray.java
+++ b/src/classes/share/javax/media/j3d/TriangleFanArray.java
@@ -30,28 +30,26 @@ public class TriangleFanArray extends GeometryStripArray {
     TriangleFanArray() {}
 
     /**
-     * Constructs an empty TriangleFanArray object with the specified
-     * number of vertices, vertex format, and
-     * array of per-strip vertex counts.
-     * @param vertexCount the number of vertex elements in this array
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
-     * @param stripVertexCounts array that specifies
-     * the count of the number of vertices for each separate strip.
-     * The length of this array is the number of separate strips.
+     * Constructs an empty TriangleFanArray object using the
+     * specified parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param stripVertexCounts
+     * see {@link GeometryStripArray#GeometryStripArray(int,int,int[])}
+     * for a description of this parameter.
      *
      * @exception IllegalArgumentException if vertexCount is less than 3
      * or any element in the stripVertexCounts array is less than 3
+     * ;<br>
+     * See {@link GeometryStripArray#GeometryStripArray(int,int,int[])}
+     * for more exceptions that can be thrown
      */
     public TriangleFanArray(int vertexCount,
 			    int vertexFormat,
@@ -64,65 +62,34 @@ public class TriangleFanArray extends GeometryStripArray {
     }
 
     /**
-     * Constructs an empty TriangleFanArray object with the specified
-     * number of vertices, vertex format, number of texture coordinate
-     * sets, texture coordinate mapping array, and
-     * array of per-strip vertex counts.
-     *
-     * @param vertexCount the number of vertex elements in this array<p>
-     *
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
-     *
-     * @param texCoordSetCount the number of texture coordinate sets
-     * in this GeometryArray object.  If <code>vertexFormat</code>
-     * does not include one of <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetCount</code> parameter is not used.<p>
-     *
-     * @param texCoordSetMap an array that maps texture coordinate
-     * sets to texture units.  The array is indexed by texture unit
-     * number for each texture unit in the associated Appearance
-     * object.  The values in the array specify the texture coordinate
-     * set within this GeometryArray object that maps to the
-     * corresponding texture
-     * unit.  All elements within the array must be less than
-     * <code>texCoordSetCount</code>.  A negative value specifies that
-     * no texture coordinate set maps to the texture unit
-     * corresponding to the index.  If there are more texture units in
-     * any associated Appearance object than elements in the mapping
-     * array, the extra elements are assumed to be -1.  The same
-     * texture coordinate set may be used for more than one texture
-     * unit.  Each texture unit in every associated Appearance must
-     * have a valid source of texture coordinates: either a
-     * non-negative texture coordinate set must be specified in the
-     * mapping array or texture coordinate generation must be enabled.
-     * Texture coordinate generation will take precedence for those
-     * texture units for which a texture coordinate set is specified
-     * and texture coordinate generation is enabled.  If
-     * <code>vertexFormat</code> does not include one of
-     * <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetMap</code> array is not used.<p>
-     *
-     * @param stripVertexCounts array that specifies
-     * the count of the number of vertices for each separate strip.
-     * The length of this array is the number of separate strips.
+     * Constructs an empty TriangleFanArray object using the
+     * specified parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param stripVertexCounts
+     * see {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int[])}
+     * for a description of this parameter.
      *
      * @exception IllegalArgumentException if vertexCount is less than 3
      * or any element in the stripVertexCounts array is less than 3
+     * ;<br>
+     * See {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int[])}
+     * for more exceptions that can be thrown
      *
      * @since Java 3D 1.2
      */
@@ -140,6 +107,59 @@ public class TriangleFanArray extends GeometryStripArray {
         throw new IllegalArgumentException(J3dI18N.getString("TriangleFanArray0"));
     }
 
+    /**
+     * Constructs an empty TriangleFanArray object using the
+     * specified parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrSizes
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param stripVertexCounts
+     * see {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int,int[],int[])}
+     * for a description of this parameter.
+     *
+     * @exception IllegalArgumentException if vertexCount is less than 3
+     * or any element in the stripVertexCounts array is less than 3
+     * ;<br>
+     * See {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int,int[],int[])}
+     * for more exceptions that can be thrown
+     *
+     * @since Java 3D 1.4
+     */
+    public TriangleFanArray(int vertexCount,
+			    int vertexFormat,
+			    int texCoordSetCount,
+			    int[] texCoordSetMap,
+			    int vertexAttrCount,
+			    int[] vertexAttrSizes,
+			    int[] stripVertexCounts) {
+
+	super(vertexCount, vertexFormat,
+	      texCoordSetCount, texCoordSetMap,
+	      vertexAttrCount, vertexAttrSizes,
+	      stripVertexCounts);
+
+        if (vertexCount < 3 )
+	    throw new IllegalArgumentException(J3dI18N.getString("TriangleFanArray0"));
+    }
+
     /**
      * Creates the retained mode TriangleFanArrayRetained object that this
      * TriangleFanArray object will point to.
@@ -154,25 +174,29 @@ public class TriangleFanArray extends GeometryStripArray {
      * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
      */
     public NodeComponent cloneNodeComponent() {
-	TriangleFanArrayRetained rt = (TriangleFanArrayRetained) retained;
-	int stripcounts[] = new int[rt.getNumStrips()];
-	rt.getStripVertexCounts(stripcounts);
-	int texSetCount = rt.getTexCoordSetCount();
-        TriangleFanArray t;
-	if (texSetCount == 0) {
-	    t = new TriangleFanArray(rt.getVertexCount(), 
-				     rt.getVertexFormat(),
-				     stripcounts);
-	} else {
-	    int texMap[] = new int[rt.getTexCoordSetMapLength()];
-	    rt.getTexCoordSetMap(texMap);	    
-	    t = new TriangleFanArray(rt.getVertexCount(), 
-				     rt.getVertexFormat(),
-				     texSetCount,
-				     texMap,
-				     stripcounts);
-	}
-	t.duplicateNodeComponent(this);
+        TriangleFanArrayRetained rt = (TriangleFanArrayRetained) retained;
+        int stripcounts[] = new int[rt.getNumStrips()];
+        rt.getStripVertexCounts(stripcounts);
+        int texSetCount = rt.getTexCoordSetCount();
+        int[] texMap = null;
+        int vertexAttrCount = rt.getVertexAttrCount();
+        int[] vertexAttrSizes = null;
+        if (texSetCount > 0) {
+            texMap = new int[rt.getTexCoordSetMapLength()];
+            rt.getTexCoordSetMap(texMap);
+        }
+        if (vertexAttrCount > 0) {
+            vertexAttrSizes = new int[vertexAttrCount];
+            rt.getVertexAttrSizes(vertexAttrSizes);
+        }
+        TriangleFanArray t = new TriangleFanArray(rt.getVertexCount(),
+                rt.getVertexFormat(),
+                texSetCount,
+                texMap,
+                vertexAttrCount,
+                vertexAttrSizes,
+                stripcounts);
+        t.duplicateNodeComponent(this);
         return t;
      }
 
diff --git a/src/classes/share/javax/media/j3d/TriangleFanArrayRetained.java b/src/classes/share/javax/media/j3d/TriangleFanArrayRetained.java
index ba29127..1c77d6c 100644
--- a/src/classes/share/javax/media/j3d/TriangleFanArrayRetained.java
+++ b/src/classes/share/javax/media/j3d/TriangleFanArrayRetained.java
@@ -32,14 +32,16 @@ class TriangleFanArrayRetained extends GeometryStripArrayRetained {
 	this.geoType = GEO_TYPE_TRI_FAN_SET;
     }
 
-    boolean intersect(PickShape pickShape, double dist[],  Point3d iPnt) {
+    boolean intersect(PickShape pickShape, PickInfo.IntersectionInfo iInfo,  int flags, Point3d iPnt) {
 	Point3d pnts[] = new Point3d[3];
 	double sdist[] = new double[1];
 	double minDist = Double.MAX_VALUE;
 	double x = 0, y = 0, z = 0;
 	int i = 0;
 	int j, end;
-
+        int count = 0, fSCount;
+        int minICount = 0; 
+        int minFSCount = 0;
 	pnts[0] = new Point3d();
 	pnts[1] = new Point3d();
 	pnts[2] = new Point3d();
@@ -51,19 +53,24 @@ class TriangleFanArrayRetained extends GeometryStripArrayRetained {
 	    while (i < stripVertexCounts.length) {  
 		j = stripStartVertexIndices[i];
 		end = j + stripVertexCounts[i++];
+                fSCount = count;
 		getVertexData(j++, pnts[0]);
 		getVertexData(j++, pnts[1]);
+                count += 2;
 		while (j < end) {
 		    getVertexData(j++, pnts[2]);
-		    if (intersectRay(pnts, pickRay, sdist, iPnt)) {
-			if (dist == null) {
+                    count++;
+                    if (intersectRay(pnts, pickRay, sdist, iPnt)) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minFSCount = count;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[1].set(pnts[2]);
@@ -76,20 +83,25 @@ class TriangleFanArrayRetained extends GeometryStripArrayRetained {
 	    while (i < stripVertexCounts.length) {  
 		j = stripStartVertexIndices[i];
 		end = j + stripVertexCounts[i++];
+                fSCount = count;
 		getVertexData(j++, pnts[0]);
 		getVertexData(j++, pnts[1]);
+                count += 2;
 		while (j < end) {
 		    getVertexData(j++, pnts[2]);
-		    if (intersectSegment(pnts, pickSegment.start, 
+                    count++;
+                    if (intersectSegment(pnts, pickSegment.start, 
 					 pickSegment.end, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minFSCount = fSCount;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[1].set(pnts[2]);
@@ -103,19 +115,24 @@ class TriangleFanArrayRetained extends GeometryStripArrayRetained {
 	    while (i < stripVertexCounts.length) {  
 		j = stripStartVertexIndices[i];
 		end = j + stripVertexCounts[i++];
+                fSCount = count;
 		getVertexData(j++, pnts[0]);
 		getVertexData(j++, pnts[1]);
+                count += 2;
 		while (j < end) {
 		    getVertexData(j++, pnts[2]);
+                    count++;
 		    if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minFSCount = fSCount;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[1].set(pnts[2]);
@@ -129,20 +146,25 @@ class TriangleFanArrayRetained extends GeometryStripArrayRetained {
 	    while (i < stripVertexCounts.length) {  
 		j = stripStartVertexIndices[i];
 		end = j + stripVertexCounts[i++];
-		getVertexData(j++, pnts[0]);
+                fSCount = count;
+                getVertexData(j++, pnts[0]);
 		getVertexData(j++, pnts[1]);
-		while (j < end) {
+                count += 2;
+                while (j < end) {
 		    getVertexData(j++, pnts[2]);
-		    if (intersectBoundingSphere(pnts, bsphere, sdist,
+                    count++;
+                    if (intersectBoundingSphere(pnts, bsphere, sdist,
 						iPnt)) { 
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minFSCount = fSCount;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[1].set(pnts[2]);
@@ -156,20 +178,25 @@ class TriangleFanArrayRetained extends GeometryStripArrayRetained {
 	    while (i < stripVertexCounts.length) {  
 		j = stripStartVertexIndices[i];
 		end = j + stripVertexCounts[i++];
+                fSCount = count;
 		getVertexData(j++, pnts[0]);
 		getVertexData(j++, pnts[1]);
+                count += 2;
 		while (j < end) {
 		    getVertexData(j++, pnts[2]);
-		    if (intersectBoundingPolytope(pnts, bpolytope,
+                    count++;
+                    if (intersectBoundingPolytope(pnts, bpolytope,
 						  sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minFSCount = fSCount;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[1].set(pnts[2]);
@@ -184,17 +211,22 @@ class TriangleFanArrayRetained extends GeometryStripArrayRetained {
 		end = j + stripVertexCounts[i++];
 		getVertexData(j++, pnts[0]);
 		getVertexData(j++, pnts[1]);
+                fSCount = count;
+                count += 2;
 		while (j < end) {
 		    getVertexData(j++, pnts[2]);
+                    count++;
 		    if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minFSCount = fSCount;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[1].set(pnts[2]);
@@ -207,19 +239,24 @@ class TriangleFanArrayRetained extends GeometryStripArrayRetained {
 	    while (i < stripVertexCounts.length) {  
 		j = stripStartVertexIndices[i];
 		end = j + stripVertexCounts[i++];
-		getVertexData(j++, pnts[0]);
+                fSCount = count;
+                getVertexData(j++, pnts[0]);
 		getVertexData(j++, pnts[1]);
+                count += 2;
 		while (j < end) {
 		    getVertexData(j++, pnts[2]);
+                    count++;
 		    if (intersectCone(pnts, pickCone, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
-			    x = iPnt.x;
-			    y = iPnt.y;
-			    z = iPnt.z;
+                            minFSCount = fSCount;
+                            minICount = count;
+                            x = iPnt.x;
+                            y = iPnt.y;
+                            z = iPnt.z;
 			}
 		    }
 		    pnts[1].set(pnts[2]);
@@ -234,14 +271,22 @@ class TriangleFanArrayRetained extends GeometryStripArrayRetained {
 	} 
 
 	if (minDist < Double.MAX_VALUE) {
-	    dist[0] = minDist;
+            assert(minICount >= 3);
+            int[] vertexIndices = iInfo.getVertexIndices();
+            if (vertexIndices == null) {
+                vertexIndices = new int[3];
+                iInfo.setVertexIndices(vertexIndices);
+            }
+            vertexIndices[0] = minFSCount;
+            vertexIndices[1] = minICount - 2;
+            vertexIndices[2] = minICount - 1;
 	    iPnt.x = x;
 	    iPnt.y = y;
 	    iPnt.z = z;
-	    return true;
+            return true;
 	}
 	return false;
-    }
+    }    
  
     // intersect pnts[] with every triangle in this object
     boolean intersect(Point3d[] pnts) {
diff --git a/src/classes/share/javax/media/j3d/TriangleStripArray.java b/src/classes/share/javax/media/j3d/TriangleStripArray.java
index 9c6c324..e6c9ed8 100644
--- a/src/classes/share/javax/media/j3d/TriangleStripArray.java
+++ b/src/classes/share/javax/media/j3d/TriangleStripArray.java
@@ -29,28 +29,26 @@ public class TriangleStripArray extends GeometryStripArray {
     TriangleStripArray() {}
 
     /**
-     * Constructs an empty TriangleStripArray object with the specified
-     * number of vertices, vertex format, and
-     * array of per-strip vertex counts.
-     * @param vertexCount the number of vertex elements in this array
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.
-     * @param stripVertexCounts array that specifies
-     * the count of the number of vertices for each separate strip.
-     * The length of this array is the number of separate strips.
+     * Constructs an empty TriangleStripArray object using the
+     * specified parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int)}
+     * for a description of this parameter.
+     *
+     * @param stripVertexCounts
+     * see {@link GeometryStripArray#GeometryStripArray(int,int,int[])}
+     * for a description of this parameter.
      *
      * @exception IllegalArgumentException if vertexCount is less than 3
      * or any element in the stripVertexCounts array is less than 3
+     * ;<br>
+     * See {@link GeometryStripArray#GeometryStripArray(int,int,int[])}
+     * for more exceptions that can be thrown
      */
     public TriangleStripArray(int vertexCount,
 			      int vertexFormat,
@@ -63,65 +61,34 @@ public class TriangleStripArray extends GeometryStripArray {
     }
 
     /**
-     * Constructs an empty TriangleStripArray object with the specified
-     * number of vertices, vertex format, number of texture coordinate
-     * sets, texture coordinate mapping array, and
-     * array of per-strip vertex counts.
-     *
-     * @param vertexCount the number of vertex elements in this array<p>
-     *
-     * @param vertexFormat a mask indicating which components are
-     * present in each vertex.  This is specified as one or more
-     * individual flags that are bitwise "OR"ed together to describe
-     * the per-vertex data.
-     * The flags include: COORDINATES, to signal the inclusion of
-     * vertex positions--always present; NORMALS, to signal 
-     * the inclusion of per vertex normals; one of COLOR_3,
-     * COLOR_4, to signal the inclusion of per vertex
-     * colors (without or with color information); and one of 
-     * TEXTURE_COORDINATE_2, TEXTURE_COORDINATE_3 or TEXTURE_COORDINATE_4, 
-     * to signal the
-     * inclusion of per-vertex texture coordinates 2D, 3D or 4D.<p>
-     *
-     * @param texCoordSetCount the number of texture coordinate sets
-     * in this GeometryArray object.  If <code>vertexFormat</code>
-     * does not include one of <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3 or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetCount</code> parameter is not used.<p>
-     *
-     * @param texCoordSetMap an array that maps texture coordinate
-     * sets to texture units.  The array is indexed by texture unit
-     * number for each texture unit in the associated Appearance
-     * object.  The values in the array specify the texture coordinate
-     * set within this GeometryArray object that maps to the
-     * corresponding texture
-     * unit.  All elements within the array must be less than
-     * <code>texCoordSetCount</code>.  A negative value specifies that
-     * no texture coordinate set maps to the texture unit
-     * corresponding to the index.  If there are more texture units in
-     * any associated Appearance object than elements in the mapping
-     * array, the extra elements are assumed to be -1.  The same
-     * texture coordinate set may be used for more than one texture
-     * unit.  Each texture unit in every associated Appearance must
-     * have a valid source of texture coordinates: either a
-     * non-negative texture coordinate set must be specified in the
-     * mapping array or texture coordinate generation must be enabled.
-     * Texture coordinate generation will take precedence for those
-     * texture units for which a texture coordinate set is specified
-     * and texture coordinate generation is enabled.  If
-     * <code>vertexFormat</code> does not include one of
-     * <code>TEXTURE_COORDINATE_2</code>,
-     * <code>TEXTURE_COORDINATE_3</code> or
-     * <code>TEXTURE_COORDINATE_4</code>, the
-     * <code>texCoordSetMap</code> array is not used.<p>
-     *
-     * @param stripVertexCounts array that specifies
-     * the count of the number of vertices for each separate strip.
-     * The length of this array is the number of separate strips.
+     * Constructs an empty TriangleStripArray object using the
+     * specified parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[])}
+     * for a description of this parameter.
+     *
+     * @param stripVertexCounts
+     * see {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int[])}
+     * for a description of this parameter.
      *
      * @exception IllegalArgumentException if vertexCount is less than 3
      * or any element in the stripVertexCounts array is less than 3
+     * ;<br>
+     * See {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int[])}
+     * for more exceptions that can be thrown
      *
      * @since Java 3D 1.2
      */
@@ -139,6 +106,59 @@ public class TriangleStripArray extends GeometryStripArray {
 	    throw new IllegalArgumentException(J3dI18N.getString("TriangleStripArray0"));
     }
 
+    /**
+     * Constructs an empty TriangleStripArray object using the
+     * specified parameters.
+     *
+     * @param vertexCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexFormat
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param texCoordSetMap
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrCount
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param vertexAttrSizes
+     * see {@link GeometryArray#GeometryArray(int,int,int,int[],int,int[])}
+     * for a description of this parameter.
+     *
+     * @param stripVertexCounts
+     * see {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int,int[],int[])}
+     * for a description of this parameter.
+     *
+     * @exception IllegalArgumentException if vertexCount is less than 3
+     * or any element in the stripVertexCounts array is less than 3
+     * ;<br>
+     * See {@link GeometryStripArray#GeometryStripArray(int,int,int,int[],int,int[],int[])}
+     * for more exceptions that can be thrown
+     *
+     * @since Java 3D 1.4
+     */
+    public TriangleStripArray(int vertexCount,
+			      int vertexFormat,
+			      int texCoordSetCount,
+			      int[] texCoordSetMap,
+			      int vertexAttrCount,
+			      int[] vertexAttrSizes,
+			      int[] stripVertexCounts) {
+
+	super(vertexCount, vertexFormat,
+	      texCoordSetCount, texCoordSetMap,
+	      vertexAttrCount, vertexAttrSizes,
+	      stripVertexCounts);
+
+        if (vertexCount < 3 )
+	    throw new IllegalArgumentException(J3dI18N.getString("TriangleStripArray0"));
+    }
+
     /**
      * Creates the retained mode TriangleStripArrayRetained object that this
      * TriangleStripArray object will point to.
@@ -153,26 +173,29 @@ public class TriangleStripArray extends GeometryStripArray {
      * @deprecated replaced with cloneNodeComponent(boolean forceDuplicate)
      */
     public NodeComponent cloneNodeComponent() {
-	TriangleStripArrayRetained rt = (TriangleStripArrayRetained) retained;
+        TriangleStripArrayRetained rt = (TriangleStripArrayRetained) retained;
         int stripcounts[] = new int[rt.getNumStrips()];
-	rt.getStripVertexCounts(stripcounts);
-	int texSetCount = rt.getTexCoordSetCount();
-        TriangleStripArray t;
-	if (texSetCount == 0) {
-	    t = new TriangleStripArray(rt.getVertexCount(), 
-				       rt.getVertexFormat(),
-				       stripcounts);
-	} else {
-	    int texMap[] = new int[rt.getTexCoordSetMapLength()];
-	    rt.getTexCoordSetMap(texMap);
-	    t = new TriangleStripArray(rt.getVertexCount(), 
-				       rt.getVertexFormat(),
-				       texSetCount,
-				       texMap,
-				       stripcounts);
-	    
-	}
-	t.duplicateNodeComponent(this);
+        rt.getStripVertexCounts(stripcounts);
+        int texSetCount = rt.getTexCoordSetCount();
+        int[] texMap = null;
+        int vertexAttrCount = rt.getVertexAttrCount();
+        int[] vertexAttrSizes = null;
+        if (texSetCount > 0) {
+            texMap = new int[rt.getTexCoordSetMapLength()];
+            rt.getTexCoordSetMap(texMap);
+        }
+        if (vertexAttrCount > 0) {
+            vertexAttrSizes = new int[vertexAttrCount];
+            rt.getVertexAttrSizes(vertexAttrSizes);
+        }
+        TriangleStripArray t = new TriangleStripArray(rt.getVertexCount(),
+                rt.getVertexFormat(),
+                texSetCount,
+                texMap,
+                vertexAttrCount,
+                vertexAttrSizes,
+                stripcounts);
+        t.duplicateNodeComponent(this);
         return t;
      }
 }
diff --git a/src/classes/share/javax/media/j3d/TriangleStripArrayRetained.java b/src/classes/share/javax/media/j3d/TriangleStripArrayRetained.java
index db4b8ba..14c177a 100644
--- a/src/classes/share/javax/media/j3d/TriangleStripArrayRetained.java
+++ b/src/classes/share/javax/media/j3d/TriangleStripArrayRetained.java
@@ -30,15 +30,16 @@ class TriangleStripArrayRetained extends GeometryStripArrayRetained {
     TriangleStripArrayRetained() {
 	this.geoType = GEO_TYPE_TRI_STRIP_SET;
     }
-
-    boolean intersect(PickShape pickShape, double dist[],  Point3d iPnt) {
+    
+    boolean intersect(PickShape pickShape, PickInfo.IntersectionInfo iInfo,  int flags, Point3d iPnt) {
 	Point3d pnts[] = new Point3d[3];
 	double sdist[] = new double[1];
 	double minDist = Double.MAX_VALUE;
 	double x = 0, y = 0, z = 0;
+        int count = 0;
+        int minICount = 0;         
 	int i = 0;
 	int j, end;
-
 	pnts[0] = new Point3d();
 	pnts[1] = new Point3d();
 	pnts[2] = new Point3d();
@@ -52,18 +53,21 @@ class TriangleStripArrayRetained extends GeometryStripArrayRetained {
 		end = j + stripVertexCounts[i++];
 		getVertexData(j++, pnts[0]);
 		getVertexData(j++, pnts[1]);
+                count += 2;
 		while (j < end) {
 		    getVertexData(j++, pnts[2]);
+                    count++;                    
 		    if (intersectRay(pnts, pickRay, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
+                            minICount = count;
 			    x = iPnt.x;
 			    y = iPnt.y;
 			    z = iPnt.z;
-			}
+                        }
 		    }
 		    pnts[0].set(pnts[1]);
 		    pnts[1].set(pnts[2]);
@@ -78,15 +82,18 @@ class TriangleStripArrayRetained extends GeometryStripArrayRetained {
 		end = j + stripVertexCounts[i++];
 		getVertexData(j++, pnts[0]);
 		getVertexData(j++, pnts[1]);
+                count += 2;
 		while (j < end) {
 		    getVertexData(j++, pnts[2]);
+                    count++;
 		    if (intersectSegment(pnts, pickSegment.start, 
 					 pickSegment.end, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
+                            minICount = count;
 			    x = iPnt.x;
 			    y = iPnt.y;
 			    z = iPnt.z;
@@ -106,14 +113,17 @@ class TriangleStripArrayRetained extends GeometryStripArrayRetained {
 		end = j + stripVertexCounts[i++];
 		getVertexData(j++, pnts[0]);
 		getVertexData(j++, pnts[1]);
+                count += 2;
 		while (j < end) {
 		    getVertexData(j++, pnts[2]);
+                    count++;
 		    if (intersectBoundingBox(pnts, bbox, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
+                            minICount = count;
 			    x = iPnt.x;
 			    y = iPnt.y;
 			    z = iPnt.z;
@@ -133,15 +143,18 @@ class TriangleStripArrayRetained extends GeometryStripArrayRetained {
 		end = j + stripVertexCounts[i++];
 		getVertexData(j++, pnts[0]);
 		getVertexData(j++, pnts[1]);
+                count += 2;
 		while (j < end) {
 		    getVertexData(j++, pnts[2]);
+                    count++;
 		    if (intersectBoundingSphere(pnts, bsphere, sdist,
 						iPnt)) { 
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
+                            minICount = count;
 			    x = iPnt.x;
 			    y = iPnt.y;
 			    z = iPnt.z;
@@ -161,15 +174,18 @@ class TriangleStripArrayRetained extends GeometryStripArrayRetained {
 		end = j + stripVertexCounts[i++];
 		getVertexData(j++, pnts[0]);
 		getVertexData(j++, pnts[1]);
+                count += 2;                
 		while (j < end) {
 		    getVertexData(j++, pnts[2]);
-		    if (intersectBoundingPolytope(pnts, bpolytope,
+                    count++;
+                    if (intersectBoundingPolytope(pnts, bpolytope,
 						  sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
+                            minICount = count;
 			    x = iPnt.x;
 			    y = iPnt.y;
 			    z = iPnt.z;
@@ -188,14 +204,17 @@ class TriangleStripArrayRetained extends GeometryStripArrayRetained {
 		end = j + stripVertexCounts[i++];
 		getVertexData(j++, pnts[0]);
 		getVertexData(j++, pnts[1]);
+                count += 2;
 		while (j < end) {
 		    getVertexData(j++, pnts[2]);
-		    if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
-			if (dist == null) {
+                    count++;
+                    if (intersectCylinder(pnts, pickCylinder, sdist, iPnt)) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
+                            minICount = count;
 			    x = iPnt.x;
 			    y = iPnt.y;
 			    z = iPnt.z;
@@ -214,14 +233,17 @@ class TriangleStripArrayRetained extends GeometryStripArrayRetained {
 		end = j + stripVertexCounts[i++];
 		getVertexData(j++, pnts[0]);
 		getVertexData(j++, pnts[1]);
+                count += 2;
 		while (j < end) {
 		    getVertexData(j++, pnts[2]);
+                    count++;
 		    if (intersectCone(pnts, pickCone, sdist, iPnt)) {
-			if (dist == null) {
+			if (flags == 0) {
 			    return true;
 			}
 			if (sdist[0] < minDist) {
 			    minDist = sdist[0];
+                            minICount = count;
 			    x = iPnt.x;
 			    y = iPnt.y;
 			    z = iPnt.z;
@@ -240,7 +262,15 @@ class TriangleStripArrayRetained extends GeometryStripArrayRetained {
 	} 
 
 	if (minDist < Double.MAX_VALUE) {
-	    dist[0] = minDist;
+            assert(minICount >=3);
+            int[] vertexIndices = iInfo.getVertexIndices();
+            if (vertexIndices == null) {
+                vertexIndices = new int[3];
+                iInfo.setVertexIndices(vertexIndices);
+            }
+            vertexIndices[0] = minICount - 3;
+            vertexIndices[1] = minICount - 2;
+            vertexIndices[2] = minICount - 1;
 	    iPnt.x = x;
 	    iPnt.y = y;
 	    iPnt.z = z;
diff --git a/src/classes/share/javax/media/j3d/View.java b/src/classes/share/javax/media/j3d/View.java
index 2b54b6a..daf6cd4 100644
--- a/src/classes/share/javax/media/j3d/View.java
+++ b/src/classes/share/javax/media/j3d/View.java
@@ -3305,7 +3305,7 @@ public class View extends Object {
 	}
 
 	// This is a temporary fix for bug 4267395 
-	// TODO:cleanup in RenderBin after View detach
+	// XXXX:cleanup in RenderBin after View detach
 	// universe.addViewIdToFreeList(viewId);
 	
 	// using new property -Dj3d.forceReleaseView to disable bug fix 4267395
diff --git a/src/classes/share/javax/media/j3d/ViewCache.java b/src/classes/share/javax/media/j3d/ViewCache.java
index 5aeec71..b506793 100644
--- a/src/classes/share/javax/media/j3d/ViewCache.java
+++ b/src/classes/share/javax/media/j3d/ViewCache.java
@@ -312,8 +312,7 @@ class ViewCache extends Object {
 	    trackerBaseToHeadTracker.setIdentity();
 	}
 
-	// TODO: implement head to vworld tracking if userHeadToVworldEnable
-	// is set
+	// XXXX: implement head to vworld tracking if userHeadToVworldEnable is set
     	userHeadToVworld.setIdentity();
     }
 
diff --git a/src/classes/share/javax/media/j3d/ViewPlatform.java b/src/classes/share/javax/media/j3d/ViewPlatform.java
index 59ebf66..bd518f2 100644
--- a/src/classes/share/javax/media/j3d/ViewPlatform.java
+++ b/src/classes/share/javax/media/j3d/ViewPlatform.java
@@ -108,6 +108,11 @@ public class ViewPlatform extends Leaf {
     public static final int
     ALLOW_POLICY_WRITE = CapabilityBits.VIEW_PLATFORM_ALLOW_POLICY_WRITE;
 
+   // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_POLICY_READ
+    };
+    
     /**
      * Constructs a ViewPlatform object with default parameters.
      * The default values are as follows:
@@ -117,6 +122,8 @@ public class ViewPlatform extends Leaf {
      * </ul>
      */
     public ViewPlatform() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);
     }
 
     /**
diff --git a/src/classes/share/javax/media/j3d/ViewPlatformRetained.java b/src/classes/share/javax/media/j3d/ViewPlatformRetained.java
index 230235a..c523108 100644
--- a/src/classes/share/javax/media/j3d/ViewPlatformRetained.java
+++ b/src/classes/share/javax/media/j3d/ViewPlatformRetained.java
@@ -164,7 +164,6 @@ class ViewPlatformRetained extends LeafRetained {
     /**
      * This sets the view that is associated with this view platform.
      */
-    // TODO: This must be changed to a list of views!
     void setView(View v) {
 	synchronized (viewList) {
 	    if (!viewList.contains(v)) {
diff --git a/src/classes/share/javax/media/j3d/ViewSpecificGroup.java b/src/classes/share/javax/media/j3d/ViewSpecificGroup.java
index 8203551..bd87d04 100644
--- a/src/classes/share/javax/media/j3d/ViewSpecificGroup.java
+++ b/src/classes/share/javax/media/j3d/ViewSpecificGroup.java
@@ -71,11 +71,17 @@ public class ViewSpecificGroup extends Group {
     public static final int
     ALLOW_VIEW_WRITE = CapabilityBits.VIEW_SPECIFIC_GROUP_ALLOW_VIEW_WRITE;
 
+    // Array for setting default read capabilities
+    private static final int[] readCapabilities = {
+        ALLOW_VIEW_READ
+    };
 
     /**
      * Constructs and initializes a new ViewSpecificGroup node object.
      */
     public ViewSpecificGroup() {
+        // set default read capabilities
+        setDefaultReadCapabilities(readCapabilities);        
     }
 
 
@@ -324,7 +330,7 @@ public class ViewSpecificGroup extends Group {
      */
     void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
 
-	// TODO: implement this
+	// XXXX: implement this?
         super.duplicateAttributes(originalNode, forceDuplicate);
 
         ViewSpecificGroupRetained attr = (ViewSpecificGroupRetained) originalNode.retained;
diff --git a/src/classes/share/javax/media/j3d/ViewSpecificGroupRetained.java b/src/classes/share/javax/media/j3d/ViewSpecificGroupRetained.java
index 58cc08f..f60d052 100644
--- a/src/classes/share/javax/media/j3d/ViewSpecificGroupRetained.java
+++ b/src/classes/share/javax/media/j3d/ViewSpecificGroupRetained.java
@@ -517,7 +517,7 @@ class ViewSpecificGroupRetained extends GroupRetained {
         // don't remove this group node
         mergeFlag = SceneGraphObjectRetained.DONT_MERGE;
 
-	// TODO: complete this
+	// XXXX: complete this
     }
     
     void setLive(SetLiveState s) {
@@ -549,7 +549,7 @@ class ViewSpecificGroupRetained extends GroupRetained {
 	    s.viewScopedNodeList = new ArrayList();
 	    s.scopedNodesViewList = new ArrayList();
 	}
-	// TODO: This is a hack since removeNodeData is called before
+	// XXXX: This is a hack since removeNodeData is called before
 	// children are clearLives
 	int[] tempIndex = null;
 	// Don't keep the indices if everything will be cleared
diff --git a/src/classes/share/javax/media/j3d/VirtualUniverse.java b/src/classes/share/javax/media/j3d/VirtualUniverse.java
index bb9b14d..20c783b 100644
--- a/src/classes/share/javax/media/j3d/VirtualUniverse.java
+++ b/src/classes/share/javax/media/j3d/VirtualUniverse.java
@@ -15,6 +15,8 @@ package javax.media.j3d;
 import java.util.Vector;
 import java.util.ArrayList;
 import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Map;
 
 /**
@@ -155,6 +157,12 @@ public class VirtualUniverse extends Object {
     boolean isSceneGraphLock = false;
 
     private Object waitLock = new Object();
+    
+    private HashSet structureChangeListenerSet = null;
+
+    private HashSet shaderErrorListenerSet = null;
+    private ShaderErrorListener defaultShaderErrorListener =
+	ShaderProgram.getDefaultErrorListener();
 
     /**
      * Constructs a new VirtualUniverse.
@@ -549,7 +557,7 @@ public class VirtualUniverse extends Object {
     /**
      * This returns the next available nodeId as a string.
      */
-    // TODO: reuse of id's imply a slight collision problem in the
+    // XXXX: reuse of id's imply a slight collision problem in the
     // render queue's.
     // BUG 4181362
     String getNodeId() {
@@ -990,4 +998,184 @@ public class VirtualUniverse extends Object {
 	    isSceneGraphLock = false;
 	}
     }
+    
+    /**
+     * Adds the specified GraphStructureChangeListener to the set of listeners
+     * that will be notified when the graph structure is changed on a live
+     * scene graph. If the specifed listener is null no action is taken and no 
+     * exception is thrown.
+     *
+     * @param listener the listener to add to the set.
+     *
+     * @since Java 3D 1.4
+     */
+    public void addGraphStructureChangeListener(GraphStructureChangeListener listener) {
+        if (listener == null) {
+            return;
+        }
+        
+        if (structureChangeListenerSet == null) {
+            structureChangeListenerSet = new HashSet();
+        }
+        
+        synchronized(structureChangeListenerSet) {
+            structureChangeListenerSet.add(listener);
+        }
+    }
+    
+    /**
+     * Removes the specified GraphStructureChangeListener from the set of listeners. This
+     * method performs no function, nor does it throw an exception if the specified listener
+     * is not currently in the set or is null.
+     *
+     * @param listener the listener to remove from the set.
+     *
+     * @since Java 3D 1.4
+     */
+    public void removeGraphStructureChangeListener(GraphStructureChangeListener listener) {
+        if (structureChangeListenerSet == null) {
+	    return;
+	}
+
+        synchronized(structureChangeListenerSet) {
+            structureChangeListenerSet.remove(listener);
+        }
+    }
+    
+    /**
+     * Processes all live BranchGroup add and removes and notifies
+     * any registered listeners. Used for add and remove
+     */
+    void notifyStructureChangeListeners(boolean add, Object parent, BranchGroup child) {
+        if (structureChangeListenerSet == null) {
+            return;
+	}
+        
+        synchronized(structureChangeListenerSet) {
+            Iterator it = structureChangeListenerSet.iterator();
+            while(it.hasNext()) {
+                GraphStructureChangeListener listener = (GraphStructureChangeListener)it.next();
+                try {
+                    if (add) {
+                        listener.branchGroupAdded(parent, child);
+                    } else {
+                        listener.branchGroupRemoved(parent, child);
+                    }
+                }
+                catch (RuntimeException e) {
+                    System.err.println("Exception occurred in GraphStructureChangeListener:");
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+    
+    /**
+     * Processes all live BranchGroup moves and notifies
+     * any registered listeners. Used for moveTo
+     */
+    void notifyStructureChangeListeners(Object oldParent, Object newParent, BranchGroup child) {
+        if (structureChangeListenerSet == null) {
+            return;
+	}
+
+        synchronized(structureChangeListenerSet) {
+            Iterator it = structureChangeListenerSet.iterator();
+            while(it.hasNext()) {
+                GraphStructureChangeListener listener = (GraphStructureChangeListener)it.next();
+                try {
+                    listener.branchGroupMoved(oldParent, newParent, child);
+                }
+                catch (RuntimeException e) {
+                    System.err.println("Exception occurred in GraphStructureChangeListener:");
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Adds the specified ShaderErrorListener to the set of listeners
+     * that will be notified when a programmable shader error is
+     * detected on a live scene graph. If the specifed listener is
+     * null no action is taken and no exception is thrown.
+     * If a shader error occurs, the listeners will be called
+     * asynchronously from a separate notification thread. The Java 3D
+     * renderer and behavior scheduler will continue to run as if the
+     * error had not occurred, except that shading will be disabled
+     * for the objects in error. If applications desire to detach or
+     * modify the scene graph as a result of the error, they should
+     * use a behavior post if they want that change to be
+     * synchronous with the renderer.
+     *
+     * @param listener the listener to add to the set.
+     *
+     * @since Java 3D 1.4
+     */
+    public void addShaderErrorListener(ShaderErrorListener listener) {
+        if (listener == null) {
+            return;
+        }
+
+        if (shaderErrorListenerSet == null) {
+            shaderErrorListenerSet = new HashSet();
+        }
+
+        synchronized(shaderErrorListenerSet) {
+            shaderErrorListenerSet.add(listener);
+        }
+    }
+
+    /**
+     * Removes the specified ShaderErrorListener from the set of
+     * listeners. This method performs no function, nor does it throw
+     * an exception if the specified listener is not currently in the
+     * set or is null.
+     *
+     * @param listener the listener to remove from the set.
+     *
+     * @since Java 3D 1.4
+     */
+    public void removeShaderErrorListener(ShaderErrorListener listener) {
+        if (shaderErrorListenerSet == null) {
+	    return;
+	}
+
+        synchronized(shaderErrorListenerSet) {
+            shaderErrorListenerSet.remove(listener);
+        }
+    }
+
+    /**
+     * Notifies all listeners of a shader error. If no listeners exist, a default
+     * listener is notified.
+     */
+    void notifyShaderErrorListeners(ShaderError error) {
+	boolean errorReported = false;
+
+	// Notify all error listeners in the set
+        if (shaderErrorListenerSet != null) {
+            synchronized(shaderErrorListenerSet) {
+                Iterator it = shaderErrorListenerSet.iterator();
+                while(it.hasNext()) {
+                    ShaderErrorListener listener = (ShaderErrorListener)it.next();
+                    try {
+                        listener.errorOccurred(error);
+                    }
+                    catch (RuntimeException e) {
+                        System.err.println("Exception occurred in ShaderErrorListener:");
+                        e.printStackTrace();
+                    }
+                    errorReported = true;
+                }
+            }
+        }
+
+	// Notify the default error listener if the set is null or empty
+	if (!errorReported) {
+	    defaultShaderErrorListener.errorOccurred(error);
+	}
+    }
+
 }
diff --git a/src/classes/share/javax/media/j3d/WakeupIndexedList.java b/src/classes/share/javax/media/j3d/WakeupIndexedList.java
index c0a52ec..4a20594 100644
--- a/src/classes/share/javax/media/j3d/WakeupIndexedList.java
+++ b/src/classes/share/javax/media/j3d/WakeupIndexedList.java
@@ -59,7 +59,7 @@ package javax.media.j3d;
 
 class WakeupIndexedList implements Cloneable, java.io.Serializable  {
 
-    // TODO: set to false when release
+    // XXXX: set to false when release
     final static boolean debug = false;
 
     /**
-- 
cgit v1.2.3